android - 当 Activity 通过 "Don' 被终止时不会触发 ActivityLifecycleCallbacks t keep Activity ”

标签 android android-activity callback activity-lifecycle

在我的 Android 应用程序中,我有两个 Activity :

  • DemoActivity用按钮启动 SearchActivityIntent
  • 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 SearchActivity.onStart触发的回调是 失踪 .

    最佳答案

    简答

    您看到的是 onStart仅当 Activity 在后台一段时间后被带到前台时才从 View 调用。目前不可能从您的 Activity View 中看到较早的 Activity 事件,因为 View 层次结构仍在创建中,并且 View 尚未附加到窗口。

    当一个 Activity 从头开始初始化时, View 层次结构直到 onResume 之后才完全附加。 .这意味着一旦你 View 的 onAttachedToWindow被称为,onStart已经被执行了。如果您退出问题中提到的 Activity ,您仍然应该看到 onPause 的事件。等等。

    通常,如果您通过按主页按钮将 Activity 置于后台,则 Activity 会停止但不会被销毁。如果有足够的系统资源来执行此操作,它将与其 View 层次结构一起保留在内存中。当 Activity 恢复到前台时,它会调用 onStart,而不是从头开始创建它。并从它停止的地方恢复,而不重新创建 View 层次结构。

    “不保留 Activity ”选项确保每个 Activity 在离开前台后立即销毁,确保您的 View onAttachedToWindow总是在 onResume 之后调用因为每次都需要重新创建 View 层次结构。

    你可以做什么

    在不共享更多代码的情况下,不清楚为什么需要在 View 中设置监听器。看来无论如何都需要监听 Activity 的生命周期方法。

    如果监听器仅与 Activity 的生命周期相关联,您可能能够将其从 View 中完全提取出来并放入 Activity 中。

    如果它同时与 View 和 Activity 的生命周期相关联,您可以尝试在 View 的构造函数中注册 Activity 生命周期回调,因为此时上下文已经可用。

    或者,您可以使用 Google Maps 当前拥有的解决方案,例如在 map View 中。它要求 Activity 将所有生命周期方法代理到 View 。如果您的 View 与 Activity 的生命周期紧密相连,这可能很有用。 You can see the documentation here.

    第四个选项是使用 fragment 而不是 View ,因为它有自己的生命周期方法集。就我个人而言,我对 Fragment 不太满意,因为它们的生命周期可能更加复杂。

    更长的答案

    为了解释为什么会发生这种情况,我们需要深入研究 Android 的源代码。我在此处解释的内容特定于此实现,并且可能因制造商的更改而在 SDK 版本之间甚至在 Android 设备之间有所不同。您不应依赖代码中的这些细节。我将使用 Android Studio 附带的 SDK 23 源代码和构建 MTC19T 的 Nexus 6P。

    最容易开始调查的地方是 onAttachedToWindow方法。什么时候真正被调用? Its documentation says它是在 View 的表面创建以进行绘图后调用的,但我们对此并不满意。

    为了找到答案,我们为 View 设置了一个断点,重新启动应用程序以便重新创建 Activity,并调查 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)
          ...
    

    我们可以看到第一帧来自 View 的内部逻辑,来自父 ViewGroup,来自称为 ViewRootImpl 的东西,然后来自 Choreographer 和 Handler 的一些回调。

    我们不确定是什么创建了这些回调,但最接近的回调实现名为 ViewRootImpl$TraversalRunnable,因此我们将检查一下:
        final class TraversalRunnable implements Runnable {
            @Override
            public void run() {
                doTraversal();
            }
        }
        final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    

    有定义,下面是此方法中提供给 Choreographer 的回调实例:
        void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    

    Choreographer 是在 Android 上的每个 UI 线程上运行的东西。它用于将事件与显示器的帧速率同步。使用它的一个原因是为了避免通过比显示器显示的速度更快地绘制东西来避免浪费处理能力。

    由于 Choreographer 使用线程的消息队列,我们​​无法在前面的帧中看到这个调用,因为在 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)
          ...
    

    如果我们查看 ActivityThread 的 handleLaunchActivity , 有拨打 handleResumeActivity 的电话.在此之前是拨打 performLaunchActivity ,在该方法中调用 Instrumentation#callActivityOnCreate , Activity#performStart等等。

    因此,我们有证据表明 View 直到 onResume 之后才被附加。 .

    关于android - 当 Activity 通过 "Don' 被终止时不会触发 ActivityLifecycleCallbacks t keep Activity ”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39896382/

    相关文章:

    android - json 数组中元素的计数

    android - 始终在 Android 中使用 fragment

    java - Android - 在 SDK 中实现回调

    javascript - 在 JavaScript 中将一个函数放入一个空函数中的目的是什么?

    android - 未知动画名称 : objectAnimator. 使用九个旧机器人。仍然出现错误

    android - 找不到 com 的提供商信息。 google.android.gsf.gservices 无法加载 map 。无法联系谷歌服务器

    java - 从 JSON 对象中获取第一个键和值

    java - 在 Android 中从不同 View 调用按钮

    Android:捕获 Activity 的返回

    vue.js - 外部付款后使用 POST 重定向到前端 URL