老生常谈 activity 启动模式

来源:互联网 发布:mac视频截取gif软件 编辑:程序博客网 时间:2024/05/24 04:22

最近新参加的项目中使用到了activity singleInstance模式并在开发中产生了一些bug,发现组内的同事们对launchmode这件事情还缺少一些基本的认识。给大家讲解的同时还是觉得可以把这个过程记录下来,尽管是基础部分很古老的知识但是很多开发者也还是没有认真的学习过,即使已经熟悉了仔细想想也还会有些收获的。一些基本概念的清晰理解在日常的开发也都有着意想不到的重要性。下面开始老生常谈。


AndroidMenifest.xml中每一个activitylaunchMode可以有4种选项:standardsingleTopsingleTasksingleInstance。它们之间有很多区别,也有一些启程转接的东西。


standard也是系统默认的方式。


每一次调用,系统都会创建一个新的activity然后他被置于当前发起intentactivity所在的task中的顶部。形式上可以这样描述:


新开启的#1 activity会统一位于当前task顶部与用户交互,如果此时用户关闭当前页面按钮会回到#0 activity


在实际代码的运行中我们可验证这个过程,请看下面的dumpsys activity主要信息截图:


同一个appintent调用的发起方和响应方同处于一个task中,他们的先后关系也如前面图示中描述的样子。


同一个app中的调用场景是最常见的,那如果进行跨app的调用场景下又是个什么样子呢?请看下面dumpsys activity的主要信息截图:


请注意红色线框中的信息,两个activity分属不同的app中,通过intent的调用被排进了同一个task中与前面同一个app中是一致的,因此对于back按钮的响应也与前述是一致的。


这里面请注意ProcessRecord的信息与前面的截图有着截然的区别,不同的ProcessRecord ID表明了2activity分别运行于2个不同的process中。这里确认了另一个重要的信息,androidtaskprocess两者间并没有必然的关联性。这样设计也为夸app的信息传送添加了一种方式。


下一个是 singleTop


它的基本操作过程与standart是一致的。关键的区别在于如果被调用的activitytask的顶部,那么系统会继续使用它并调用onNewIntent传入新的intent信息。此时需要特别关注的是新的intent在调用onNewIntent后并不会自动与activity关联,使用者必须自己调用setIntent()来建立这种关系。


这个过程图示说明如下:


对于back按钮的响应和面前的standard是一致的,这里就不重复描述了。


在2次调用中dumpsys activity的主要信息是一致的,如下截图:



singleTask


google的文档中是这样说明的“The system creates the activity at the root of a new task and routes the intent to it. However, if an instance of the activity already exists, the system routes the intent to existing instance through a call to its onNewIntent() method, rather than creating a new one.”


相对于前面的类型这一个就比较复杂了,我们分几步来详细说明。

首先:第一次调用这个activity时,系统会将他单独放入一个task中,然后将新的task切换到前台与用户交互。当用户关闭当前页面,系统会将前台被调用的activity移出task,并将启动他的task切换到前台。这个过程如下图所示:


singleTop相似的是,一旦activity被启动后续所有的调用都会传递到到这个实例上,并将它所处的task切换到前台,而不会开启新的实例。同时也有者显著的区别,在task中如果activity没有处于顶部那么调用的时候它前面的activity都会被清除掉,以保证它位于顶部。如下图所示:



通过分析描述为可以知道系统中这个activity只会被启动一次,常驻于他启动时所在的task并保持为根节点。


以上都是对文档的理解,下面用实际的代码检验一下理解的是否正确。


首先我们在app2中创建如下activity描述:


然后进行跨越app的调用,从app1中通过intent调用这个activity,这时dumpsys activity的主要信息截图如下:


这里可以清楚的看到系统在intent调用后创建了task4527在其中安置了新启动的MainActivity2,同时新建的task处于前台与用户交互状态。对back的响应也一如我们预期的那样。


我们再验证同一个app中调用的场景。操作步骤为启动app2然后调用这个activity。此时dumpsys activity的主要信息截图如下:


这时候可以看到并没有创建新的task,启动的activity也没有被放到所处task的底部,此处系统的行为开始与文档中描述的不符。再来尝试跨越app的调用,启动app1调用MainActivity2,此时dumpsys activity的主要信息截图如下:


在这个截图中我们发现了与文档描述相似的地方,app1中发出的调用启动的是前面已经打开过的activity,同时依然被放置于原先的task并没有被加入app1task


接下来继续验证activity处于task下部时,被调用的场景。首先通过已经被启动的MainActivity2调用其它activity,此时dumpsys activity的主要信息截图如下:


然后从activity1上调用MainActivity2,此时dumpsys activity的主要信息截图如下:


系统将activity1task中移除,activity0被置于task顶部与用户进行交互。在此处终于与文档中描述的完全一致了。


singleInstance


和前面的 “singleTask” 非常相似的作用,他们的区别主要有2个:第一被调用的activity一定会独处于一个task中;第二这个activity会是task中唯一的一个activity。这个过程如下图所示:



“singleTask” “singleInstance”相关的还有另外一个参数“taskAffinity”,它的存在对它们的行为有着很重要的影响。我们来看下它的效果。


首先从activity配置开始:


然后在同一个app中启动MainActivity2,看一下dump sys activity的截图信息:


这里看到的信息和前面是完全一致的。但是设备中的截图是这样的:

`

这样的情况大多数同学都会很意外吧,同一个app,同一个ProcessRecord ID,在系统的任务管理里面却被列为2个单项?而且这两个单项都是可以独立关闭的。


在前面已经说明了,系统中processtask是分开管理的并没有必然的联系。在这里我们更进一步可以发现系统的任务管理是基于task的而非基于process,当我们在手机上通过系统的任务管理器关闭一个项目的时候,实际上系统关闭的是一个task,而并不是直接作用于一个process。这样的情况可能和大部分人心中的认知是不符的。关于系统对taskprocess管理将会在后面的文章里介绍。


特别说明一下,文章里的测试结果~截图~dump信息都是在google官方的模拟器和原生的5.0系统中获取的,其他第三方系统或多或少在行为和显示上都会有一些区别,具体机型还需要具体测试。


在这次的介绍里面我最想表达给读者的是3个关键点:

第一:关于学习,务必主要基于官方的文档和说明。在我自己学习的过程中验证过很多网络上文章中的内容,可以发现其中有很多错误的信息,也有很多文章几经转载与原文已经有很大的出入了。

第二:学习的过程中务必要加入自己的实践操作,将各种相关的工具结合起来验证,前面的检验中已经发现了即使官方的文档中也可能有一些不是很精确的内容。特别是对于android这样的开源系统,经过各个厂商的定制它们的产品在一些细节的地方系统行为也是各有不同的,没有经过实际的验证很可能会发现一些意外的情况。

第三:细节。所有的学习务必要关注细节,对细节的关注不足很可能导致在认知上与实际情况产生偏差。


心心相念,必有回响。

2 0