Intereting Posts
Как играть на YouTube в фоновом режиме в Android? Не удалось приостановить активность, пока еще не создан контент Android: Как использовать разные темы для разных версий Android? Почему параметр android.database.SQLException не установлен? Папки для Android Studio Пейджинг не работает, и, возможно, я нашел причину Отображение объявления внизу без перекрытия списка Ошибка компилятора: com.sun.tools.javac.code.Symbol $ CompletionFailure: файл класса для rx.Observable не найден IntentService будет убит после остановки моего приложения Черные текстуры Unity android при построении разработки с помощью отладки скриптов Значок приложения запуска не удаляется с Начального экрана при удалении приложения для Android Проблема с углублением эскизов в Android Studio 2.3 OnServiceDisconnected () не вызывается после вызова службы stopSelf () Внешний ключ SQLite Android, сделайте текст анимации прокручивающимся на экране, как первая часть Star Wars

ActivityLifecycleCallbacks не запускаются, когда активность убивается с помощью «Не продолжать действия»,

В моем приложении для Android у меня есть два вида деятельности:

  • DemoActivity с кнопкой для запуска SearchActivity с Intent
  • SearchActivity

Кнопка – это настраиваемая группа ViewGroup:

  • SearchButton

Как только SearchButton оживает, он регистрирует события жизненного цикла (соответствующего SearchActivity ):

 public class SearchButton extends CardView implements Application.ActivityLifecycleCallbacks { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); Context applicationContext = getContext().getApplicationContext(); if (applicationContext instanceof Application) { ((Application) applicationContext) .registerActivityLifecycleCallbacks(this); } } // ... 

События расходуются следующим образом:

 // ... @Override public void onActivityStarted(Activity activity) { if (activity instanceof SearchActivity) { SearchActivity searchActivity = (SearchActivity) activity; searchActivity.addSomeListener(someListener); } } @Override public void onActivityStopped(Activity activity) { if (activity instanceof SearchActivity) { SearchActivity searchActivity = (SearchActivity) activity; searchActivity.removeSomeListener(someListener); } } 

После SearchActivity я помещаю приложение в фоновый режим и возвращаю его на передний план. Можно увидеть следующий стек вызовов:

 1. SearchButton.onActivityStarted // triggered by DemoActivity 2. DemoActivity.onStart 3. SearchButton.onActivityStarted // triggered by SearchActivity 4. SearchActivity.addSomeListener 5. SearchActivity.onStart 

Как вы можете видеть, слушатель добавлен. Это прекрасно работает.


Проблема

Как только я включу « Don't keep activities в настройках разработчика», стек вызовов выглядит так, когда я снова получаю приложение:

 1. DemoActivity.onCreate 2. SearchButton.init // Constructor 3. DemoActivity.onStart 4. SearchActivity.onStart 5. SearchButton.onAttachedToWindow 6. DemoApplication.registerActivityLifecycleCallbacks 

Здесь слушатель не добавляется . onActivityStarted требуемый onActivityStarted вызов onActivityStarted запускаемый SearchActivity.onStart .

Короткий ответ

Вы видите вызовы onStart из представления только тогда, когда активность была перенесена на передний план после некоторого времени в фоновом режиме. В настоящее время невозможно увидеть более ранние события активности из представлений вашей деятельности, так как иерархия представлений все еще создается, а представления еще не привязаны к окну.

Когда активность инициализируется с нуля, иерархия представлений не полностью привязана до тех пор, пока она не будет onResume . Это означает, что, когда вы onAttachedToWindow представления, onStart уже выполнен. Если вы выйдете из действия, о котором вы говорили в вопросе, вы все равно должны видеть события onPause и т. Д.

Обычно, если вы помещаете операцию на задний план, нажав, например, кнопку «домой», действие останавливается, но не уничтожается. Он остается в памяти с его иерархией представлений, если для этого имеются достаточные системные ресурсы. Когда действие восстанавливается на передний план, вместо того, чтобы создавать его с нуля, он вызывает onStart и возобновляет его с того места, где он остановился, без воссоздания иерархии представления.

Параметр «Не сохранять действия» гарантирует, что каждое действие будет уничтожено сразу после его выхода на передний план, убедившись, что onAttachedToWindow вашего вида всегда onResume после onResume так как иерархию представления нужно воссоздавать каждый раз.

Что вы могли бы сделать вместо этого

Не разделяя больше кода, не сразу понятно, почему вам нужно установить слушателя в представлении. Кажется, что вам нужно в любом случае выслушать метод жизненного цикла активности.

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

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

В качестве альтернативы вы можете найти решение, которое в настоящее время использует Карты Google, например, в MapView. Он требует, чтобы активность проксировала все методы жизненного цикла для представления. Это может быть полезно, если ваш взгляд очень тесно связан с жизненным циклом деятельности. Здесь вы можете увидеть документацию.

Четвертый вариант – использовать фрагмент вместо представления, поскольку он имеет свой собственный набор методов жизненного цикла. Лично я не чувствую себя вполне комфортно с фрагментами, поскольку их жизненные циклы потенциально еще сложнее.

Более длинный ответ

Чтобы объяснить, почему это происходит, нам нужно углубиться в исходный код Android. То, что я объясняю здесь, специфично для этой реализации и может отличаться между версиями SDK и даже между устройствами Android из-за изменений производителя. Вы не должны полагаться на эти данные в своем коде. Я буду использовать исходный код SDK 23, который поставляется с Android Studio и Nexus 6P со сборкой MTC19T.

Самое легкое место для начала исследования – метод onAttachedToWindow . Когда это действительно называется? В его документации говорится, что он вызывается после того, как поверхность представления создана для рисования, но мы этого не удовлетворены.

Чтобы узнать, мы устанавливаем точку останова для представления, перезапускаем приложение, чтобы воссоздана активность, и исследуем первые несколько кадров в Android Studio:

 "main@4092" prio=5 runnable java.lang.Thread.State: RUNNABLE at com.lnikkila.callbacktest.TestView.onAttachedToWindow(TestView.java:18) at android.view.View.dispatchAttachedToWindow(View.java:14520) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2843) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1372) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1115) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6023) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858) at android.view.Choreographer.doCallbacks(Choreographer.java:670) at android.view.Choreographer.doFrame(Choreographer.java:606) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5422) ... 

Мы можем видеть, что первые кадры из внутренней логики представления, из родительской ViewGroup, из чего-то, называемого ViewRootImpl, а затем из некоторых обратных вызовов от хореографа и обработчика.

Мы не знаем, что создало эти обратные вызовы, но ближайшая реализация обратного вызова называется ViewRootImpl $ TraversalRunnable, поэтому мы проверим это:

  final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); 

Существует определение, а ниже – экземпляр обратного вызова, который передается хореографу в этом методе:

  void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } } 

Хореограф – это то, что работает на каждом потоке пользовательского интерфейса на Android. Он используется для синхронизации событий с частотой кадров дисплея. Одна из причин его использования заключается в том, чтобы не тратить силы на обработку, вытягивая вещи быстрее, чем показывает дисплей.

Поскольку хореограф использует очередь сообщений потока, мы не могли видеть этот вызов в предыдущих кадрах, потому что вызов не был выполнен, пока Looper не обработал сообщение. Мы можем установить точку останова для этого метода, чтобы узнать, откуда этот вызов:

 "main@4091" prio=5 runnable java.lang.Thread.State: RUNNABLE at android.view.ViewRootImpl.scheduleTraversals(ViewRootImpl.java:1084) at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:913) at android.view.ViewRootImpl.setView(ViewRootImpl.java:526) - locked <0x100a> (a android.view.ViewRootImpl) at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:310) at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3169) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2481) at android.app.ActivityThread.-wrap11(ActivityThread.java:-1) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5422) ... 

Если мы посмотрим на handleLaunchActivity ActivityThread, handleLaunchActivity вызов handleResumeActivity . Прежде чем это вызов performLaunchActivity , и в этом методе есть вызовы Instrumentation#callActivityOnCreate , Activity#performStart и т. Д.

Итак, у нас есть наше доказательство того, что взгляды не привязаны до тех пор, пока они не onResume .