问题描述:
我正在使用Delphi XE7 Firemonkey开发一个Android和iOS应用程序。这个应用程序需要离线工作,这样用户就可以在没有网络的情况下使用它,当手机联网时,无论应用程序处于后台还是手机处于待机状态,应用程序都会将所有工作发送到服务器。
Android解决方案:
我发现,在应用程序进入后台或手机进入待机状态后,TThread和TTimer对象停止运行。
最终,我在Android上找到了这个库,它像TTimer对象一样工作,但即使应用程序进入后台或手机待机时仍然可以工作。
https://github.com/dkstar88/AsyncTask
不幸的是,它在iOS上不起作用,因为它的行为类似于TThread和TTimer,一旦应用程序进入后台,它就停止了。
iOS尝试过的解决方案
我尝试了几种方法,但发现很多人对Android有很多信息,但对iOS的了解则相对较少。
我尝试的第一件事是在plist文件中添加所需的权限,告诉iOS我想在后台执行某些操作,我尝试了“fetch”属性。
但是这单独使用并不能解决TTimer、TThread或在Android中尝试了AsyncTask的问题。
因此,我认为你必须告诉iOS,你要在后台进行某些操作,并且在后台时要执行哪个过程或代码。
我发现最有希望的代码在此链接中(查看评论):
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,'v@:@?');
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无法正常运行此应用程序。我将手机放置一段时间并检查应用程序时,备忘录字段为空白。您有什么解决办法吗?我现在陷入了困境。谢谢!PS:在我的原帖中,我放置了更多链接和来源(包括其他stackoverflow帖子),但不幸的是,我只留下了最相关的两个,因为我没有达到发布更多帖子所需的10声望。 通过混合以下两个示例的代码,我创建了以下单元:http://qc.embarcadero.com/wc/qcmain.aspx?d=128968(请参见评论)从Delphi调用Objective C代码块。
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,
'v@:@?'
);
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>
有什么想法吗?
qc.embarcadero.com
链接。如果您需要访问旧的QC数据,请查看QCScraper。 - Remy Lebeau