2015-09-02 99 views
5

問題描述:德爾福Firemonkey iOS的後臺處理

我目前正在開發和Android和iOS用Delphi XE7 Firemonkey應用。此應用程序需要脫機工作,以便用戶可以使用它,當手機上線時,即使應用程序在後臺或手機處於待機狀態,應用程序也會將所有工作發送到服務器。

Android平臺的解決方案:

正如我發現,物體的TThread和TTimer,因爲他們停止運行,一旦應用程序被切換到後臺或手機進入待機不起作用。

我最終找到了這個類似於TTimer對象的android庫,但它在應用程序轉到背景或手機處於待機狀態時仍然有效。

https://github.com/dkstar88/AsyncTask

遺憾的是它並沒有在iOS上,因爲它的行爲就像TThread類和TTimer工作,並停止一旦應用程序切換到後臺。

的iOS試圖解決方案

我試了幾種方法,但我發現,很多人有很多關於Android,但信息的iOS少了很多。

我嘗試的第一件事是在plist文件中添加所需的權限,告訴iOS我想在後臺執行某些操作。我嘗試了「獲取」屬性。

這一點在TTimer,TThread或AsyncTask在Android中嘗試不起作用。

所以,我認爲你必須告訴iOS你想在後臺做些什麼,以及你希望iOS在背景中執行什麼程序或代碼。

最promsinig代碼,我發現這個鏈接(查看評論):

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

這裏是我使用的代碼,改編自以前的鏈接。它只是將一行添加到備註字段進行測試。

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. 

這不適用於我的iPhone 4與iOS 8.4.1。我將手機擱置了一會兒,當我查看應用程序時,備忘錄字段爲空。

有什麼想法可能是錯的?我在這裏有點死路一條。

非常感謝!在我原來的文章中,我把更多的鏈接和來源(包括其他stackoverflow帖子),但不幸的是我只剩下最相關的兩個,因爲我沒有10個聲望需要發佈更多。


從這兩個例子混合代碼,我做了如下單位:

http://qc.embarcadero.com/wc/qcmain.aspx?d=128968(見註釋) 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. 

此代碼不能正常工作。我在應用程序啓動時調用initializeBackgroundFetch。我確信我做錯了什麼,但不知道它是什麼。

另外我注意到我的應用程序沒有出現在背景允許應用程序在iPhone設置,應該出現嗎?我在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> 

任何想法?

+0

的另外的源: iOS的背景屬性:[鏈接](http://delphi.radsoft.com .au/2013/10/provides-background-services-support-to-your-ios-apps-with-delphi /) StackOverflow相關鏈接:[link](https://stackoverflow.com/questions/24168144/ delphi-firemonkey-ios-background-fetch) Objective-C示例: [鏈接](http://hayageek.com/ios-background-fetch/) [li nk](https://possiblemobile.com/2013/09/ios-7-background-fetch/) –

+0

也試過這個[link](http://codeverge.com/embarcadero.delphi.ios/does-delphi- for-ios-support-complete -b/1095555) 但引發了「外部異常0」。 –

+0

用新代碼更新了主文章 –

回答

2

iOS的工作液

我終於得到了它的Delphi下10西雅圖(你需要這個版本,我已經嘗試XE7並不起作用,我沒試過xe8)工作。以下是步驟:

1-項目>選項>版本信息。檢查UIBackgroundModes需要(I用於抓取)

2-這是我所使用的代碼:

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.