2015-09-02 17 views
5

Problembeschreibung:Delphi Firemonkey iOS Hintergrundverarbeitung

Ich bin derzeit die Entwicklung und Android und iOS-Anwendung mit Delphi XE7 Firemonkey. Diese App muss offline arbeiten, damit der Benutzer damit arbeiten kann und wenn das Telefon online geht, sendet die App die gesamte Arbeit an die Server, selbst wenn die App im Hintergrund läuft oder das Telefon im Standby-Modus ist.

Android-Arbeitslösung:

Als ich herausgefunden habe, Objekte TThread und TTimer nicht funktioniert, wie sie ausgeführt wird gestoppt, sobald die App in den Hintergrund geht oder das Telefon zu stehen geht.

Ich fand diese Bibliothek für Android, die wie ein TTimer-Objekt funktioniert, aber es funktioniert immer noch, wenn die App in den Hintergrund geht oder das Telefon in Bereitschaft ist.

https://github.com/dkstar88/AsyncTask

Leider ist es nicht auf iOS arbeiten, wie es wie TThread und TTimer verhält und es stoppt, sobald die App in den Hintergrund geht.

iOS versucht Lösungen

ich verschiedene Ansätze ausprobiert, aber ich fand, dass eine Menge Leute eine Menge Informationen über Android hat aber viel weniger für iOS.

Das erste, was ich ausprobiert habe, war das Hinzufügen der PLIST-Datei die erforderlichen Berechtigungen, iOS zu sagen, dass ich etwas im Hintergrund tun möchte. Ich habe die Eigenschaft "fetch" versucht.

Dies allein funktioniert nicht mit TTimer, TThread oder AsyncTask in Android versucht.

Also dachte ich, du musst iOS sagen, dass du etwas im Hintergrund tun willst UND welche Prozedur oder welchen Code iOS im Backgroud ausführen soll.

Der promsinig Code Ich war (siehe Kommentare) in diesen Links gefunden:

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

Hier ist der Code ich aus dem vorherigen Link angepasst verwendet. Es fügt einfach eine Zeile zum Testen in ein Memofeld ein.

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. 

Dies funktioniert nicht in meinem iPhone 4 mit iOS 8.4.1. Ich ließ das Telefon für eine Weile stehen und als ich die App überprüfte, war das Memofeld leer.

Irgendeine Idee von was könnte falsch sein? Ich bin hier ein bisschen eine Sackgasse.

Vielen Dank!

PS: in meinem ursprünglichen Beitrag habe ich mehr Links und Quellen (einschließlich andere stackoverflow Beiträge), aber leider habe ich nur die relevantesten zwei, weil ich nicht die 10 Ruf benötigt, um mehr zu posten.


aus diesen beiden Beispielen Misch Code habe ich die folgende Einheit:

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

Dieser Code funktioniert nicht. Ich rufe initializeBackgroundFetch beim Start der Anwendung auf. Ich bin mir sicher, dass ich etwas falsch mache, aber nicht herausfinden kann, was es ist.

Auch ich habe bemerkt, dass meine Anwendung nicht im Hintergrund erscheint Anwendungen in den iPhone-Einstellungen zulassen, sollte es angezeigt werden? Ich fügte folgendes in die info.plist Datei hinzu:

<?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> 

Irgendwelche Ideen?

+0

Weitere Quellen: iOS Hintergrund Eigenschaften: [link] (http://delphi.radsoft.com .au/2013/10/providering-background-services-support-to-your-ios-apps-with-delphi /) Link zu StackOverflow: [link] (https://stackoverflow.com/questions/24168144/ delphi-firemonkey-ios-background-fetch) Objective-C Beispiele: [link] (http://hayageek.com/ios-background-fetch/) [li nk] (https://possiblemobile.com/2013/09/ios-7-background-fetch/) –

+0

Auch versucht dies [link] (http://codeverge.com/embarcadero.delphi.ios/does-delphi-i- for-ios-support-complete-b/1095555) Aber löst "Externe Ausnahme 0". –

+0

aktualisiert die Hauptpost mit neuen Code –

Antwort

2

iOS Arbeitslösung

Ich habe es endlich unter delphi 10 Seattle (diese Version benötigen, i XE7 haben versucht, und nicht funktioniert, ich habe xe8 nicht versucht) zu arbeiten. Hier sind die Schritte:

1- Projekt> Optionen> Versionsinfo. Prüfen UIBackgroundModes Sie benötigen (I verwendet Fetch)

2- Dies ist der Code, den ich verwendet:

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. 
Verwandte Themen