android 4.4.X: taskaffinity & launchmode против жизненного цикла активности

Я разработал простое приложение, которое демонстрирует некоторые странное поведение на Android 4.4.X устройств я заметил.

допустим, я хочу иметь 2 "основных" действия, где первый говорит " Привет "(начиная "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 () и println кодом, который передает на вход своего жизненного цикла.

Проверка Сценария

  1. Запустить MainActivity.
  2. Запустить AffinityTestActivity.
  3. Finish AffinityTestActivity (когда AffinityTestActivity заканчивается, MainActivity возобновляется и запускается HelloActivity).
  4. анализ выходных данных.

журналы

Android 4.4.2 и 4.4.3: (протестировано на Nexus 7 II и Samsung Galaxy S5) Как вы можете видеть, log заканчивается 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@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@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@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@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 (

Я разрабатываю приложение, которое намного сложнее и работает с жизненным циклом своей деятельности, и это поведение нарушает функциональность моего приложения.

спасибо много!

1 ответов


Я создал проект на основе кода, который вы предоставили, и мне удалось воссоздать вашу проблему на моем собственном Nexus 7. Хотя у меня нет конкретного академического ответа для вас, мое лучшее объяснение заключается в следующем:

1) MainActivity запущен

2) кнопка нажата. AffinityTestActivity запускается в новой задаче.

3) кнопку нажал. AffinityTestActivity заканчивается.

4) MainActivity возобновляется в старой задаче.

5) в MainActivity onResume, намерение для HelloActivity вызывается в той же задаче.

6) таинственная часть, которая является моей теорией после небольшой возни: некоторая часть вывода старой задачи на передний план продолжает взаимодействовать с MainActivity, корнем старой задачи, во время ее onResume вызова. Это взаимодействие вызывает запуск метода onPause HelloActivity (вероятно, не предназначен разработчиками ОС). Хотя это не самый удовлетворительный ответ (учитывая мой ограниченный опыт работы с кодом планирования на уровне ОС и проблемами времени), мои эксперименты указывают на что-то в этом роде. Моей первой подсказкой к этому вмешательству была эта частая ошибка, наблюдаемая в 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@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@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.access00(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

как вы можете видеть, метод onPause MainActivity даже не вызывался до тех пор, пока HelloActivity не был завершен. Это тоже неправильно. Это, по-моему, показывает, что запуск активности в onResume во время вывода задачи на передний план вызывает некоторые непреднамеренные конфликты в жизненном цикле.

чтобы увидеть, что произойдет, если я дам activity / task секунду для завершения любой невидимой обработки, я использовал обработчик для вызова намерения HelloActivity в MainActivity:

 @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 не назвал. Очевидно, что это не идеально для рабочего кода, но это показывает, что простое перемещение времени выполнения вперед на секунду исправило проблему. Больше доказательств внутреннего конфликт планирования в задаче.

затем я попытался дать 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, хотя и странный край. Если это не вариант, вы можете преследовать один из маршруты, которые я использовал, чтобы обойти его. Я пробовал еще пару вещей, но трудно обойти тот факт, что планирование контролируется на уровне ОС вне нашей досягаемости.

Извините, что я не смог получить более подробный ответ, но это все, что у меня есть сейчас!