我开发了一个简单的应用程序,演示了我注意到的android 4.4.x设备上的一些奇怪行为。
假设我希望有两个“main”活动,第一个活动在每次恢复时都说“hello”(通过启动“helloactivity”),第二个活动定义了android:launchMode="singleTask" android:taskAffinity=".MyAffinity"
。第二个由第一个开始。
我的代码
清单很简单:
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="14" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.affinitylaunchmodebugtest.MainActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:label="HELLO"
android:name="com.example.affinitylaunchmodebugtest.HelloActivity"
android:configChanges="keyboardHidden|orientation|screenSize">
</activity>
<activity
android:label="AffinityTestActivity"
android:name="com.example.affinitylaunchmodebugtest.AffinityTestActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:launchMode="singleTask"
android:taskAffinity=".MyAffinity">
</activity>
</application>
mainActivity在单击按钮时启动affinityTestActivity并记录其生命周期。它还将在每次恢复时启动HelloActivity:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
System.out.println(this+" onCreate");
super.onCreate(savedInstanceState);
Button b = new Button(MainActivity.this);
b.setText("START AFFINITY TEST ACTIVITY");
b.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
System.out.println(this+" starts "+AffinityTestActivity.class.getSimpleName());
Intent intent = new Intent(MainActivity.this, AffinityTestActivity.class);
startActivity(intent);
}
});
setContentView(b);
}
private boolean skipHello = true;
@Override
protected void onResume() {
System.out.println(this+" onResume");
super.onResume();
if (!skipHello) {
System.out.println(this+" starts "+HelloActivity.class.getSimpleName());
Intent intent = new Intent(MainActivity.this, HelloActivity.class);
startActivity(intent);
skipHello = true;
} else {
skipHello = false;
}
}
@Override
protected void onPause() {
System.out.println(this+" onPause");
super.onPause();
}
@Override
protected void onDestroy() {
System.out.println(this+" onDestroy");
super.onDestroy();
}
}
单击按钮时affinityTestActivity调用finish(),并记录其生命周期:
public class AffinityTestActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
System.out.println(this+" onCreate");
super.onCreate(savedInstanceState);
Button b = new Button(AffinityTestActivity.this);
b.setText("FINISH");
b.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
System.out.println(this+" finishes");
finish();
}
});
setContentView(b);
}
@Override
protected void onResume() {
System.out.println(this+" onResume");
super.onResume();
}
@Override
protected void onPause() {
System.out.println(this+" onPause");
super.onPause();
}
@Override
protected void onDestroy() {
System.out.println(this+" onDestroy");
super.onDestroy();
}
}
HelloActivity与AffinityTestActivity实际上是一样的——它只有按钮来调用finish()和printlns来记录它的生命周期。
测试方案
启动主活动。
启动AffinityTestActivity。
finish affinitytestactivity(当affinitytestactivity完成时,将恢复mainActivity并启动helloActivity)。
分析输出。
原木
Android 4.4.2和4.4.3:(在Nexus 7 II和三星Galaxy S5上测试)
如您所见,日志以helloactivity的onpause结束,这是没有意义的(helloactivity显示在步骤3的顶部)。同时,affinityTestActivity不会被破坏,mainActivity也不会暂停。
06-20 11:13:20.547: I/System.out(18650): com.example.affinitylaunchmodebugtest.MainActivity@41f17e50 onCreate
06-20 11:13:20.557: I/System.out(18650): com.example.affinitylaunchmodebugtest.MainActivity@41f17e50 onResume
06-20 11:13:25.371: I/System.out(18650): com.example.affinitylaunchmodebugtest.MainActivity$1@41f6e5c0 starts AffinityTestActivity
06-20 11:13:25.581: I/System.out(18650): com.example.affinitylaunchmodebugtest.MainActivity@41f17e50 onPause
06-20 11:13:25.591: I/System.out(18650): com.example.affinitylaunchmodebugtest.AffinityTestActivity@41f6a480 onCreate
06-20 11:13:25.611: I/System.out(18650): com.example.affinitylaunchmodebugtest.AffinityTestActivity@41f6a480 onResume
06-20 11:13:36.452: I/System.out(18650): com.example.affinitylaunchmodebugtest.AffinityTestActivity$1@41f1ede8 finishes
06-20 11:13:36.662: I/System.out(18650): com.example.affinitylaunchmodebugtest.AffinityTestActivity@41f6a480 onPause
06-20 11:13:36.682: I/System.out(18650): com.example.affinitylaunchmodebugtest.MainActivity@41f17e50 onResume
06-20 11:13:36.682: I/System.out(18650): com.example.affinitylaunchmodebugtest.MainActivity@41f17e50 starts HelloActivity
06-20 11:13:36.782: I/System.out(18650): com.example.affinitylaunchmodebugtest.HelloActivity@41f8dbb8 onCreate
06-20 11:13:36.802: I/System.out(18650): com.example.affinitylaunchmodebugtest.HelloActivity@41f8dbb8 onResume
06-20 11:13:36.852: I/System.out(18650): com.example.affinitylaunchmodebugtest.HelloActivity@41f8dbb8 onPause
较旧的Android版本(
06-20 11:16:30.867: I/System.out(3296): com.example.affinitylaunchmodebugtest.MainActivity@40f998b0 onCreate
06-20 11:16:30.907: I/System.out(3296): com.example.affinitylaunchmodebugtest.MainActivity@40f998b0 onResume
06-20 11:16:34.157: I/System.out(3296): com.example.affinitylaunchmodebugtest.MainActivity$1@40f9b350 starts AffinityTestActivity
06-20 11:16:34.277: I/System.out(3296): com.example.affinitylaunchmodebugtest.MainActivity@40f998b0 onPause
06-20 11:16:34.297: I/System.out(3296): com.example.affinitylaunchmodebugtest.AffinityTestActivity@40fab810 onCreate
06-20 11:16:34.357: I/System.out(3296): com.example.affinitylaunchmodebugtest.AffinityTestActivity@40fab810 onResume
06-20 11:16:38.687: I/System.out(3296): com.example.affinitylaunchmodebugtest.AffinityTestActivity$1@40fad640 finishes
06-20 11:16:38.707: I/System.out(3296): com.example.affinitylaunchmodebugtest.AffinityTestActivity@40fab810 onPause
06-20 11:16:38.717: I/System.out(3296): com.example.affinitylaunchmodebugtest.MainActivity@40f998b0 onResume
06-20 11:16:38.717: I/System.out(3296): com.example.affinitylaunchmodebugtest.MainActivity@40f998b0 starts HelloActivity
06-20 11:16:38.747: I/System.out(3296): com.example.affinitylaunchmodebugtest.MainActivity@40f998b0 onPause
06-20 11:16:38.777: I/System.out(3296): com.example.affinitylaunchmodebugtest.HelloActivity@40fbdd48 onCreate
06-20 11:16:38.827: I/System.out(3296): com.example.affinitylaunchmodebugtest.HelloActivity@40fbdd48 onResume
06-20 11:16:39.877: I/System.out(3296): com.example.affinitylaunchmodebugtest.AffinityTestActivity@40fab810 onDestroy
我的问题
为什么我的helloactivity在android 4.4.x设备上启动并显示在顶部后就暂停了?
我如何才能避免它,并强制应用程序具有“正常”的活动生命周期,就像旧的Android版本(我开发的应用程序更为复杂,并且可以处理其活动的生命周期,这种行为正在破坏我的应用程序的功能。
非常感谢你!
最佳答案
我已经基于您提供的代码创建了一个项目,并且我能够在自己的nexus 7上重新创建您的问题。虽然我没有给你一个具体的学术答案,但我最好的解释是:
1)主活动已启动
2)点击按钮。在新任务中启动AffinityTestActivity。
3)点击按钮。AffinityTestActivity完成。
4)主活动在旧任务中恢复。
5)在mainActivity的onResume中,在同一任务中调用helloActivity的意图。
6)经过一番修改后,我的理论中的神秘部分是:在旧任务的onresume调用期间,将旧任务带到前台的某些部分继续与旧任务的根mainactivity交互。这种交互导致HelloActivity的onPause方法被触发(可能不是OS开发人员想要的)。虽然这不是最令人满意的答案(考虑到我在操作系统级调度代码和计时问题上的有限经验),但我的实验指出了一些类似的问题。我对这种干扰的第一个线索是logcat中经常出现的错误:
06-24 11:06:28.015 27200-27200/com.stackoverflow I/System.out﹕ com.stackoverflow.MainActivity@64e05830 onPause
06-24 11:06:28.055 27200-27200/com.stackoverflow I/System.out﹕ com.stackoverflow.AffinityTestActivity@64e22fc0 onCreate
06-24 11:06:28.075 27200-27200/com.stackoverflow I/System.out﹕ com.stackoverflow.AffinityTestActivity@64e22fc0 onResume
06-24 11:06:28.175 665-685/? I/ActivityManager﹕ Displayed com.stackoverflow/.AffinityTestActivity: +163ms
06-24 11:06:29.997 27200-27200/com.stackoverflow I/System.out﹕ com.stackoverflow.AffinityTestActivity$1@64e24bf8 finishes
06-24 11:06:30.007 27200-27200/com.stackoverflow I/System.out﹕ com.stackoverflow.AffinityTestActivity@64e22fc0 onPause
06-24 11:06:30.027 27200-27200/com.stackoverflow I/System.out﹕ com.stackoverflow.MainActivity@64e05830 onResume
06-24 11:06:30.027 27200-27200/com.stackoverflow I/System.out﹕ com.stackoverflow.MainActivity@64e05830 starts HelloActivity
06-24 11:06:30.027 665-6346/? I/ActivityManager﹕ START u0 {cmp=com.stackoverflow/.HelloActivity} from pid 27200
06-24 11:06:30.117 27200-27200/com.stackoverflow I/System.out﹕ com.stackoverflow.HelloActivity@64e33b18 onCreate
06-24 11:06:30.127 27200-27200/com.stackoverflow I/System.out﹕ com.stackoverflow.HelloActivity@64e33b18 onResume
06-24 11:06:30.137 27200-27200/com.stackoverflow I/System.out﹕ com.stackoverflow.HelloActivity@64e33b18 onPause
06-24 11:06:30.287 665-685/? I/ActivityManager﹕ Displayed com.stackoverflow/.HelloActivity: +182ms
06-24 11:06:32.389 27200-27200/com.stackoverflow I/System.out﹕ com.stackoverflow.HelloActivity$1@64e356b0 finishes
06-24 11:06:32.389 27200-27200/com.stackoverflow I/System.out﹕ com.stackoverflow.HelloActivity@64e33b18 onDestroy
06-24 11:06:32.399 27200-27200/com.stackoverflow I/System.out﹕ com.stackoverflow.MainActivity@64e05830 onPause
06-24 11:06:32.399 27200-27200/com.stackoverflow E/ActivityThread﹕ Performing pause of activity that is not resumed: {com.stackoverflow/com.stackoverflow.MainActivity}
java.lang.RuntimeException: Performing pause of activity that is not resumed: {com.stackoverflow/com.stackoverflow.MainActivity}
at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3015)
at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3003)
at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:2981)
at android.app.ActivityThread.access$1000(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1207)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5001)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
06-24 11:06:32.409 27200-27200/com.stackoverflow I/System.out﹕ com.stackoverflow.MainActivity@64e05830 onResume
06-24 11:06:32.769 27200-27200/com.stackoverflow I/System.out﹕ com.stackoverflow.AffinityTestActivity@64e22fc0 onDestroy
如您所见,mainActivity的onPause方法在HelloActivity完成之后才被调用。那也不对。对我来说,这表明在onresume中启动一个活动,而将任务置于前台会在生命周期中导致一些意外的冲突。
为了查看如果我给活动/任务一秒时间来完成任何未看到的处理,会发生什么,我使用了一个处理程序来调用mainActivity中的helloActivity意图:
@Override
protected void onResume() {
System.out.println(this + " onResume");
super.onResume();
if (!skipHello) {
System.out.println(this+" starts "+HelloActivity.class.getSimpleName());
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(MainActivity.this, HelloActivity.class);
startActivity(intent);
}
}, 1000);
skipHello = true;
} else {
skipHello = false;
}
}
这导致了更好的行为。HelloActivity正常工作,未调用OnPause。很明显,这对于工作代码来说并不理想,但它表明只要将执行时间向前移动一秒就解决了这个问题。任务内部调度冲突的更多证据。
接下来,我也试着给他自己的任务:
<activity
android:label="HELLO"
android:name="com.stackoverflow.HelloActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:launchMode="singleTask"
android:taskAffinity=".DifferentTask">
</activity>
(记录在案,这种配置没有多大意义,但我假设它反映了实际项目中的一个场景,具有更合理的用途。)
在这种情况下,一切正常。HelloActivity的生命周期不会干扰MainActivity的生命周期。但是,它现在有了自己任务的开销和运行活动的伴随问题,如
singleTask
(点击“主页”按钮并重新打开应用程序将带您进入mainActivity,使helloActivity在其新任务中不可访问,即使它是关闭前查看的最后一个活动应用程序)。我最好的建议是找到避免这种特殊情况的方法。:)这似乎是安卓后期版本中的一个漏洞,尽管这是一个奇怪的边缘案例。如果那不是一个选择,你可以走我过去绕开它的一条路线。我尝试过其他一些方法,但很难回避这样一个事实:调度是在我们无法控制的操作系统级别上控制的。
很抱歉我不能给你一个更深入的答案,但这就是我现在所拥有的一切!