【问题标题】:Simplest Android Activity Lifecycle最简单的 Android Activity 生命周期
【发布时间】:2011-07-29 03:23:19
【问题描述】:

我注意到 Android Developers Activity 部分在我启动应用程序后已经更新,但我仍然不清楚最简单的 Activity Lifecycle 是什么。

据我所知:

onCreate、onResume 和 onPause 是必不可少的。

活动可能在 onPause 之后的任何时间被删除,所以我应该将我的整个应用程序状态保存到文件 onPause 而不是依赖 onStop 或 onDestroy。此外,onSaveInstanceState 不会在每次 onPause 之前被调用,因此不值得使用。

与其尝试编写大量代码来处理所有场景,不如在其 onPause 结束时销毁 Activity?

生命周期将在它处于活动状态之前为 onCreate 和 onResume,然后在它变为非活动状态时为 onPause。不需要其他方法。

我会使用 onCreate 调用 setContentView 并设置视图侦听器,但其他所有内容都会放在 onResume 中,包括从文件加载恢复的状态? 如前所述,onPause 会将状态保存到文件并销毁活动。

据我所知,这样做的唯一缺点可能是当屏幕上出现弹出窗口时,活动被删除并且必须在弹出窗口关闭时重新创建,这意味着活动将在后面不可见弹出窗口(虽然我没有测试过)

重新启动活动可能需要更长的时间,但由于系统可能会在没有任何通知的情况下删除活动,因此您必须保存整个状态。

有什么想法吗?

更新: 我想我在想的是“首页”活动称为游戏活动的地方。当玩家点击“播放”时,首页活动将调用游戏活动

游戏活动将在 onCreate 中设置其视图和侦听器等,并在 onResume 中加载包含游戏状态的文件,或者如果不存在文件则启动新游戏。

游戏的onPause,它将游戏状态写入文件,然后无论游戏活动发生什么(什么都没有,或者被停止/销毁,或者其他什么),onResume 方法总是会再次从文件。

我就是这么想的,如果这有意义吗?

更新2: 我设计了一个简单的解决方案,如果有人感兴趣,我已经在下面的答案中记录了它!

它不支持 Android 活动生命周期“暂停”和“停止”状态。一旦它不再显示,它就会自行杀死并必须手动重新启动,但它确实会从你停止的地方继续!

【问题讨论】:

  • 没有比Activities 文章中更简单的 Activity 生命周期描述了。
  • 是的,Activities 文章确实清楚地解释了整个生命周期,但我试图确定制作一个能够处理整个生命周期的完整 App 所需的最少方法数。例如如果 onStop 可能永远不会被调用,为什么要实现它?
  • 在 onPause() 之后可能会杀死 Activity 是否仍然是真的?请参阅:developer.android.com/training/basics/activity-lifecycle/… "...一旦您的活动停止,如果需要恢复系统内存,系统可能会销毁实例。..."
  • @Waterbear,在developer.android.com/reference/android/app/Activity.html中说onPause后仍然可以被系统销毁
  • 这不是其他人已经回答的直接答案,但我邀请您查看LogLifeCycle 以了解您的 Android 应用程序在生命周期方面发生了什么。

标签: android android-activity lifecycle


【解决方案1】:

onCreate() 显然首先。然后您的活动可以进入 onPause()。从那里它可以是 onResume() 或 onDestroy()。这是我所知道的生命周期中最简单的路径。

我有一个没有 onPause() 方法的活动。通过一系列奇怪的事件,我在 DDMS 中注意到我的应用程序不可见,但它仍在向 AdMob 请求新鲜广告 :) 不错的电池吸盘。这已经解决了,但提醒了我简单的事情是多么重要。

【讨论】:

  • 大概你的 onPause 和 onResume 在你的第一段中绕错了?
  • 在 onDestroy 中需要发生什么吗?我认为没有任何清理工作要做?
  • 好久没看安卓的文档了,既然从来没有onPause(),为什么还要onResume()呢?不要流鼻涕......如果我错了,我很想知道为什么。 Google Analytics 文档说要在 onDestory() 中实现 .stop() 调用。我猜你可以使用 onDestory() 来杀死任何后台线程?
  • 很明显,通过上面的流程图,我错了。对我来说没有意义,但我想我有一些阅读要做;)
  • onCreate 应该调用 setContentView 并设置所有视图侦听器并处理在其 bundle 参数中传递给它的数据。 onResume 可用于其他所有内容,尽管我不清楚 onResume 中的所有内容是否都可以进入 onCreate
【解决方案2】:

Android 系统正在处理生命周期:即实例化活动和调用生命周期方法。所以我不知道你所说的“破坏活动”是什么意思。作为开发者,你没有这样的能力。

其次,活动生命周期流程有时会令人困惑(我知道我一开始就遇到了困难)。因此,只需实现所有生命周期方法并将日志语句放入其中。然后尝试所有现实生活中的用例(包括在应用程序使用期间接收呼叫)以查看生命周期方法是如何被调用的。

【讨论】:

  • 一个activity可以通过调用finish()来销毁
  • 我在我的活动方法中添加了很多日志记录,这就是我将 onCreate/onResume/onPause 想法作为最简单的生命周期的方法。 onSaveInstanceState 似乎被非常零星地调用,所以没有真正的用处。
  • 我做了很多研究,得出了我在下面添加的答案。在我看来,唯一保证为正在运行的活动调用的事件是 onPause。生命周期表明在某些情况下可能不会调用 onSaveInstanceState、onStop 和 onDestroy,因此我不想依赖它们
  • @FrinkTheBrave 检查活动文档的更新。您会发现 Google 已经升级了文档,并且它读取 onStart() 和 onStop() 现在是当 Activity 不可见但正在启动或关闭时。 onResume() 和 onPause() 指的是活动何时可见或即将变得不可见。
【解决方案3】:

你在找这个吗?

为了进一步回答您的问题,是的,从上图中您可以清楚地看到,“最简单”(即最少的方法调用次数)生命周期确实是 onCreate(); onStart(); onResume(); onPause();

您还应该了解onSaveInstanceState()onRetainNonConfigurationInstance()。这些是不是生命周期方法。

所有这些方法都有很好的文档记录。请仔细阅读本文档。

为了进一步说明问题,这里有几个现实场景:

  1. 活动正在运行,其他活动在它之上,onPause 被调用。系统内存不足,调用onSaveInstanceState,终止活动。用户按下几次,必须重新实例化活动(最好使用保存在onSaveInstanceState 中的数据)。
  2. 活动正在运行,用户按回。此时调用onPause->onDestroy,而不调用onSaveInstanceState

您应该了解onPauseonSaveInstanceState 之间的本质区别。前者总是被调用,而后者仅在活动instance可能在未来被重新实例化时被调用。按照这种思路,您的用户会期待两件事:

  1. 当他们离开您的 Activity 并稍后返回时,他们希望它在与离开它完全相同的实例中(这可以使用 onSaveInstanceState 来实现)。他们不会期望如果他们退出您的活动。但是:
  2. 他们希望他们输入的数据会被持久化(这将在onPause 中完成)。例如,如果他们开始撰写消息,那么即使他们退出了活动,他们也会希望在下次回来时将其视为草稿。

您应该了解应该如何使用这些方法,以便获得用户的期望。您实际如何使用它们取决于您、您的需求以及您应用的性质。

【讨论】:

  • 没有。我追求的是最简单的生命周期。例如,onStart 和 on Restart 用于何时可能永远不会为活动调用 onStop。
  • 嗯,Activity 文档页面上有一个关于“活动生命周期”的非常完整的部分。
  • 是的,我知道。这显示了所有调用的方法,我追求的是最简单(即最小)的生命周期
  • 感谢 Felix,这些真实场景和您的解释非常有帮助,请打勾!
  • 最简单的生命周期其实就是 onResume > onPause 。但不推荐,你应该使用 FULL 循环
【解决方案4】:

答案就像生命周期一样简单。仅当您需要处理其中的内容时才覆盖回调。

Android 将始终按照应有的方式调用每个回调,除非在某些情况下。

仅仅因为某些回调不能保证被调用并不意味着它们是无用的。只是不要尝试在此类回调方法中处理明智的事情。

【讨论】:

    【解决方案5】:

    我想我找到了我要找的东西! (我 1,波诺 0)

    这适用于单个“游戏”活动,该活动使用最少的方法来控制处理游戏的“游戏线程”线程。它使用一个额外的“GameData”类来保存需要在激活之间持久化的数据。

    如果应用失去焦点(即来电或用户点击返回等),Game 会将 GameData 保存到文件并退出。要恢复,只需重新启动应用程序,它就会回到您上次中断的地方。

    布局文件'game.xml'是一个覆盖整个屏幕的SurfaceView

    Game.java:

    1. onCreate 为 SurfaceView 设置 SurfaceHolder 并创建 GameThread
    2. surfaceChanged 调用 GameThread.startThread 来启动 GameThread,如果尚未启动,则传递屏幕尺寸
    3. onPause 调用 GameThread.endThread 结束 GameThread 并结束此活动
    4. onTouch 将触摸事件传递给 GameThread.doTouch 方法

    GameThread.java:

    1. startThread 设置本地保存的数据,从文件中加载 GameData 并启动线程
    2. run 是一个标准的游戏循环,为每一帧调用相当标准的 updatePhysics 和 drawScreen 例程。游戏循环完成后,它将 GameData 保存到文件中
    3. endThread 在运行中停止游戏循环并等待它完成
    4. doTouch 动作触摸来自 Game Activity 的事件

    它似乎工作。这确实意味着必须在一个线程中处理不同的屏幕(例如标题屏幕、选项、播放和游戏结束),但这并不是世界末日,恕我直言。

    也许我会在 CodeReview 上发布代码(如果我这样做了,我会更新它),看看是否有人有任何 cmets。同时,我最好开始编写下一个愤怒的小鸟!

    【讨论】:

    • 每秒保存60次有没有可能导致内存死的更快?
    【解决方案6】:

    我个人认为开发人员应该将工作分成不同的活动状态。在这种情况下,必须保留工作的顺序,我认为这更重要,这就是为什么因为 Android 无法在单个线程中处理长时间的 UI 处理并且它给出了 Android 有“太多工作要做”的错误这个引用有时可能会导致崩溃所以我们应该防止在一个部分中编写整个代码。代码将被写入不同的函数或类,我们可以根据需要派生这些函数。谢谢。

    【讨论】:

      【解决方案7】:

      我从onCreate 开始研究活动生命周期以及开始任何活动时会发生什么的完整过程。这是一个可以帮助您的 UML 图。

      点击此链接查看High Resolution 版本。

      【讨论】:

      • +1 用于详细的 UML sequence diagram 方法。高分辨率版本的链接无效。知道在哪里可以找到它吗?谢谢。
      • @WebViewer,我认为链接已过期。这是更新的链接i.stack.imgur.com/ImckV.png
      【解决方案8】:

      有 7 种方法可以管理 Android 应用程序中的 Activity 生命周期:

      1. onCreate()
      2. onStart()
      3. onResume()
      4. onRestart()
      5. onPause()
      6. onStop()
      7. onDestroy()

      我在my blog上写了更深入的关于这些方法的文章

      【讨论】:

      • 感谢您的关注,但正如我在这篇文章中多次声明的那样,这是我所追求的最简单的生活方式。我需要为典型应用程序编码的最少状态数是多少?编码一个可能不会被调用的状态有什么意义?然后您必须将更多代码放入其他状态以处理可能或可能未调用状态的事实
      【解决方案9】:

      根据@Felix 提供的答案,成熟、功能齐全且功能齐全的 Android 应用程序已转换(状态 = 时间函数)。

      但是,如果您只想创建一个甚至没有 GUI 的应用程序怎么办。而你想要最简单的生命周期,那么我在下面有答案。

      这个答案可能你会觉得有趣。 Android系统在阅读AndroidManifest.xml后就知道哪个是入口点。无论哪个活动都有这个

      <activity android:name=".MainActivity">
                  <intent-filter>
                      <action android:name="android.intent.action.MAIN" />
      
                      <category android:name="android.intent.category.LAUNCHER" />
                  </intent-filter>
              </activity>
      

      将是系统开始的起点或入口点。在此之后,您的应用程序进程获得了可以自行启动的内存。

      您的主进程创建是Android系统的任务。当一个应用程序组件启动并且该应用程序没有任何其他组件在运行时,Android 系统会启动一个新的 Linux 进程。进程调用onCreate()后,会创建主线程。这就是为什么必须调用onCreate()的原因,因为主线程然后被初始化,主线程是你未来所有动作的触发器。

      在 onCreate() 上,你得到了你的主线程。在那之后,你想调用什么回调完全取决于你。没有规定必须在 onCreate() 之后调用 onStart()。

      只有一个生命周期方法 onCreate() 保证是你组件的生命周期返回。

      此外,要了解每个生命周期方法在其中执行的操作,请输入以下代码。

      //add the log line
              Log.i(this.getClass().getCanonicalName(), "##############");
              int count = 0;
              for (StackTraceElement stackTraceElement:  Thread.currentThread().getStackTrace()) {
                  count++;
                  Log.i(this.getClass().getCanonicalName(), "\""+Thread.currentThread().getStackTrace()[2].getMethodName()+"\" "+count+" "+stackTraceElement.getMethodName());
              }; //end of log line
      

      因此,请始终添加上述相同代码以在 log cat 控制台中查看日志。

      当您的应用启动 GUI 时,它会调用 onStart()。如果在回调中不调用 super() 方法,android 系统会出现断管错误,无法做进一步的处理和调试。

      但是,您可以在每个回调方法中实现自己的逻辑。例如,您可以在 onStart() 上对两个数字求和。

      所以,所有的android回调方法都是部分实现的方法。 它们不是纯粹的抽象方法,因为您应该在其中调用 super() 方法

      附图说明:应用程序主进程调用onCreate(),它将自己的职责委托给主线程(UI线程),主线程调用这16个其他回调。

      此外,如果您想在特定时间查看所有方法,请将其放在代码下方。

      //to see all the methods (including super methods) at this time, you call use the java reflection as below
          Log.i(this.getClass().getCanonicalName(), "##############");
          int count1 = 0;
          for (Method method:  this.getClass().getMethods()) {
              count1++;
              count++;
              Log.i(this.getClass().getCanonicalName(), count1+" "+method);
          }; //end of log line
      
          //At another time, after you add some other methods or android using GC removes some methods, you see your changed state (Bundle Snapshot) of your class
          //Because your bundle (i.e, the state of your class/activity) is the function of time.
      

      在我的场景中,从上面的代码 sn-p 中,我看到它调用了 375 个方法。

      注意:如果您在使用上面的代码 sn-p 打印所有方法之前在 onCreate() 中添加了另一个方法,那么您也会看到该方法。这意味着,您的班级状态(即快照)取决于您,您一次又一次地做什么,您会不断更新您的快照。

      【讨论】:

        猜你喜欢
        • 2013-04-06
        • 2013-07-10
        • 1970-01-01
        • 1970-01-01
        • 2019-02-02
        • 1970-01-01
        • 1970-01-01
        • 2015-05-13
        • 1970-01-01
        相关资源
        最近更新 更多