Delphi Firemonkey iOS 后台处理

5

问题描述:

我正在使用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>

有什么想法吗?

其他参考资料:iOS 后台属性:链接StackOverflow 相关链接:链接Objective-C 示例: 链接 链接 - Sion P.M.
也尝试了这个链接(http://codeverge.com/embarcadero.delphi.ios/does-delphi-for-ios-support-complete-b/1095555)但是引发了“外部异常0”。 - Sion P.M.
已更新主贴并附上新代码。 - Sion P.M.
请注意,QualityCentral现已关闭,因此您无法再访问qc.embarcadero.com链接。如果您需要访问旧的QC数据,请查看QCScraper - Remy Lebeau
1个回答

2

iOS 工作解决方案

我终于在 Delphi 10 Seattle 上成功运行了它(你需要这个版本,我尝试过 XE7 但不起作用,我没有尝试过 xe8)。以下是步骤:

1- 项目 > 选项 > 版本信息。勾选你需要的 UIBackgroundModes(我使用了 Fetch)

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,
                  'v@:@?');
end;

end.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接