Delphi Firemonkey iOS后台处理 [英] Delphi Firemonkey iOS background processing

查看:218
本文介绍了Delphi Firemonkey iOS后台处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题描述:

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

I am currently developing and Android and iOS application with Delphi XE7 Firemonkey. This app needs to work offline so user can work with it and when the phone goes online the app sends all the work to the servers even if the app is on background or the phone is in stand by.

Android工作解决方案

我发现,对象TThread和TTimer在停止运行时无效一旦应用程序转到后台或电话就会待命。

As I found out, objects TThread and TTimer doesn't work as they stop running once the app goes to background or the phone goes to stand by.

我最终找到了这个类似于TTimer对象的android库,但它仍适用于app转到后台或手机待机。

I ended up finding this library for android that works like a TTimer object but it still works when the app goes to background or the phone is on stand by.

https://github.com/dkstar88/AsyncTask

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

Unfortunately it doesn't work on iOS as it behaves like TThread and TTimer and it stops once the app goes to background.

iOS尝试的解决方案

我尝试了几种方法但是我发现很多人都有很多关于Android的信息,但对iOS来说要少得多。

I tried several approaches but i found that a lot of people has a lot of information about Android but a lot less for iOS.

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

The first thing i tried was adding in the plist file the required permissions to tell iOS that I want to do something in background. I tried the "fetch" property.

单独使用Android中的TTimer,TThread或AsyncTask无效。

This alone doesn't work with TTimer, TThread or AsyncTask tried in Android.

所以,我认为你必须告诉iOS你想在后台做一些事情以及你想在iOS中执行什么程序或代码。

So, I thought you have to tell iOS that you want to do something in background AND what procedure or code is you want iOS to execute when in backgroud.

我发现的最常见的代码是在这个链接中(查看评论):

The most promsinig code I found was in this links (view comments):

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

这是我使用的代码,改编自上一个链接。它只是在备注字段中添加一行进行测试。

Here is the code I used, adapted from the previous link. It simply adds a line to a memo field for testing.

 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中不起作用。我把手机待了一段时间,当我检查应用程序时,备忘录字段是空白的。

This doesn't work in my iphone 4 with iOS 8.4.1. I left the phone in stand by for a while and when I checked the app the memo field was in blank.

任何可能出错的想法?我在这里有点死路。

Any idea of what could be wrong? I am a bit of a dead end here.

非常感谢!

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

PS: in my original post i put more links and sources (including other stackoverflow posts) but unfortunately i only left the most relevant two because i don't have the 10 reputation required to post more.

从这两个例子中混合代码我做了以下单位:

Mixing code from this two examples i made the following unit:

http://qc.embarcadero.com/wc/qcmain.aspx?d=128968 (见评论)
从德尔福调用目标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。我确信我做错了但却无法确定它是什么。

This code doesn't work. I call initializeBackgroundFetch when the application starts. I am sure I'm doing something wrong but can't figure what is it.

另外我注意到我的应用程序没有出现在后台允许iPhone中的应用程序设置,它应该出现?我在info.plist文件中添加了以下内容:

Also I noticed my application doesn't appear in the background allow applications in the iPhone settings, should it appear? I added the following in the info.plist file:

<?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)。以下是步骤:

I finally got it to work under delphi 10 Seattle (you need this version, i have tried XE7 and doesn't work, I haven't tried xe8). Here are the steps:

1-项目>选项>版本信息。检查你需要的UIBackgroundModes(我用过Fetch)

1- Project > Options > Version Info. Check UIBackgroundModes you need (I used Fetch)

2-这是我用过的代码:

2- This is the code I used:

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.

这篇关于Delphi Firemonkey iOS后台处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆