草庐IT

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

coder 2023-11-21 原文

在我的 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 - 当 Activity 通过 "Don' 被终止时不会触发 ActivityLifecycleCallbacks t keep Activity ”的更多相关文章

    1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

      我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

    2. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

      我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

    3. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

      我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

    4. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

      为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

    5. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

      我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

    6. ruby - Highline 询问方法不会使用同一行 - 2

      设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案

    7. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

      在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

    8. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

      我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

    9. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

      我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

    10. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

      我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

    随机推荐