Mô tả vấn đề:Delphi Firemonkey iOS xử lý nền
Tôi hiện đang phát triển và ứng dụng Android và iOS với Delphi XE7 Firemonkey. Ứng dụng này cần phải hoạt động ngoại tuyến để người dùng có thể làm việc với nó và khi điện thoại trực tuyến, ứng dụng sẽ gửi tất cả công việc tới máy chủ ngay cả khi ứng dụng ở chế độ nền hoặc điện thoại ở chế độ chờ.
Android giải pháp làm việc:
Như tôi đã phát hiện ra, các đối tượng TThread và TTimer không hoạt động khi họ ngừng chạy khi ứng dụng đi vào nền hoặc điện thoại chuyển sang đứng.
Tôi đã kết thúc tìm thư viện này cho android hoạt động giống như đối tượng TTimer nhưng nó vẫn hoạt động khi ứng dụng chuyển sang nền hoặc điện thoại ở chế độ chờ.
https://github.com/dkstar88/AsyncTask
Đáng tiếc là nó không hoạt động trên iOS vì nó hoạt động như TThread và TTimer và nó dừng lại một khi ứng dụng đi vào nền.
iOS cố gắng giải pháp
Tôi đã thử nhiều cách tiếp cận nhưng tôi phát hiện ra rằng có rất nhiều người có rất nhiều thông tin về Android nhưng ít hơn rất nhiều cho iOS.
Điều đầu tiên tôi đã thử là thêm vào tệp plist các quyền cần thiết để cho iOS biết rằng tôi muốn làm điều gì đó trong nền. Tôi đã thử thuộc tính "fetch".
Tính năng này không hoạt động với TTimer, TThread hoặc AsyncTask đã thử trong Android. Vì vậy, tôi nghĩ bạn phải nói với iOS rằng bạn muốn làm điều gì đó ở chế độ nền VÀ bạn muốn iOS thực hiện quy trình hoặc mã nào khi chạy trong backgroud.
Mã promsinig nhất mà tôi tìm thấy là trong các liên kết này (xem ý kiến):
http://qc.embarcadero.com/wc/qcmain.aspx?d=128968
Đây là mã tôi đã sử dụng, chuyển thể từ các liên kết trước đó. Nó chỉ đơn giản là thêm một dòng vào một lĩnh vực ghi nhớ để thử nghiệm.
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.
Điều này không hoạt động trong iphone 4 của tôi với iOS 8.4.1. Tôi để điện thoại ở lại một lúc và khi tôi kiểm tra ứng dụng, trường ghi nhớ bị trống.
Bất kỳ ý tưởng nào về điều gì có thể sai? Tôi hơi chết ở đây.
Cảm ơn rất nhiều!
PS: trong bài đăng gốc, tôi đặt nhiều liên kết và nguồn hơn (bao gồm cả các bài đăng ngăn xếp khác) nhưng tiếc là tôi chỉ để lại hai liên quan nhất vì tôi không có 10 danh tiếng cần thiết để đăng thêm.
Trộn mã từ hai ví dụ này tôi đã thực hiện các đơn vị sau đây:
http://qc.embarcadero.com/wc/qcmain.aspx?d=128968 (xem ý kiến) 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.
Mã này không hoạt động. Tôi gọi initializeBackgroundFetch khi ứng dụng bắt đầu. Tôi chắc chắn tôi đang làm điều gì đó sai trái nhưng không thể hình dung nó là gì.
Ngoài ra, tôi nhận thấy ứng dụng của tôi không xuất hiện ở chế độ nền cho phép ứng dụng trong cài đặt iPhone, nó có xuất hiện không? Tôi đã thêm thông tin sau vào tệp 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>
Bất kỳ ý tưởng nào?
nguồn bổ sung: tính nền iOS: [link] (http://delphi.radsoft.com .au/2013/10/cung cấp-nền-dịch vụ-hỗ trợ-to-bạn-ios-apps-with-delphi /) StackOverflow liên kết liên quan: [link] (https://stackoverflow.com/questions/24168144/ delphi-firemonkey-ios-background-fetch) Ví dụ về mục tiêu-C: [liên kết] (http://hayageek.com/ios-background-fetch/) [li nk] (https://possiblemobile.com/2013/09/ios-7-background-fetch/) –
Cũng đã thử [link] này (http://codeverge.com/embarcadero.delphi.ios/does-delphi- for-ios-support-complete-b/1095555) Nhưng tăng "Ngoại lệ bên ngoài 0". –
cập nhật bài đăng chính với mã mới –