2015-09-02 11 views
5

Mô tả vấn đề:Delphi Firemonkey iOS xử lý nền

Tôi hiện đang phát triển và ứng dụng Android và iOS với Delphi XE7 Firemonkey. Ứng dụng này cần phải hoạt động ngoại tuyến để người dùng có thể làm việc với nó và khi điện thoại trực tuyến, ứng dụng sẽ gửi tất cả công việc tới máy chủ ngay cả khi ứng dụng ở chế độ nền hoặc điện thoại ở chế độ chờ.

Android giải pháp làm việc:

Như tôi đã phát hiện ra, các đối tượng TThread và TTimer không hoạt động khi họ ngừng chạy khi ứng dụng đi vào nền hoặc điện thoại chuyển sang đứng.

Tôi đã kết thúc tìm thư viện này cho android hoạt động giống như đối tượng TTimer nhưng nó vẫn hoạt động khi ứng dụng chuyển sang nền hoặc điện thoại ở chế độ chờ.

https://github.com/dkstar88/AsyncTask

Đáng tiếc là nó không hoạt động trên iOS vì nó hoạt động như TThread và TTimer và nó dừng lại một khi ứng dụng đi vào nền.

iOS cố gắng giải pháp

Tôi đã thử nhiều cách tiếp cận nhưng tôi phát hiện ra rằng có rất nhiều người có rất nhiều thông tin về Android nhưng ít hơn rất nhiều cho iOS.

Điều đầu tiên tôi đã thử là thêm vào tệp plist các quyền cần thiết để cho iOS biết rằng tôi muốn làm điều gì đó trong nền. Tôi đã thử thuộc tính "fetch".

Tính năng này không hoạt động với TTimer, TThread hoặc AsyncTask đã thử trong Android. Vì vậy, tôi nghĩ bạn phải nói với iOS rằng bạn muốn làm điều gì đó ở chế độ nền VÀ bạn muốn iOS thực hiện quy trình hoặc mã nào khi chạy trong backgroud.

Mã promsinig nhất mà tôi tìm thấy là trong các liên kết này (xem ý kiến):

http://qc.embarcadero.com/wc/qcmain.aspx?d=128968

Đây là mã tôi đã sử dụng, chuyển thể từ các liên kết trước đó. Nó chỉ đơn giản là thêm một dòng vào một lĩnh vực ghi nhớ để thử nghiệm.

unit uBackgroundiOS_2; 

interface 

uses iOSapi.UIKit,Macapi.ObjCRuntime,Macapi.ObjectiveC,iOSapi.CocoaTypes,FMX.Platform.iOS,iOSapi.Foundation,Macapi.Helpers, 

FMX.Memo, system.SysUtils; 

const UIBackgroundFetchResultNewData:NSUInteger = 0; 
     UIBackgroundFetchResultNoData:NSUInteger = 1; 
     UIBackgroundFetchResultFailed:NSUInteger = 2; 

     UIApplicationBackgroundFetchIntervalMinimum:NSTimeInterval = 0; 
     UIApplicationBackgroundFetchIntervalNever:NSTimeInterval = -1; 

type 
     id=Pointer; 
     IMP = function(self : id; cmd : SEL; Param1 : NSUInteger) : id; cdecl; 

Var UIApp : UIApplication; 
    memo: TMemo; 

function imp_implementationWithBlock(block :Pointer) : IMP; cdecl; external libobjc name _PU + 'imp_implementationWithBlock'; 
function imp_removeBlock(anImp : IMP) : integer; cdecl; external libobjc name _PU + 'imp_removeBlock'; 
function objc_addClass(Cls: Pointer): Integer; cdecl; external libobjc name _PU + 'objc_addClass'; 

procedure performFetchWithCompletionHandler(self:id; _cmd: SEL;application:id; handler:id); 


procedure Init(p_memo: Tmemo); 

implementation 

procedure Init(p_memo: Tmemo); 
var 
    Res: Integer; 
begin 
{ 
Info: 
use .\plist\BackgroundOPs.info.plist from Project Main Pfad and copy to Info.plist 

include the Keys: 
<key>UIBackgroundModes</key> 
<array> 
<string>fetch</string> 
<string>remote-notification</string> 
<string>newsstand-content</string> 
</array> 
} 
UIApp := TUIApplication.Wrap(TUIApplication.OCClass.sharedApplication); 

objc_msgSend((UIApp as ILocalObject).GetObjectId,sel_getUid('setMinimumBackgroundFetchInterval:'),UIApplicationBackgroundFetchIntervalMinimum); 
Res := class_addMethod(objc_getClass('DelphiAppDelegate') , sel_getUid('application:performFetchWithCompletionHandler:'), @performFetchWithCompletionHandler,'[email protected]:@?'); 

memo := p_memo; 
end; 



procedure performFetchWithCompletionHandler(self:id; _cmd: SEL;application:id; handler:id); 
{ 
Using your device you can fire application:performFetchWithCompletionHandler with the following steps: 
Put your app in the Background state. 
Lock your device and wait 5 minutes. (I know, it's a waste of time, get some coffee) 
Unlock your device, this is will fire the method. 
} 
var 
    ahandlerimp: IMP; 
begin 
    try 
    // here your code max. 30 Sec. 
    Memo.Lines.Add(FormatDateTime('hh:nn:ss', Now)); 

    ahandlerimp := imp_implementationWithBlock(handler); 
    ahandlerimp(self,_cmd,UIBackgroundFetchResultNewData); // return the Do- code 
    imp_removeBlock(ahandlerimp); 
    except 
    end; 
end; 

end. 

Điều này không hoạt động trong iphone 4 của tôi với iOS 8.4.1. Tôi để điện thoại ở lại một lúc và khi tôi kiểm tra ứng dụng, trường ghi nhớ bị trống.

Bất kỳ ý tưởng nào về điều gì có thể sai? Tôi hơi chết ở đây.

Cảm ơn rất nhiều!

PS: trong bài đăng gốc, tôi đặt nhiều liên kết và nguồn hơn (bao gồm cả các bài đăng ngăn xếp khác) nhưng tiếc là tôi chỉ để lại hai liên quan nhất vì tôi không có 10 danh tiếng cần thiết để đăng thêm.


Trộn mã từ hai ví dụ này tôi đã thực hiện các đơn vị sau đây:

http://qc.embarcadero.com/wc/qcmain.aspx?d=128968 (xem ý kiến) Calling objective C code block from delphi

unit uBackgroundiOS; 

interface 

uses fmx.platform.iOS,iOSapi.CocoaTypes, Macapi.ObjCRuntime, Macapi.ObjectiveC, 
    iOSapi.UIKit; 

const 
    UIBackgroundFetchResultNewData:NSUInteger = 0; 
    UIBackgroundFetchResultNoData:NSUInteger = 1; 
    UIBackgroundFetchResultFailed:NSUInteger = 2; 

    UIApplicationBackgroundFetchIntervalMinimum:NSTimeInterval = 0; 
    UIApplicationBackgroundFetchIntervalNever:NSTimeInterval = -1; 

type 

    // copied from fmx.platform.iOS as it's on private declaration 
    id = Pointer; 
    SEL = Pointer; 
    PUIApplication = Pointer; 

    IMP = function(self : id; cmd : SEL; Param1 : NSUInteger) : id; cdecl; 

    function imp_implementationWithBlock(block :id) : IMP; cdecl; external libobjc name _PU + 'imp_implementationWithBlock'; 
    function imp_removeBlock(anImp : IMP) : integer; cdecl; external libobjc name _PU + 'imp_removeBlock'; 

    procedure performFetchWithCompletionHandler(self : id; _cmd : SEL; application: PUIApplication; handler : id); 

    procedure initializeBackgroundFetch; 

    //to test if procedure is called in background 
    var fecth_string_test: string; 

implementation 

procedure performFetchWithCompletionHandler(self : id; _cmd : SEL; application: PUIApplication; handler : id); 
var 
    ahandlerimp: IMP; 
begin 
    //Code to perform fetch 
    fecth_string_test := 'entered background code!!'; 

    ahandlerimp := imp_implementationWithBlock(handler); //Create c function for block 
    ahandlerimp(self,_cmd, UIBackgroundFetchResultNewData); //Call c function, _cmd is ignored 
    imp_removeBlock(ahandlerimp); //Remove the c function created two lines up 
end; 

procedure initializeBackgroundFetch; 
Var 
    UIApp : UIApplication; 
    Interval: Pointer; 
begin 
    UIApp := TUIApplication.Wrap(TUIApplication.OCClass.sharedApplication); 

    Interval := objc_msgSend((UIApp as ILocalObject).GetObjectId, 
          sel_getUid('setMinimumBackgroundFetchInterval:')); 
//    Interval); 
    class_addMethod(objc_getClass('DelphiAppDelegate') , 
        sel_getUid('application:performFetchWithCompletionHandler:'), 
        @performFetchWithCompletionHandler, 
        '[email protected]:@?' 
); 
end; 

end. 

Mã này không hoạt động. Tôi gọi initializeBackgroundFetch khi ứng dụng bắt đầu. Tôi chắc chắn tôi đang làm điều gì đó sai trái nhưng không thể hình dung nó là gì.

Ngoài ra, tôi nhận thấy ứng dụng của tôi không xuất hiện ở chế độ nền cho phép ứng dụng trong cài đặt iPhone, nó có xuất hiện không? Tôi đã thêm thông tin sau vào tệp info.plist:

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 
<plist version="1.0"> 
<dict> 
[...] 
    <key>UIBackgroundModes</key> 
    <array> 
     <string>fetch</string> 
     <string>remote-notification</string> 
     <string>newsstand-content</string> 
    </array> 
</dict> 
</plist> 

Bất kỳ ý tưởng nào?

+0

nguồn bổ sung: tính nền iOS: [link] (http://delphi.radsoft.com .au/2013/10/cung cấp-nền-dịch vụ-hỗ trợ-to-bạn-ios-apps-with-delphi /) StackOverflow liên kết liên quan: [link] (https://stackoverflow.com/questions/24168144/ delphi-firemonkey-ios-background-fetch) Ví dụ về mục tiêu-C: [liên kết] (http://hayageek.com/ios-background-fetch/) [li nk] (https://possiblemobile.com/2013/09/ios-7-background-fetch/) –

+0

Cũng đã thử [link] này (http://codeverge.com/embarcadero.delphi.ios/does-delphi- for-ios-support-complete-b/1095555) Nhưng tăng "Ngoại lệ bên ngoài 0". –

+0

cập nhật bài đăng chính với mã mới –

Trả lời

2

Giải pháp iOS Working

Cuối cùng tôi đã nhận nó làm việc dưới delphi 10 Seattle (bạn cần phiên bản này, tôi đã cố gắng XE7 và không làm việc, tôi đã không cố gắng xe8). Dưới đây là các bước:

1- Dự án> Tùy chọn> Thông tin phiên bản. Kiểm tra UIBackgroundModes bạn cần (tôi đã sử dụng Fetch)

2- Đây là mã tôi đã sử dụng:

unit uBackgroundiOS; 

interface 

uses fmx.platform.iOS,iOSapi.CocoaTypes, Macapi.ObjCRuntime, Macapi.ObjectiveC, 
    iOSapi.UIKit; 

const 
    UIBackgroundFetchResultNewData:NSUInteger = 0; 
    UIBackgroundFetchResultNoData:NSUInteger = 1; 
    UIBackgroundFetchResultFailed:NSUInteger = 2; 

    UIApplicationBackgroundFetchIntervalMinimum:NSTimeInterval = 0; 
    UIApplicationBackgroundFetchIntervalNever:NSTimeInterval = -1; 

type 

    // copied from fmx.platform.iOS as it's on private declaration 
    id = Pointer; 
    SEL = Pointer; 
    PUIApplication = Pointer; 

    IMP = function(self : id; cmd : SEL; Param1 : NSUInteger) : id; cdecl; 

    function imp_implementationWithBlock(block :id) : IMP; cdecl; external libobjc name _PU + 'imp_implementationWithBlock'; 
    function imp_removeBlock(anImp : IMP) : integer; cdecl; external libobjc name _PU + 'imp_removeBlock'; 

    procedure performFetchWithCompletionHandler(self : id; _cmd : SEL; application: PUIApplication; handler : id); 

    procedure initializeBackgroundFetch; 

    function objc_msgSend(theReceiver: Pointer; theSelector: Pointer): Pointer; cdecl; varargs; 
    external libobjc name _PU + 'objc_msgSend'; 

    //to test if procedure is called in background 
    var fecth_string_test: string; 

implementation 

procedure performFetchWithCompletionHandler(self : id; _cmd : SEL; application: PUIApplication; handler : id); 
var 
    ahandlerimp: IMP; 
begin 
    //Code to perform fetch HERE!!!! 
    fecth_string_test := 'entered background code!!'; 

    ahandlerimp := imp_implementationWithBlock(handler); //Create c function for block 
    ahandlerimp(self,_cmd, UIBackgroundFetchResultNewData); //Call c function, _cmd is ignored 
    imp_removeBlock(ahandlerimp); //Remove the c function created two lines up 
end; 

procedure initializeBackgroundFetch; 
Var 
    UIApp: UIApplication; 
begin 
    UIApp := TUIApplication.Wrap(TUIApplication.OCClass.sharedApplication); 

    objc_msgSend((UIApp as ILocalObject).GetObjectId, 
       sel_getUid('setMinimumBackgroundFetchInterval:'), 
       UIApplicationBackgroundFetchIntervalMinimum); 

    class_addMethod(objc_getClass('DelphiAppDelegate') , 
        sel_getUid('application:performFetchWithCompletionHandler:'), 
        @performFetchWithCompletionHandler, 
        '[email protected]:@?'); 
end; 

end. 
Các vấn đề liên quan