Activity生命周期及启动模式——代码实践篇

一、概述

本篇主要简单介绍一下activity的生命周期和启动模式。同时,我们会用代码实践在不同的启动模式下,生命周期的具体执行方式。


二、Activity的生命周期

首先,让我们看一下关于Activity生命周期的一张经典图片:


在讲Activity生命周期之前,我们先需要理解三种活动的状态。

前台活动:当前activity正在前台与用户交互。

可见活动:当前activity对用户是可见的,但是很可能不在与用户进行交互。

后台活动:activity对用户不可见,数据在后台被保存起来。


从图上可以看出,生命周期中,onCreate,onStart,onResume,onPause,onStop,onDestroy这六个方法最为重要,而这六个方法是两两对应的。

onCreate():activity被创建时调用,应该在这个方法中完成布局加载,时间绑定等初始化操作。

onStart():activity可见时调用。

onResume():activity在前台时调用。

onPause():activity不是在前台是调用。

onStop():activity已经不可见时调用。

onDestroy():activity正在停止或即将被销毁时调用。


下面,我们通过代码实践中打印的日志来了解android生命周期。

1、activity A 中启动 activity B

2567-2567/example.com.activitydemo I/Lifecycle: A onPause
2567-2567/example.com.activitydemo I/Lifecycle: B onCreate
2567-2567/example.com.activitydemo I/Lifecycle: B onStart
2567-2567/example.com.activitydemo I/Lifecycle: B onResume
2567-2567/example.com.activitydemo I/Lifecycle: A onStop

从日志中我们可以看出,A调用了onPause 和onStop进入了后台状态,而B被创建并且进入了前台状态。

那么这里A和B的生命周期有没有固定的顺序呢?在Android官方文档中,有写出,只有在老的activity执行onPause完成后,才能执行新activity的onResume。然而从sdk23的源码中,我们可以看到其实是在老的activity执行onPause后,新的activity才被启动,所以,若是activity的启动机制没有大改,老activity的onPause将会最先执行。而老activity的onStop则没有固定的顺序。

2、activity B 返回 activityA

2567-2567/example.com.activitydemo I/Lifecycle: B onPause
2567-2567/example.com.activitydemo I/Lifecycle: A onStart
2567-2567/example.com.activitydemo I/Lifecycle: A onResume
2567-2567/example.com.activitydemo I/Lifecycle: B onStop
2567-2567/example.com.activitydemo I/Lifecycle: B onDestroy

从日志中我们可以看出,B被销毁了,而A不需要重新创建,然后进入了前台状态。

顺序和之前同理,老activity的onPause总被最先执行。

3、activity A 按home键

2567-2567/example.com.activitydemo I/Lifecycle: A onPause
2567-2567/example.com.activitydemo I/Lifecycle: A onStop

正常情况下,按home键之后,activity会进入后台状态。那么有没有异常情况呢?那肯定是有的。当系统内存不足时,会回收优先级较低的activity,优先级基本按照 前台activity>可见activity>后台activity。因此,进入后台的activity很可能被回收。activity被回收时,会调用 onSaveInstanceState() 用于保存现场,开发者可以重写这个函数,用于activity被回收后保存需要留存的变量,保存现场后,会调用 onDestroy()销毁activity。 当activity再次被启动时,会先调用onCreate,然后调用onRestoreInstanceState()函数来恢复现场。


三、activity 启动模式

activity有四种启动模式:standard, singleTop,singleTask,singleInstance。

1、standard

标准启动模式,系统默认的activity启动模式,上个章节介绍的生命周期,均是以standard模式启动。

2、singleTop

栈顶复用模式。使用这种模式启动新的activity,如果将要启动的activity在栈顶的话,将不会被重新创建实例。如果要启动的不在栈顶的,那么会新建一个实例,其过程和standard模式相同。

我们来看下已经在栈顶的E,以singleTop模式再次启动E时,会发生什么。

2567-2567/example.com.activitydemo I/Lifecycle: E onPause
2567-2567/example.com.activitydemo I/Lifecycle: E onNewIntent
2567-2567/example.com.activitydemo I/Lifecycle: E onResume
E调用了onPause退出前台,然后调用onNewIntent,再调用onResume回到前台。

3、singleTask

栈内复用模式。类似于单例模式,在这种启动模式下,activity只要在一个栈中存在,那么就不会重新创建实例,并且在重新启动时,会将activity调到栈顶,注意,这里的调到栈顶指的是将被重新启动的activity上面的所有activity全部出栈。

我们先来看下,在栈内为 AFBC其中F为栈内复用模式启动,当我们再次启动F时,生命周期会发生什么

3071-3071/example.com.activitydemo I/Lifecycle: B onDestroy
3071-3071/example.com.activitydemo I/Lifecycle: C onPause
3071-3071/example.com.activitydemo I/Lifecycle: F onNewIntent
3071-3071/example.com.activitydemo I/Lifecycle: F onStart
3071-3071/example.com.activitydemo I/Lifecycle: F onResume
3071-3071/example.com.activitydemo I/Lifecycle: C onStop
3071-3071/example.com.activitydemo I/Lifecycle: C onDestroy

这里可以看到,和我们上面说的情况相同,BC被销毁出栈,使得F恢复到栈顶,然后F并不会重新创建,而是调用了onNewIntant函数。

当F在已经在栈顶,并且在前台时,再次调用F,与singleTop的生命周期调用是相同的。

这里还要再提到一种情况,那么当有多个activity栈的时候,重新启动一个singleTask模式的activity会出现什么情况。

现在我们模拟一种情况: 后台栈为 AFB,前台栈为DC, 其中F为栈内复用模式启动,C为当前前台活动,如下图


此时我们以singleTask模式启动F会发生什么情况呢

24481-24481/example.com.activitydemo I/Lifecycle: C onPause
24481-24481/example.com.activitydemo I/Lifecycle: B onDestroy
24481-24481/example.com.activitydemo I/Lifecycle: F onNewIntent
24481-24481/example.com.activitydemo I/Lifecycle: F onStart
24481-24481/example.com.activitydemo I/Lifecycle: F onResume
24481-24481/example.com.activitydemo I/Lifecycle: C onStop

从日志可以看出, C并没有被销毁,而是转到了后台,B被销毁退出了栈。F没有被重新创建,而是被调用了onNewIntent函数,并且转到了前台。此时的活动栈情况如下图:


想看详细栈的情况的话,可用通过命令 adb shell dumpsys activity 来查看,这里就不贴出来了。

4、singleInstance

单实例启动模式。这个和singleTask有些类似,简单来说,是singleTask的加强版,即以singleInstance启动模式启动的activiy,在所有栈中只能存在一个实例,且该实例单独占有一个独立的栈。在该实例的活动栈中启动其他活动,也会转到其他栈中创建。

我们构造一个G为单实例模式启动,A、B为标准模式启动,示意过程如下图。


5、使用方式

在代码中,如需使用这四种启动模式, 需要在AndroidManifest.xml中指定,如下:

        <activity android:name=".D"
            android:taskAffinity="com.example.activitydemo1"/>
        <activity android:name=".E"
            android:launchMode="singleTop"/>
        <activity android:name=".F"
            android:launchMode="singleTask"/>
        <activity android:name=".G"
            android:launchMode="singleInstance"/>

launchMode是指定activity的启动模式,taskAffinity是指定activity的启动栈。


四、Activity 启动模式的 Flags

Activity的启动模式,除了上面说的在xml文件中指定外,还能可以在启动时指定intent中的flags来启动。

1、FLAG_ACTIVITY_NEW_TASK

这个关键字需要配合 taskAffinity属性去使用。当你为activity指定一个任务栈时,需要通过该Flag来启动activity,否则直接启动的话,会使得activity无法在新的任务栈中启动。这里还有一个需要注意的地方,在taskAffinity所指定的任务栈中,已经存在将要启动的activity实例,那么使用FLAG_ACTIVITY_NEW_TASK启动的话,将会使得startActivity失效,生命周期不会被调用。

在开发中时,我们都是通过Activity的上下文来启动另一个Activity。那么如果需要使用非Activity的上下文来启动一个activity行不行呢?当然可以,但是不是直接启动,直接启动的话会报以下异常:

android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag.

这个异常说明的很明显,需要用FLAG_ACTIVITY_NEW_TASK这个flag来启动activity。是的,这个就是FLAG_ACTIVITY_NEW_TASK的另一个作用,当你使用非activity的上下文启动activity时,会因为没有对应的任务栈,而导致启动失败,FLAG_ACTIVITY_NEW_TASK关键字便是创建一个新的任务栈,来存放启动的activity。那么会有人问,那应用启动的第一个activity不是也没有对应的任务栈吗?是的,launch的activity的启动模式实际上是singleTask,这个后面的博客,会对这里进行分析。

2、FLAG_ACTIVITY_SINGLE_TOP

这个关键字非常简单,和singleTop启动模式一模一样,与在xml中指定launchMode为singleTop没有区别,这里就不详述了。

3、FLAG_ACTIVITY_CLEAR_TOP

使用该flag启动activity时,会查看在启动activity的任务栈中是否有相同的activity实例,有的话,则将该activity实例以及其之上的所有activity全部出栈,在创建一个新的activity实例压入栈中。示意图如下


如果有多个B的实例,那么此时以FLAG_ACTIVITY_CLEAR_TOP启动B,会出现什么情况呢?

在有多个实例的情况下,只会出栈到最靠近栈顶的实例,如下图:


那么有多个任务栈时,FLAG_ACTIVITY_CLEAR_TOP会不会像singleTask一样,将后台任务栈切到前台呢?

其实并不会,该flag只关注与activity将要进入的栈,如下图:



五、最后说几句

其实 activity的启动,包括其生命周期,这里只是一个概述,在具体的开发过程中,会遇到很多厂商修改系统,导致在一些操作中生命周期的运行和预想的并不相同。

在写这篇文章之前,看了许多书籍和其他博客,发现大家写的不尽相同。此篇博客的结论,是使用小米5、小米6、MX6等主流手机,在 Android 6.0 7.0 7.1 等主流版本上测试得到的结果,然后结合 SDK23的源码得出的结论。若在其他手机上有出入。可留言说明。

为了这篇博客,写了一个小的activity启动程序,用于测试结果。

    




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值