草庐IT

Activity 和 Fragment 生命周期

沐风雨木 2023-03-28 原文

生命周期,就是一个对象从创建到销毁的过程,每一个对象都有自己的生命周期。同样,Activity 也具有相应的生命周期,在 Activity 的生命周期中分为四种状态,分别是运行状态、暂停状态、停止状态和销毁状态。
Activity 从一种状态转变到另一种状态时会触发一些事件,执行一些回调方法来通知状态的变化,在这里 Activity 类提供了六个核心回调:onCreate()、onStart()、onResume()、onPause()、onStop()onDestroy()。当 Activity 进入新状态时,系统会调用其中每个回调。

一. Activity 运行状态

1. 基本状态

  • 运行状态(Running)Activity 在屏幕的最前端时,它是可见的、有焦点的。能够实现用户交互,如点击、双击、长按事件等。
  • 暂停状态(Paused) 失去焦点因此不可与用户进行交互,但与窗口管理器保持连接状态,状态性和成员变量依然存在。例如,当最上面的 Activity 没有完全覆盖屏幕或者是透明的,被覆盖的 Activity 仍然对用户可见,并且存活(它保留着所有的状态和成员信息并保持与 Activity 管理器的连接)。但当内存不足时,这个暂停状态的 Activity 可能会被杀死。
  • 停止状态(Stopped)Activity 完全不可见时,它就处于停止状态,但仍然保留着当前状态和成员信息。只是不可见的,但当系统内存不足时,这个 Activity 很容易被杀死。
  • 销毁状态(Killed)界面被系统回收后,处于销毁状态,当该界面需要再次显示与用户交互时,需要重新开始并重置。
值得一提的是,当 Activity 处于运行状态时,Android 会尽可能地保持它的运行,
即使出现内存不足的情况,Android 也会先杀死栈底部的 Activity,来确保可见的 Activity 正常运行。

2. 状态转换

当一个 Activity 实例被创建、销毁或者启动另外一个 Activity 时,它在这四种状态之间进行转换,这种转换的发生依赖于用户程序的动作。下图说明了 Activity 在不同状态间转换的时机和条件:

状态转换

二. Activity生命周期

activity 生命周期

从图中可以看出,当 Activity 从启动到关闭时,会依次执行

onCreate() → onStart() → onResume() → onPause() → onStop() → onDestroy()

Activity 执行到 onPause() 方法失去焦点时,重新调用回到前台会执行 onResume() 方法,如果此时进程被杀死 Activity 重新执行时会先执行 onCreate() 方法。当执行到 onStop() 方法 Activity 不可见时,再次回到前台会执行 onRestart() 方法,如果此时进程被杀死 Activity 会重新执行 onCreate() 方法。

1. 常用生命周期

方法名 简介
onCreate 表示 Activity 正在被创建,这也是 Activity 的生命周期的第一个方法。
onRestart 表示 Activity 正在重新启动,此生命周期只有在 onPause 与onStop 都执行过才会被调用
onStart 表示 Activity 正在被启动,即将开始,此时 Activity 已经可见但是还没有出现在前台,还无法交互
onResume 表示 Activity 已经可见并出现在前台可以与用户进行交互
onPause 表示 Activity 正在停止
onStop 表示 Activity 停止并不可见
onDestroy 表示 Activity 即将被销毁,这是 Activity 的最后一个回调

2. Activity 生命周期切换过程

(1)单 Activity
  1. Activity 第一次启动,回调如下:onCreate -> onStart -> onResume
Activity 第一次启动
  1. 打开新 Activity 或按 Home 键:onPause->onStop
打开新 Activity 或按 Home 键
  1. 再次回到 Activity:onRestart->onStart->onResume
再次回到 Activity
  1. 如果新的 ActivityThemeDialog 或者 Translucent(透明)时不会调用 onStop 方法。
新的 Activity 的 Theme 为 Dialog 或者 Translucent
  1. Back 键退出 ActivityonPause->onStop->onDestroy
按 Back 键退出 Activity
注:一般这里只会走 onPause() 和 onStop(),当把 app 杀死时,才会调用 onDestrroy()。
(2)ActivityA 启动 ActivityB

正常情况下:

ActivityA 启动 ActivityB

返回 ActivityA:

返回 ActivityA
(3)Theme 为 Dialog 或 Translucent

ActivityA 启动 ActivityC:

ActivityA 启动 ActivityC

返回 ActivityA:

返回 ActivityA
(4)旋转屏幕横竖屏切换(未指定 configChanges)

Activity 正常运行,此时旋转屏幕:

旋转屏幕

当系统配置被更改时 Activity会被销毁并重新创建,ActivityonPause、onStop、onDestroy 均会被调用,同时由于 Activity 是异常情况下终止并销毁的系统会调用 onSaveInstanceState 方法来保存当前 Activity 的状态。

**注意**:当用户显式关闭 Activity 时,或者在其他情况下调用 `finish()` 时,
系统不会调用 onSaveInstanceState()。
也就是说,系统在“未经你许可”销毁 Activity 时调用 onSaveInstanceState 方法,用于保存 Activity 状态信息。
什么时候调用 onSaveInstanceState 方法:
(1) 当用户按下 HOME 键时。
(2) 切换到其他进程时。
(3) 锁屏时。
(4) 启动新的 Activity 时。
(5) 屏幕方向切换时。
什么时候调用 onRestoreInstanceState 方法:
在 Activity 被系统销毁,又回到该 Activity 的时候。如用户按下 HOME 键又马上返回该 Activity,
这个时候该 Activity 一般不会因为内存不足而被系统回收,故不调用 onRestoreInstanceState 方法。
所以 onSaveInstanceState 与 onRestoreInstanceState 不一定会成对被调用。

之后就是正常的启动流程,当然会有 onRestoreInstancesState 方法在 onStartonResume 之间调用用以恢复 onSaveInstanceState 保存的状态。
onSaveInstancesState 调用时期从 Build.VERSION_CODES.P 开始在 onStop 方法之后调用;对于面向较早平台版本的应用程序,此方法将在onStop() 之前发生,链接

官方解释

接下来用 onSaveInstance()onRestoreInstanceState() 来保存与恢复数据

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("ActivityA 生命周期", "onCreate()");
        if (savedInstanceState != null) {
            Log.d("ActivityA 生命周期", "from onContext:" + savedInstanceState.getString("text"));
        }
    }
    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("text","数据");
        Log.d("ActivityA 生命周期", "onSaveInstanceState()");
        Log.d("ActivityA 生命周期", "数据已保存:"+"数据");
    }

    @Override
    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        Log.d("ActivityA 生命周期", "onRestoreInstanceState()");
        Log.d("ActivityA 生命周期", "from onRestoreInstancesState() 的值为:" + savedInstanceState.getString("text"));
    }

我们可以在 onCreate()onRestoreInstanceState() 方法进行数据的恢复处理,onCreate() 中有一个参数,此参数就是 onSaveInstanceState() 保存的值,在 onCreate() 中需要对该参数进行空判断因为此参数在 onCreate() 正常启动的情况下是为 null 的,至于 onRestoreInstanceState() 这个方法不用进行空值判断因为此方法只要被调用它的值不可能为空,代码效果如下。

数据恢复
(5)资源内存不足导致低优先级 Activity 被杀死

Activity 优先级

  • 前台 Activity 用户正在交互的 Activity,优先级最高;
  • 可见但非前台 Activity
  • 后台 Activity 已经被停止的 Activity,例如执行了 onStop 方法,优先级最低。

当系统内存不足时会按照上面的优先级进行销毁,并通过 onSaveInstanceState()onRestoreInstanceState() 来存储与恢复数据。

防止 Activity 被重新创建
当某项内容被改变时不想停止并重新创建 Activity 可以通过在 AndroidManifest 清单文件中对该 Activity 指定 configChanges 属性来防止重新创建:

比如旋转屏幕时不想重新创建 Activity 可以指定 orientation 这个属性值,
如:android:configChanges="orientation",
如果想指定多个值可以用 “|” 来连接起来如:
android:configChanges="orientation|keyboardHidden"

一些 configChanges 属性:

configChanges 属性

在指定 configChanges 之后 Activity 在该系统配置改变的情况下不会重新创建 Activity 也不会调用 onSaveInstanceState()onRestoreInstanceState() 来保存或恢复数据,取而代之的是使用 onConfigurationChanged() 方法。

    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.d("ActivityA 生命周期", "onConfigurationChanged()");
    }

三. Activity 四种启动模式

  • standard 模式:standard 模式是 Android 的默认启动模式,你不在配置文件中作任何设置,那么这个 Activity 就是 standard 模式,这种模式下,Activity 能够有多个实例,每次启动 Activity,不管任务栈中是否已经有这个 Activity 的实例,系统都会建立一个新的 Activity 实例。

  • SingleTop 模式:SingleTop 模式和 standard 模式很是类似,主要区别就是当一个 SingleTop 模式的 Activity 已经位于任务栈的栈顶,再去启动它时,不会再建立新的实例,若是不位于栈顶,就会建立新的实例。

  • singleTask模式:singleTask 模式的 Activity 在同一个 Task 内只有一个实例,若是 Activity 已经位于栈顶,系统不会建立新的 Activity 实例,和 singleTop 模式同样。但 Activity 已经存在但不位于栈顶时,系统就会把该 Activity 移到栈顶,并把它上面的 Activity 出栈。

  • singleInstance 模式:singleInstance 模式也是单例的,但和 singleTask 不一样,singleTask 只是任务栈内单例,系统里是能够有多个 singleTask Activity 实例的,而 singleInstance Activity 在整个系统里只有一个实例,启动 singleInstance Activity 时,系统会建立一个新的任务栈,而且这个任务栈只有他一个 Activity

四. OnNewIntent()

Activity 被设以 singleTop 模式启动,当需要再次响应此 Activity 启动需求时,会复用栈顶的已有 Activity,还会调用 onNewIntent()。并且,再接受新发送来的 intent(onNewIntent()) 之前,一定会先执行 onPause(),如下图所示:

生命周期

1. onNewIntent() 与启动模式

前提: ActivityA 已经启动过,处于当前应用的 Activity 任务栈中; 
(1)当 ActivityA 的 LaunchMode 为 standard 时:

由于每次启动 ActivityA 都是启动新的实例,和原来启动的没关系,所以不会调用原来 ActivityAonNewIntent()

(2)当 ActivityA 的 LaunchMode 为 SingleTop 时:

如果 ActivityA 在栈顶,且现在要再启动 ActivityA,这时会调用onNewIntent() ,生命周期顺序为:

LaunchMode 为 SingleTop

(3)当 ActivityA 的 LaunchMode 为 singleInstance,singleTask:

如果 ActivityA 已经在任务栈中,再次启动 ActivityA,那么此时会调用 onNewIntent(),生命周期调用顺序为:

LaunchMode 为 singleInstance,singleTask

因此:onNewIntent() 在情况 1 不调用,在情况 23 调用。

更准确的说法是,只对 singleTop(且位于栈顶),singleTasksingleInstance(且已经在任务栈中存在实例)的情况下,再次启动它们时才会调用,即只对 startActivity 有效,对仅仅从后台切换到前台而不再次启动的情形,不会触发 onNewIntent()

五. Fragment 生命周期

FragmentAndroid v3.0 版本开始引入的,随着界面布局的复杂化,处理起来也更加的复杂,引入 Fragment 可以把 Activity 拆分成多个部分。一个 Activity 可以同时组合多个 Fragment,一个 Fragment 也可被多个 Activity 复用。Fragment 可以响应自己的输入事件,并拥有自己的生命周期,但它们的生命周期直接被其所属的 Activity 的生命周期控制。

1. Fragment 状态

Fragment 状态与 Activity 类似,也存在如下 4 种状态:

  • 运行:当前 Fmgment 位于前台,用户可见,可以获得焦点。
  • 暂停:其他 Activity 位于前台,该 Fragment 依然可见,只是不能获得焦点。
  • 停止:该 Fragment 不可见,失去焦点。
  • 销毁:该 Fragment 被完全删除,或该 Fragment 所在的 Activity 被结束。

2. 生命周期状态

Fragment 的生命周期与 Activity 的生命周期十分相似,如下图所示:

Activity 和 Fragment 生命周期对比流程图

可以看到 Fragment 的生命周期和 Activity 很相似,只是多了一下几个方法:onAttach(),onCreateView(),onActivityCreated(),onDestroyView()onDetach()

再来看一下它的常用生命周期:

方法名 简介
onAttach() 当 Fragment 与 Activity 发生关联时调用。
onCreate() 创建 Fragment 时被回调。
onCreateView() 每次创建、绘制该 Fragment 的 View 组件时回调该方法,Fragment 将会显示该方法返回的 View 组件。
onActivityCreated() 当 Fragment 所在的 Activity 被启动完成后回调该方法。
onStart() 启动 Fragment 时被回调,此时 Fragment 可见。
onResume() 恢复 Fragment 时被回调,获取焦点时回调。
onPause() 暂停 Fragment 时被回调,失去焦点时回调。
onStop() 停止 Fragment 时被回调,Fragment 不可见时回调。
onDestroyView() 销毁与 Fragment 有关的视图,但未与 Activity 解除绑定。
onDestroy() 销毁 Fragment 时被回调。
onDetach() 与 onAttach() 相对应,当 Fragment 与 Activity关联被取消时调用。

3. 生命周期调用

Fragment 生命周期调用
(1)创建 Fragment

创建 Fragment

(2)按下 Home 键回到桌面 / 锁屏

按下 Home 键回到桌面 / 锁屏

(3)从桌面回到 Fragment / 解锁

从桌面回到 Fragment / 解锁

(4)按下 Back 键退出

按下 Back 键退出

Activity 和 Fragment 生命周期调用

  • 打开页面
打开页面
  • 按下主屏幕键
按下主屏幕键
  • 重新打开页面
重新打开
  • 按后退键


    按后退键

Fragment 生命周期与 Activity 生命周期的一个关键区别就在于,Fragment 的生命周期方法是由托管 Activity 而不是操作系统调用的。Activity 中生命周期方法都是 protected,而 Fragment 都是 public,也能印证了这一点,因为 Activity 需要调用 Fragment 那些方法并管理它。

Fragment 和 Fragment 生命周期调用
FragmentA 切换到 FragmentB 时变化:

(1)通过 add hide show 方式来切换

  • FragmentA 的生命周期变化为:
    回调 onHiddenChanged() 方法;
  • FragmentB 的生命周期变化为:
    onCreate() --> onCreateView() --> onActivityCreated() --> onStart() --> onResume()
  • FragmentB 再次返回到 FragmentA:
    不走任何生命周期方法但是回调 onHiddenChanged()方法
当以这种方式进行 FragmentA 与 FragmentB 的切换时,
Fragment 隐藏的时候并不走 onDestroyView,
所有的显示也不会走 onCreateView 方法,所有的 view 都会保存在内存。

(2)使用 replace 的方法进行切换时

  • 载入FragmentA 时:
    FragmentA 的生命周期:onCreate() --> onCreateView() --> onActivityCreated() --> onStart() --> onResume()

  • 切换到FragmentB 时:
    FragmentA 的生命周期:onPause() --> onStop() --> onDestroyView() --> onDestroy() --> onDetach()
    FragmentB 的生命周期:onCreate() --> onCreateView() --> onActivityCreated() --> onStart() --> onResume()
    如下图:

    切换到FragmentB 时

  • FragmentB 切换回 FragmentA 时:
    FragmentB 的生命周期:onPause() --> onStop() --> onDestroyView() --> onDestroy() --> onDetach()
    FragmentA 的生命周期:onCreate() --> onCreateView() --> onActivityCreated() --> onStart() --> onResume()

注:通过 replace 方法进行替换的时,Fragment 都是进行了销毁,
重建的过程,相当于走了一整套的生命周期

(3)使用 ViewPager 进行切换时
当使用 ViewPagerFragment 进行切换时,Fragment 会进行预加载操作。

  • 所有的 Fragment 都会提前初始--->预加载;
  • 初始化时 Fragment 们的生命周期:
    FragmentA 的生命周期:onCreate() --> onCreateView() --> onActivityCreated() --> onStart() --> onResume()
    FragmentB 的生命周期:同上
  • FragmentA 切换到 FragmentB 的生命周期:
    FragmentA :走 setUserVisVleHint() 方法;
    FragmentB同上

切回去也是一样的。

有关Activity 和 Fragment 生命周期的更多相关文章

  1. ruby - 在 Ruby 中查找周期和范围集差异的有效方法 - 2

    我在Ruby中有很多时间范围:period=Time.parse('8:00am')..Time.parse('8:00pm')incidents=[Time.parse('7:00am')..Time.parse('9:00am'),Time.parse('1:00pm')..Time.parse('3:00pm'),Time.parse('1:30pm')..Time.parse('3:30pm'),Time.parse('7:00pm')..Time.parse('9:00pm'),]我正试图在这段时间内获得一系列无事件block。对于以上内容:[Time.parse('9:00

  2. ruby-on-rails - 有没有一种简单的方法可以在 Passenger 的请求周期之外运行垃圾收集? - 2

    unicorn有OobGC可用于在一定数量的请求后运行GC.start的机架中间件。PhusionPassenger中有类似的东西吗? 最佳答案 PhusionPassenger4正式引入了带外垃圾回收机制。它比Unicorn更灵活,允许任意工作,而不仅仅是垃圾收集。http://blog.phusion.nl/2013/01/22/phusion-passenger-4-technology-preview-out-of-band-work/ 关于ruby-on-rails-有没有一种

  3. ruby-on-rails - rails 周期性任务 - 2

    我有一个ruby​​onrails应用程序,我试图在其中找到每隔几秒运行一些代码的方法。我发现了很多使用cron或类似cron的实现的信息和想法,但这些只是准确到分钟,并且/或需要外部工具。我想每15秒左右启动一次任务,并且我希望它完全独立于应用程序中(如果应用程序停止,任务也停止,并且没有外部设置)。这用于缓存数据的后台生成。每隔几秒,任务就会收集一些数据,然后将其存储在缓存中,供所有客户端请求使用。该任务非常慢,因此需要在后台运行并且不阻塞客户端请求。我是ruby​​的新手,但有很强的perl背景,我解决这个问题的方法是创建一个间隔计时器和处理程序,它fork、运行代码,然后在完成

  4. android 多屏幕显示activity,副屏,无线投屏 - 2

    目录1.首先,需要一个副屏1.1可以通过代码的形式自己创建VirtualDispaly,创建副屏。1.2或者,在手机的开发者模式中直接开启模拟副屏,也是可以的。2.0怎么利用这个副屏幕?2.1 用作presentation演示ppt:2.2克隆主屏幕的内容,就是主屏幕显示什么,副屏显示同样的内容,镜像模式。2.3 将一个activity从第二个屏幕上启动,作为一个独立的屏幕首先说明一下这个多屏幕的概念,这里不是指分屏显示。分屏显示:是一个屏幕分出多个窗口,分别显示不同app.多屏支持:是一个设备有多个屏幕,怎么让不同的屏幕显示不同的app,或者是一个app同时用两个屏幕来显示不同的页面内容。多

  5. ruby-on-rails - Rails 应用程序的生命周期 - 2

    我正在尝试了解Rails应用程序的生命周期。application_controller.rb什么时候运行?是每次更改时只执行一次,还是每次请求时都执行一次?我想了解以下文件:config/environments/*.rb(开发、生产或测试,取决于当前模式)boot.rb环境.rb路线.rb我问这个的原因之一是,我想知道放在哪里比较好初始化代码自定义配置数据编辑:@Gdeglin的回答很好,但我实际上很想知道这些文件中的每一个何时运行。 最佳答案 应用程序Controller.rbApplicationController是所有C

  6. ruby - 在 Sinatra(Ruby) 中,我应该如何创建在应用程序生命周期中只赋值一次的全局变量? - 2

    在Sinatra中,我无法创建在应用程序生命周期中仅分配一次值的全局变量。我错过了什么吗?我的简化代码如下所示:require'rubygems'ifRUBY_VERSION这导致nil2在终端和,2在浏览器中。如果我尝试将@a=1放入initialize方法中,我会在WebApp.run!中遇到错误线。我觉得我错过了一些东西,因为如果我不能有全局变量,那么我如何在应用程序实例化期间加载大数据?beforedo似乎每次有来自客户端的请求时都会被调用。 最佳答案 classWebApp请注意,如果您使用Shotgun或其他在每次请求时

  7. ruby-on-rails - Rails 4.0 expire_fragment/缓存过期不起作用 - 2

    我一直在尝试使用Rails的缓存功能,但我无法使某些缓存片段过期,尽管它们似乎已过期。使用Rails教程站点中指出的“俄罗斯娃娃缓存”,我正在使用此配置我在release_controller.rbController中使外部缓存过期,我在其中使用expire_fragment("all_available_releases")使片段过期。我在更新、删除或添加条目的Controller的每个方法中都使用它。这是WEBrick的日志,尽管过期片段已在其中注册,但5行后过期片段被读取并使用,而这是不应该的。这个例子是在destroy调用之后。ProcessingbyReleasesCont

  8. ruby-on-rails - 如何从 Rails Observer/Model 调用 expire_fragment? - 2

    我几乎什么都试过了,但似乎无法使用来自模型的expire_fragment?我知道你不应该这样做非MVC,但肯定有很多方法可以做到。我在lib/cache_helper.rb中用我所有的过期助手创建了一个模块,在每个中只是一堆expire_fragment调用。我有我所有的/app/sweepers下的缓存清扫器设置,并有一个“includeCacheHelper”在我的应用程序Controller中,因此在通过Controller调用时应用程序工作正常。然后事情是我有一些外部守护进程,尤其是一些循环的cron任务调用一个调用特定任务的rake任务方法。此方法进行一些处理并将条目输入到

  9. javascript - 使用 ajax 调用 react 组件 - 生命周期 - 2

    所以我有一个使用React和Ajax调用的有趣案例。在上下文中,我有一个带有3个选项卡的Accordion。初始化Accordionreact组件后,我首先打开第一个选项卡,其余选项卡关闭。每个选项卡的主体中都有所谓的DictionaryCall组件,如下所示:returnclassDictionaryCallextendsReact.Component{constructor(props){super();this.state={word:'',data:[],error:false,nodata:false,initialLoaded:props.load}}componentDi

  10. javascript - 为什么 setInterval() 周期每次都变快? - 2

    我正在Javascript上构建自定义slider,我希望每次用户单击slider的div时,slider都应停止X秒。我的代码是:$(document).ready(function(){varciclo;varindex_slide=1;functionstartSlidercicle(){ciclo=setInterval(function(){//Slidercodegoeshere},3000);}//HereIstarttheslideranimationstartSlidercicle();//Whentheuserclicksonadivcalled'slide',st

随机推荐