android - Delphi Firemonkey iOS后台处理

标签 android ios delphi firemonkey

问题描述:

我目前正在使用 Delphi XE7 Firemonkey 开发 Android 和 iOS 应用程序。此应用程序需要离线工作,以便用户可以使用它,当手机上线时,即使应用程序处于后台或手机处于待机状态,应用程序也会将所有工作发送到服务器。

Android 工作解决方案:

我发现,对象 TThread 和 TTimer 不起作用,因为一旦应用程序进入后台或手机进入待机状态,它们就会停止运行。

我最终找到了这个适用于 android 的库,它像 TTimer 对象一样工作,但当应用程序进入后台或手机处于待机状态时它仍然可以工作。

https://github.com/dkstar88/AsyncTask

不幸的是,它在 iOS 上不起作用,因为它的行为类似于 TThread 和 TTimer,一旦应用程序进入后台,它就会停止。

iOS 尝试过的解决方案

我尝试了几种方法,但我发现很多人了解很多关于 Android 的信息,但了解 iOS 的信息却很少。

我尝试的第一件事是在 plist 文件中添加所需的权限,以告诉 iOS 我想在后台执行某些操作。我尝试了“获取”属性。

仅此一项不适用于在 Android 中尝试过的 TTimer、TThread 或 AsyncTask。

所以,我认为你必须告诉 iOS 你想在后台做一些事情,以及你希望 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.

这在装有 iOS 8.4.1 的 iphone 4 上不起作用。我 Handlebars 机放在待机状态了一会儿,当我检查应用程序时,备忘录字段是空白的。

知道哪里出了问题吗?我在这里有点死胡同。

非常感谢!

PS:在我的原始帖子中,我放置了更多链接和来源(包括其他 stackoverflow 帖子),但不幸的是我只留下了最相关的两个,因为我没有发布更多帖子所需的 10 个声誉。


将这两个示例中的代码混合在一起,我制作了以下单元:

http://qc.embarcadero.com/wc/qcmain.aspx?d=128968 (看评论) 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,
                  '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 工作解决方案

我终于让它在 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.

关于android - Delphi Firemonkey iOS后台处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32348081/

相关文章:

德尔福通用框架

android - 使用两个 Viewholder 在 RecyclerView 中查看重叠

android - onCreate() 和 onCreateView() 调用比需要的多得多( fragment )

c# - 使用 LIKE 表达式时 SQLite 中的土耳其字符

ios - 为代码签名身份选择哪些配置文件与分发签名与代码签名权利

delphi - 我如何知道控件将停靠在哪里,以及如何防止停靠?

Visual Studio 2010 中的 Delphi "Object TreeView"等效项

android - 如何在 recyclerview 中为每个 cardview 实现单选警报对话框?

ios - 使 iPhone 进入待机状态

ios - 使用 iOS5 和 iOS6 的 Facebook