android Activity 四种启动模式剖析

    这是正式写的第一篇技术博客,之前很多东西都是放在自己的有道云笔记里,一来感觉很多时候写东西只是作为自己思路梳理之用,并不系统完备。二来很多东西都是循序渐进,理解上可能存在偏差,担心把别人带沟里去。但是慢慢发现,技术的东西还是应该拿出来跟大家分享的。不过应该里面还是有很多坑的,还请大家斧正。


    android四种启动模式分别为,Standard标准模式, singleTop栈顶复用模式, singleTask栈内复用模式, singleInstance栈内复用模式。

    Activity具体存放在不同的栈中,先入后出,当界面跳转时,系统会对Activity的栈进行调度。包括调度同一个任务栈内的不同Activity实例和前台任务栈与后台任务栈的切换。


    四种启动模式的简单介绍:

     1.标准模式,对Activity没有指定具体启动模式时,其默认为标准模式,当启动新的Activity时,不管当前任务栈中是否已经具有该Activity的实例,系统都会创建全新的Activity实例押入栈中。

    2.singleTop栈顶复用模式,当启动新的Activity时如果当前任务栈的栈顶已经存在该实例,那么直接复用该实例对象,不再创建新的Activity,如果栈顶不是该Activity,那么重新入栈该Activity。

3.singleTask栈内复用模式。栈内复用模式和栈顶模式类似,不同的是,栈顶模式只是判断是否在栈顶具有相同的已经存在的Activity实例,而栈内复用模式则判断整个任务栈,如果存在相同实例,则将该实例之上的所有的Activity全都出栈,使该Activity置顶。

4.singleInstance单例模式。与singleTask类似,只是singleInstance模式限定某个Activity只能单独存在于某个特定的任务栈当中,由于其具有栈内复用特性,所以后续的请求均不会再请求新的Activity实例入栈。该栈内始终只有一个Activity实例存在。

启动模式指定方式:

1.在配置文件中显示调用,设置launchMode = “singleTop”  “singleTask”等。

2.在代码中为intent直接设定flag,但是在flag中不能设定singleInstance。



Activity启动过程总结,流程图--------------


源码分析:

      1.startActivity后,都会调用startActivityForResult(),那么系统是如何根据 启动模式的不同操纵任务栈以及相应实例的呢,我们一步步来看,首先startActivityResult()如下图所示:


Instrumentation是ActivityThread的一个负责管理,统计的工具类,在这里是调用了Instrumentation的execStartActivity()方法来进行启动。之后线程挂起,在启动完成返回后,mMainThread.sendActivityResult()对result进行派发.下面的mParent是相对于原来的ActivityGroup来讲的,fragment诞生之后被弃用,暂且不表。

2.接下类是Instrumentation的execStartActivity()方法:

由方法注释就能看出来,execStartActivity()方法负责调用AMS中的方法来启动目标Activity。其中ActivityManagerNative类其实就是AMS的父类,其实现了Binder接口,所以AMS也是一个Binder对象,ActivityManagerNative.getDeDefault()返回的就是一个AMS对象,startActivity方法返回的是一个int值,下面的checkStartActivityResult()就是对该值进行了判断,进行对应处理,诸如抛出异常。我们平时经常遇到的NullPointerException和ClassNotFoundExceptiontion都是从这里来的。

3.我们继续来到AMS中的StartActivity():


可以看到,两个重载的startActivity()最后都辗转调用了mActivityStarter的startActivityMayWait(),然后其实这个ActivityStarter类就是系统封装的专门的控制类,用于在启动Activity之前专门处理flag,Intent的判断以及对stack的操作。


那么我们好好看ActivityStarter就可以了。但是这个类比较多,我只选择两段关键代码说一下好了。


在startActivityMayWait方法中对flag权限进行简单判断之后,来到startActivityUnchecked()方法,这个方法才是具体用来处理启动模式相关逻辑的。首先通过setInitialState(),computeLaunchingTaskFlags(),computeSourceStack()三个方法对启动模式进行设置,初始化ActivityStarter类中的相关变量。所以无论是在xml中指定具体的启动模式还是Intent设置flag其本质上最终都是要在设置到mLaunchFlag中来,而mLaunchFlag又是直接从flag中获取的,故而两种指定启动模式的方法以Intent的flag为最终判定,flag可以覆盖xml中的设置。

    以上部分代码首先是获取mReusedActivity,判断是否具有可以复用的Activity实例,若存在,并且具有CLEARN_TOP权限,并且处于单例 模式或者栈内复用模式的话,那么是可以进行对栈内实例进行出栈操作的,所以调用TaskperformClearTaskForReuseLocked()清除顶部的Activity实例,并调用对应ActivityRecord的deliverNewIntentLocked方法复用原来的实例。


如果mReuseActivity不存在,那么首先对栈顶元素与目标Activity进行判断,如果完全匹配,并且当前启动模式是栈顶复用模式或者栈内复用模式,那么说明当前的栈顶元素可以直接复用。调用resumeFousedStackTopActivity()方法就OK了。



其中singleInstance模式时,在computeLaunchingTaskFlags()就会进行判断,设置mAddingNewTask为true,创立新的任务栈,假如SourceRecord的属性为singleInstance,会为Intent添加NEW_TASK的flag,之后创立新的任务栈,保证原来的任务栈中只有一个Activity实例。





附:关于flag的种类以及其功能的总结

FLAG_ACTIVITY_CLEAR_TOP
    如果设置,并且这个Activity已经在当前的Task中运行,因此,不再是重新启动一个这个Activity的实例,而是在这个Activity上方的所有Activity都将关闭,然后这个Intent会作为一个新的Intent投递到老的Activity(现在位于顶端)中。
    例如,假设一个Task中包含这些Activity:A,B,C,D。如果D调用了startActivity(),并且包含一个指向Activity B的Intent,那么,C和D都将结束,然后B接收到这个Intent,因此,目前stack的状况是:A,B。
    上例中正在运行的Activity B既可以在onNewIntent()中接收到这个新的Intent,也可以把自己关闭然后重新启动来接收这个Intent。如果它的启动模式声明为 “multiple”(默认值),并且你没有在这个Intent中设置FLAG_ACTIVITY_SINGLE_TOP标志,那么它将关闭然后重新创建;对于其它的启动模式,或者在这个Intent中设置FLAG_ACTIVITY_SINGLE_TOP标志,都将把这个Intent投递到当前这个实例的onNewIntent()中。
    这个启动模式还可以与FLAG_ACTIVITY_NEW_TASK结合起来使用:用于启动一个Task中的根Activity,它会把那个Task中任何运行的实例带入前台,然后清除它直到根Activity。这非常有用,例如,当从Notification Manager处启动一个Activity。

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
    如果设置,这将在Task的Activity stack中设置一个还原点,当Task恢复时,需要清理Activity。也就是说,下一次Task带着 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记进入前台时(典型的操作是用户在主画面重启它),这个Activity和它之上的都将关闭,以至于用户不能再返回到它们,但是可以回到之前的Activity。
    这在你的程序有分割点的时候很有用。例如,一个e-mail应用程序可能有一个操作是查看一个附件,需要启动图片浏览Activity来显示。这个 Activity应该作为e-mail应用程序Task的一部分,因为这是用户在这个Task中触发的操作。然而,当用户离开这个Task,然后从主画面选择e-mail app,我们可能希望回到查看的会话中,但不是查看图片附件,因为这让人困惑。通过在启动图片浏览时设定这个标志,浏览及其它启动的Activity在下次用户返回到mail程序时都将全部清除。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    如果设置,新的Activity不会在最近启动的Activity的列表中保存。

FLAG_ACTIVITY_FORWARD_RESULT
    如果设置,并且这个Intent用于从一个存在的Activity启动一个新的Activity,那么,这个作为答复目标的Activity将会传到这个新的Activity中。这种方式下,新的Activity可以调用setResult(int),并且这个结果值将发送给那个作为答复目标的 Activity。

FLAG_ACTIVITY_NEW_TASK 
    如果设置,这个Activity会成为历史stack中一个新Task的开始。一个Task(从启动它的Activity到下一个Task中的 Activity)定义了用户可以迁移的Activity原子组。Task可以移动到前台和后台;在某个特定Task中的所有Activity总是保持相同的次序。
    这个标志一般用于呈现“启动”类型的行为:它们提供用户一系列可以单独完成的事情,与启动它们的Activity完全无关。
    使用这个标志,如果正在启动的Activity的Task已经在运行的话,那么,新的Activity将不会启动;代替的,当前Task会简单的移入前台。参考FLAG_ACTIVITY_MULTIPLE_TASK标志,可以禁用这一行为。
    这个标志不能用于调用方对已经启动的Activity请求结果。


FLAG_ACTIVITY_NO_HISTORY 
    如果设置,新的Activity将不再历史stack中保留。用户一离开它,这个Activity就关闭了。这也可以通过设置noHistory特性。


FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

If set, and this activity is either being started in a new task or bringing to the top an existing task, then it will be launched as the front door of the task. This will result in the application of any affinities needed to have that task in the proper state (either moving activities to or from it), or simply resetting that task to its initial state if needed. 

FLAG_ACTIVITY_SINGLE_TOP
    如果设置,当这个Activity位于历史stack的顶端运行时,不再启动一个新的。 


IntentFilter匹配方式的总结:

Flag一般在隐式调用中用的比较多,通过intent中的flag与IntentFilter进行匹配,IntentFilter中包括action, category, data,三种信息。一个activity中可以存在多个IntentFilter,但是只要匹配成功其中的某一个就可以,不过必须是跟该IntentFilter中的action,category,data都成功匹配才可以。

1.action 匹配。  可以自定义,多个action只要匹配其中一个就可以,但是必须要指定一个action,否则匹配失败。

2.category匹配。可以自定义。intent可以不指定category,默认匹配成功。但是只要是指定了,Intent的每一个category就必须要与IntentFilter中的某一个匹配。否则会匹配失败。

3.data,  data可以指定多个,其匹配方式跟action相同,只要匹配一个data就可以,data包括两个mimeType和URI,URI如果不指定的话会自动有一个默认值,如果mimeType全部匹配,那么代表data匹配成功。







  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值