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?
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/) –
Ho provato anche questo [collegamento] (http://codeverge.com/embarcadero.delphi.ios/does-delphi- for-ios-support-complete-b/1095555) Solleva "Eccezione esterna 0". –
aggiornato il post principale con il nuovo codice –