10

私が気付いた Android 4.4.X デバイスでのいくつかの奇妙な動作を示す簡単なアプリケーションを開発しました。

2 つの「メイン」アクティビティが必要であるとします。最初のアクティビティは、再開されるたびに「Hello」(「HelloActivity」を開始して) と言い、2 番目のアクティビティはandroid:launchMode="singleTask" android:taskAffinity=".MyAffinity"定義済みです。2 つ目は最初のものによって開始されます。

マイコード

マニフェストは非常に単純です。

<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 しかありません

テストシナリオ

  1. MainActivity を開始します。
  2. AffinityTestActivity を開始します。
  3. AffinityTestActivity を終了します (AffinityTestActivity が終了すると、MainActivity が再開され、HelloActivity が開始されます)。
  4. 出力を分析します。

ログ

Android 4.4.2 および 4.4.3 : (Nexus 7 II および Samsung 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 (<4.4.2、2.3.5、4.1.2、および 4.2.1 デバイスでテスト済み、4.0.3 エミュレーター) は期待どおりに動作します - onResume 後に HelloActivity が一時停止されず、AffinityTestActivity が破棄されます。

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

私の質問

  • Android 4.4.X デバイスで HelloActivity が開始された直後に一時停止されて上部に表示されるのはなぜですか?
  • 古い Android バージョン (<4.4.2) のように、それを回避してアプリケーションに「通常の」アクティビティ ライフサイクルを強制するにはどうすればよいですか?

はるかに複雑で、そのアクティビティのライフサイクルで動作するアプリケーションを開発していますが、この動作はアプリケーションの機能に違反しています。

どうもありがとうございました!

4

1 に答える 1

6

提供されたコードに基づいてプロジェクトを作成し、自分の Nexus 7 で問題を再現することができました。具体的で学術的な回答はありませんが、私の最善の説明は次のとおりです。

1) MainActivity が開始されます

2) ボタンがクリックされました。AffinityTestActivity は新しいタスクで開始されます。

3) ボタンがクリックされました。AffinityTestActivity が終了します。

4) MainActivity は古いタスク内で再開します。

5) MainActivity の onResume では、HelloActivity のインテントが同じタスク内で呼び出されます。

6) 少しいじくり回した後の私の理論である不可解な部分: 古いタスクをフォアグラウンドに持ってくることの一部は、古いタスクのルートである MainActivity と、その onResume 呼び出し中に対話し続けます。この相互作用により、HelloActivity の onPause メソッドがトリガーされます (おそらく OS 開発者が意図したものではありません)。これは最も満足のいく答えではありませんが (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 内でアクティビティを開始すると、ライフサイクルで意図しない競合が発生することを示しています。

目に見えない処理を完了するためにアクティビティ/タスクに 1 秒を与えた場合に何が起こるかを確認するために、ハンドラーを使用して 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 は呼び出されませんでした。明らかに、これは実際のコードには理想的ではありませんが、実行時間を 1 秒進めるだけで問題が解決したことを示しています。タスク内での内部スケジュールの競合の証拠が増えました。

次に、HelloActivity にも独自のタスクを与えてみました。

<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の新しいバージョンのバグのようです。それができない場合は、私が以前に回避したルートの 1 つを追求することができます。他にもいくつか試してみましたが、スケジューリングが OS レベルで制御されているという事実を回避するのは困難です。

詳細な回答が得られなくて申し訳ありませんが、今のところは以上です。

于 2014-06-24T15:33:17.903 に答える