如何让Delphi开发的Android应用后台恢复时不重启
问题翻译(中文)
我用Delphi开发了一款Android应用,启动应用并通过Intent唤起另一款应用后,关闭第二个应用返回第一个应用时,第一个应用会重启。我知道onSaveState相关机制,但我需要应用不重启——因为要用到第二个应用返回的数据。我通过Intent启动第二个应用,关闭它后应该回到第一个应用能处理返回Intent的状态,但实际却是重启了。
更新:以下是我的代码:
procedure TForm1.btnEnviarTransacaoClick(Sender: TObject); begin try var AURI:string:= 'payment-app://pay?return_scheme=retorno&amount=9999&editable_amount=0&transaction_type=credit'; var AIntent: JIntent := TJIntent.Create; AIntent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW); AIntent.addFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK); AIntent.setData(TJNet_Uri.JavaClass.parse(StringToJString(TIdURI.URLEncode(AURI)))); TAndroidHelper.Activity.startActivityForResult(AIntent, 0); Timer1.Enabled:= True; except on E: Exception do begin TThread.Synchronize(nil, procedure begin lOG.D(E.Message); end); end; end; end; procedure TForm1.Timer1Timer(Sender: TObject); begin Timer1.Enabled:= False; var ADLg:= TForm2.Create(Self); ADLg.Show; end;
注:启动第二个Activity后,我触发了一个4秒的定时器来创建并显示Form2,但结束第二个应用返回主应用时,主应用会重启。调试时偶尔会捕获到异常:
cant perform this action after onSaveInstanceState
这是我的AndroidManifest,其中通过Scheme使用显式Intent:
<activity android:name=".PaymentActivity" android:launchMode="singleTop" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:host="pay-response" android:scheme="retorno" /> </intent-filter> </activity>
1. 移除错误的Intent Flag
你给Intent添加了FLAG_ACTIVITY_NEW_TASK,但调用startActivityForResult时完全不需要这个Flag——当前Activity已经处于任务栈中,添加该Flag会强制将第二个应用启动到新任务栈,返回时系统可能会重建原Activity,直接触发重启。
修改代码,删除这一行:
AIntent.addFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
2. 废弃定时器逻辑,改用onActivityResult处理返回
定时器触发时,原Activity大概率已经执行了onSaveInstanceState(启动第二个应用后,原Activity会进入后台并保存状态),此时调用Show显示Form2会触发cant perform this action after onSaveInstanceState异常,这正是应用重启的直接原因。
正确做法是重写onActivityResult方法,在第二个应用返回后再处理数据并显示Form2:
首先在Form1的类声明中添加重写方法:
type TForm1 = class(TForm) btnEnviarTransacao: TButton; Timer1: TTimer; procedure btnEnviarTransacaoClick(Sender: TObject); procedure Timer1Timer(Sender: TObject); private procedure ActivityResult(RequestCode, ResultCode: Integer; Data: JIntent); override; public { Public declarations } end;
然后实现该方法:
procedure TForm1.ActivityResult(RequestCode, ResultCode: Integer; Data: JIntent); begin inherited; if RequestCode = 0 then // 对应startActivityForResult的请求码 begin if ResultCode = TJActivity.JavaClass.RESULT_OK then begin // 处理第二个应用返回的数据,示例:获取返回的Uri if Assigned(Data) and Assigned(Data.getData) then begin var ReturnUri := JStringToString(Data.getData.toString); // 这里添加数据处理逻辑 end; // 此时Activity处于活跃状态,安全显示Form2 var ADLg := TForm2.Create(Self); ADLg.Show; end; end; end;
同时删除btnEnviarTransacaoClick中的定时器启动代码:
// Timer1.Enabled:= True; // 删掉这一行
3. 调整Activity启动模式与Intent Filter
确保主应用的主Activity(非PaymentActivity)的launchMode设置为singleTop或standard(避免singleTask等可能导致重建的模式),同时给主Activity添加接收返回Scheme的Intent Filter,保证返回数据能正确传递到原Activity:
<!-- 主应用主Activity的配置 --> <activity android:name=".MainActivity" android:launchMode="singleTop" android:exported="true"> <!-- 原有启动入口Filter --> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <!-- 添加接收返回Scheme的Filter --> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:host="pay-response" android:scheme="retorno" /> </intent-filter> </activity>
4. 禁止后台状态下的UI操作
永远不要在Activity可能进入后台后(比如调用startActivity之后),通过定时器、异步回调等方式执行UI操作——此时Activity可能已被系统回收,或处于状态已保存的不可修改状态,极易引发异常和重启。
内容的提问来源于stack exchange,提问作者Alisson Cristhian

