2015-09-02 30 views
5

Descrizione del problema:Delphi FireMonkey iOS sfondo elaborazione

Attualmente sto sviluppando e Android e iOS applicazioni con Delphi XE7 FireMonkey. Questa app deve funzionare offline in modo che l'utente possa lavorare con esso e quando il telefono passa online l'app invia tutto il lavoro ai server anche se l'app è in background o il telefono è in stand by.

soluzione di lavoro Android:

Come ho scoperto, oggetti TThread e TTimer non funziona come si fermano in esecuzione una volta che l'applicazione va a sfondo o il telefono va a stare a.

Alla fine ho trovato questa libreria per Android che funziona come un oggetto TTimer ma funziona ancora quando l'app va in background o il telefono è in standby.

https://github.com/dkstar88/AsyncTask

Purtroppo non funziona su iOS come si comporta come TThread e TTimer e si ferma una volta che l'applicazione va a sfondo.

iOS hanno cercato soluzioni

Ho provato diversi approcci, ma ho scoperto che un sacco di gente ha un sacco di informazioni su Android, ma molto meno per iOS.

La prima cosa che ho provato è stata aggiungere nel file plist le autorizzazioni necessarie per dire a iOS che voglio fare qualcosa in background. Ho provato la proprietà "fetch".

Questo da solo non funziona con TTimer, TThread o AsyncTask provato in Android.

Quindi, ho pensato che devi dire a iOS che vuoi fare qualcosa in background E quale procedura o codice vuoi che iOS esegua quando è in backgroud.

Il codice più promsinig ho trovato è stato in questo link (vista commenti):

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

Ecco il codice che ho usato, tratto dal link precedente. Semplicemente aggiunge una riga a un campo memo per il test.

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. 

Questo non funziona nel mio iPhone 4 con iOS 8.4.1. Ho lasciato il telefono in stand by per un po 'e quando ho controllato l'app il campo memo era vuoto.

Qualche idea di cosa potrebbe essere sbagliato? Sono un po 'di punto morto qui.

Grazie mille!

PS: nel mio post originale ho messo più link e fonti (compresi altri post di stackoverflow) ma sfortunatamente ho lasciato solo i due più rilevanti perché non ho la 10 reputazione necessaria per postare di più.


miscelazione codice da questi due esempi che ho fatto la seguente unità:

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

Questo codice non funziona. Chiama initializeBackgroundFetch all'avvio dell'applicazione. Sono sicuro che sto facendo qualcosa di sbagliato ma non riesco a capire cosa sia.

Inoltre ho notato che la mia applicazione non viene visualizzata in background per consentire le applicazioni nelle impostazioni di iPhone, dovrebbe apparire? Ho aggiunto quanto segue nel file 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> 

Qualche idea?

+0

Fonti aggiuntive: proprietà dello sfondo iOS: [link] (http://delphi.radsoft.com .au/2013/10/providing-background-services-support-to-your-ios-apps-with-delphi /) Link relativo a StackOverflow: [collegamento] (https://stackoverflow.com/questions/24168144/ delphi-firemonkey-ios-background-fetch) Esempi Objective-C: [link] (http://hayageek.com/ios-background-fetch/) [li nk] (https://possiblemobile.com/2013/09/ios-7-background-fetch/) –

+0

Ho provato anche questo [collegamento] (http://codeverge.com/embarcadero.delphi.ios/does-delphi- for-ios-support-complete-b/1095555) Solleva "Eccezione esterna 0". –

+0

aggiornato il post principale con il nuovo codice –

risposta

2

iOS di lavoro Soluzione

ho finalmente farlo funzionare sotto Delphi 10 Seattle (avete bisogno di questa versione, ho provato XE7 e non funziona, non ho provato xe8). Ecco i passaggi:

1- Progetto> Opzioni> Informazioni sulla versione. Controllare UIBackgroundModes è necessario (io ho usato Fetch)

2- Questo è il codice che ho usato:

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.