android 4.4.X:taskAffinity&;启动模式与活动生命周期

android 4.4.X:taskAffinity&;启动模式与活动生命周期,android,android-activity,launchmode,taskaffinity,Android,Android Activity,Launchmode,Taskaffinity,我开发了一个简单的应用程序,演示了我注意到的安卓4.4.X设备上的一些奇怪行为 假设我想有两个“主要”活动,第一个活动在恢复时每秒说一声“你好”(通过启动“HelloActivity”),第二个活动定义了android:launchMode=“singleTask”android:taskAffinity=“.MyAffinity”。第二个由第一个启动 我的代码 清单非常简单: <uses-sdk android:minSdkVersion="8" android:targ

我开发了一个简单的应用程序,演示了我注意到的安卓4.4.X设备上的一些奇怪行为

假设我想有两个“主要”活动,第一个活动在恢复时每秒说一声“你好”(通过启动“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>

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

测试场景
  • 启动main活动
  • 开始亲缘关系
  • Finish AffinityTestActivity(当AffinityTestActivity完成时,MainActivity恢复,HelloActivity启动)
  • 分析输出
  • 日志 安卓4.4.2和4.4.3(在Nexus 7 II和三星Galaxy S5上测试) 如您所见,日志以HelloActivity的onPause结束,这毫无意义(HelloActivity在步骤3的顶部显示)。此外,亲缘关系不会被破坏,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版本(我已经根据您提供的代码创建了一个项目,并且我能够在我自己的Nexus 7上重新创建您的问题。虽然我没有具体的学术答案,但我最好的解释如下:

    1) 主活动已启动

    2) 点击按钮。AffinityTestActivity在新任务中启动

    3) 点击按钮。亲缘关系不稳定

    4) MainActivity在旧任务中恢复

    5) 在MainActivity的onResume中,在同一任务中调用HelloActivity的意图

    6) 经过一点修补后,我的理论中有一个神秘的部分:将旧任务带到前台的某些部分在调用onResume期间继续与MainActivity(旧任务的根)交互。这种交互会触发HelloActivity的onPause方法(操作系统开发人员可能并不打算这样做)。虽然这不是最令人满意的答案(考虑到我在操作系统级别的调度代码和计时问题方面的经验有限),但我的实验表明了这一点。我对这种干扰的第一个线索是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 intent:

     @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在其新任务中无法访问,即使它是关闭应用程序之前查看的最后一个活动)

    我最好的建议是找到一种避免这种特殊情况的方法。:)这似乎是Android更高版本中的一个bug,尽管是一个奇怪的edge案例。如果那不是一个选择,你可以走我过去常走的路线。我尝试过其他一些方法,但很难回避这样一个事实,即调度是在我们无法掌握的操作系统级别上控制的


    很抱歉,我不能给你一个更深入的答案,但这就是我现在所能得到的

    谢谢你的回答,我已经发现在他的活动启动上使用一些延迟(我在测试中使用了定时器和500毫秒延迟)可以使它正常工作。我怀疑这是安卓操作系统中的一个bug,我可能会提交一份bug报告。我将等待几天,等待其他一些答案,如果没有人有更好的答案,那么提供的声誉是你的谢谢你能抽出时间来提交那个bug报告吗?:)我刚刚提交了一个bug:)谢谢,为了更好地理解,请参考此链接。
     @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;
        }
    }
    
    <activity
        android:label="HELLO"
        android:name="com.stackoverflow.HelloActivity"
        android:configChanges="keyboardHidden|orientation|screenSize"
        android:launchMode="singleTask"
        android:taskAffinity=".DifferentTask">
    </activity>