现在的位置: 首页 > 综合 > 正文

深刻理解activity启动模式,彻底理解android的四种启动模式,尤其singleTask

2017年02月26日 ⁄ 综合 ⁄ 共 5760字 ⁄ 字号 评论关闭

与界面跳转联系比较紧密的概念是Task(任务)和Back Stack(回退栈),activity的启动模式会影响Task和Back Stack的状态,Intent类中定义的一些标志(以FLAG_ACTIVITY_开头)和activity的属性taskAffinity也影响Task和Back Stack的状态。Task是一个存在于Framework层的概念,在android中,一个应用就是一组组件的集合android组件化程度极高,application是由四大组件组成的。在app安装时,系统会读取manifest的信息,将所有的组件解析出来,以便在运行时对组件进行实例化和调度。task是在程序运行时,只针对activity的概念,task是一组相互关联的activity的集合,它是存在于framework层的一个概念,控制界面的跳转和返回。这个task存在于一个称为back
stack的数据结构中,也就是说,framework是以栈的形式管理用户开启的activity。这个栈的基本行为是,当用户在多个activity之间跳转时,执行压栈操作,当用户按返回键时,执行出栈操作。task是可以跨应用的,这正是task存在的一个重要原因。有的Activity,虽然不在同一个app中,但为了保持用户操作的连贯性,把他们放在同一个任务中,进程是操作系统内核中的一个概念,表示直接受内核调度的执行单位。java编写的应用程序,运行在dalvik虚拟机中,可以认为一个运行中的dalvik虚拟机实例占有一个进程,在默认情况下,一个应用程序的所有组件运行在同一个进程中,应用程序中的不同组件也可以运行在不同的进程中。只需要在manifest中用process属性指定组件所运行的进程的名字。如下所示:

<activity android:name=".MyActivity" android:label="@string/app_nam"  
    android:process=":remote">  
</activity> 

   一个Activity的Launch Mode为standard时,默认启动模式,在这种模式下启动的activity可以被多次实例化,每个新实例都会处理一个相应Intent对象。
   一个Activity的Launch Mode为singleTop时,如果实例已经存在于任务桟的桟顶,那么再启动这个Activity时,不会创建新的实例,而是重用位于栈顶的那个实例,并且会调用该实例的onNewIntent()方法将Intent对象传递到这个实例中。以singleTop模式启动的activity的一个实例已经存在与任务桟中,但是不在桟顶,那么它的行为和standard模式相同,也会创建多个实例。
   一个Activity的Launch Mode为singleTask时,在新建这个Activity时,默认taskAffinity情况下,一般不会新建task,taskAffinity属性默认application中是相同的,那么就会新建在当前栈。如果这个Activity已经在某个task的stack中了,此时只会调用它的onNewIntent(),而不会调用onCreate()。(谷歌的官方文档上称,如果一个activity的启动模式为singleTask,那么系统总会在一个新任务的最底部(root)启动这个activity,并且被这个activity启动的其他activity会和该activity同时存在于这个新任务中。如果系统中已经存在这样的一个activity则会重用这个实例,并且调用他的onNewIntent()方法。即,这样的一个activity在系统中只会存在一个实例。这种说法是不准确的,或者翻译的隔阂,已验证不是总会在栈底启动singletask的activity,已打印验证int taskId = getTaskId();命令行中执行以下命令
adb shell dumpsys activity
 
)其实,把启动模式设置为singleTask,framework在启动该activity时只会把它标示为可在一个新任务中启动,至于是否在一个新任务中启动,还要受其他条件的限制。增加一个taskAffinity属性给启动的singleTask的aty,那么将在新task启动。
   一个Activity的Launch Mode为singleInstance时,总是在新的任务中开启,并且这个新的任务中有且只有这一个实例,也就是说被该实例启动的其他activity会自动运行于另一个任务中。当再次启动该activity的实例时,会重用已存在的任务和实例。并且会调用这个实例的onNewIntent()方法,将Intent实例传递到该实例中。和singleTask相同,同一时刻在系统中只会存在一个这样的Activity实例。
   一个Activity的Launch Mode为singleInstance时,与singleTask模式相同的是在新建这个Activity时,会把它放在一个新的stack中并置于顶部(即放在新的task中)。与singleTask模式不同的是,这个新建的task的stack永远只会有一个元素,就是这个Activity自己。如果这个Activity已经在某个Task的stack中了(这个stack必定只有一个元素,那就是这个Activity自己),此时只会调用它的onNewIntent(),而不会调用onCreate()。
   因为new这两类activity,都会新建到新task。而且如果启动本task已有singletask类型的activity,他会销毁上面的activity,所以也不会得到返回结果,。综上所述,启动这两类activity不会得到返回值,startactivityforresult,会马上执行onresult,得不到数据,会cancle。所以失效。
   activityA用startactivityforresult启动ActivityB(singletask),B会放到A上面,俩在同一个task,但是startactivityforresult不会作用,只会马上会 
 case RESULT_CANCELED:  然后再进入B的onCreate,也不会得到返回值,所以这里只能理解是协议规范了记住singletask的activity不要用startactivityforresult就好,但是能启动在一个task,只是无法传值。
说道协议的关系,那么就会存在一种情况,B已经存在,B下面还有C,那此时finishB,前台出现的C,也是不会返回值给A的,介于这种情况存在,activityA用startactivityforresult启动ActivityB(singletask),协议规范马上执行onresult
  case RESULT_CANCELED:也就能理解了。具体参照博文Sodino
 
   taskAffinity表示一个任务,这个任务就是当前activity所在的任务。The task that the activity has an affinity
for
在概念上,具有相同的affinity的activity(即设置了相同taskAffinity属性的activity)属于同一个任务。一个任务的affinity取决于这个任务的根activity(root
activity)的taskAffinity。默认情况下,一个应用中的所有activity具有相同的taskAffinity,即应用程序的包名。我们可以通过设置不同的taskAffinity属性给应用中的activity分组,也可以把不同的应用中的activity的taskAffinity设置成相同的值。为一个activity的taskAffinity设置一个空字符串,表明这个activity不属于任何task。taskAffinity可以影响当activity以FLAG_ACTIVITY_NEW_TASK标志启动时,它会被启动到哪个任务中。这句话的意思是,当新启动的activity(SecondActivity)是以FLAG_ACTIVITY_NEW_TASK标志启动时(可以认为FLAG_ACTIVITY_NEW_TASK和singleTask作用相同,当启动模式为singleTask时,framework会将它的启动标志设为FLAG_ACTIVITY_NEW_TASK),framework会检索是否已经存在了一个affinity为SecondActivity的taskAffinity.second的任务(即一个TaskRecord对象),而不是随便新建task,有和SecondActivity的taskAffinity时就不新建task了,只是去那个和自己属性一样的task,如果当前的task就是和自己属性相同那就是在当前task了,官方文档可能就是这意思,寻觅和自己属性相同的“新”task。1、如果存在这样的一个任务,则检查在这个任务中是否已经有了一个SecondActivity的实例,如果已经存在一个SecondActivity的实例,重用这个任务和任务中的SecondActivity实例,将这个任务调到前台,清除位于SecondActivity上面的所有Activity,显示SecondActivity,并调用SecondActivity的onNewIntent();
如果不存在一个SecondActivity的实例,会在这个任务中创建SecondActivity的实例,并调用onCreate()方法。2、

如果不存在这样的一个任务,会创建一个新的affinity为com.jg.zhang.androidtasktest.second的任务,并且将SecondActivity启动到这个新的任务中。。。SecondActivity只设置启动模式为singleTask,而不设置taskAffinity,即和启动他的Activity的taskAffinity相同,都为应用的包名,那么SecondActivity是不会开启一个新任务的,这就是著名的怪现象(文档表述不清造成)。

      framework中的判定过程如下:1、在MainActivity启动SecondActivity时,发现启动模式为singleTask,那么设定他的启动标志为FLAG_ACTIVITY_NEW_TASK。。2、然后获得SecondActivity的taskAffinity,即为包名com.xxx.xxx。3、检查是否已经存在一个affinity为com.xxx.xxx的任务,这个任务是存在的,就是MainActivity所在的任务,这个任务是在启动MainActivity时开启的4、既然已经存在这个任务,就检索在这个任务中是否存在一个SecondActivity的实例,发现不存在5、在这个已有的任务中启动一个SecondActivity的实例。
     任务(Task)不还可以跨进程(Process)。把两个singleTask的aty设置成相同的taskAffinity的时候,X
app的A aty 启动B aty ,home。Y app的C aty启动D aty时,B和D是一样的taskAffinity,那么B和D会在一个task但是AB一个进程CD一个进程。
 
   设置singleTask模式只意味着“可以在一个新的任务中开启”,至于是不是真的会在新任务中开启,在framework中还有其他条件的限制。由上面的介绍可知,这个条件为:是否已经存在了一个由他的taskAffinity属性指定的任务。
 
   在启动一个singleTask的Activity实例时,如果系统中已经存在这样一个实例,就会将这个实例调度到任务栈的栈顶,并清除它当前所在任务中位于它上面的所有的activity。此时不走oncreate,走onNewIntent()。
 
 
以singleInstance模式启动的Activity具有全局唯一性,即整个系统(整个系统所有的app中只有一个)中只会存在一个这样的实例,具有独占性,即它会独自占用一个任务,被他开启的任何activity都会运行在其他任务中,被singleInstance模式的Activity开启的其他activity,能够开启一个新任务,但不一定开启新的任务,也可能在已有的一个任务中开启。
     我们在操作软件的过程中,一定会涉及界面的跳转。其实在对界面进行跳转时,Android
Framework既能在同一个任务中对Activity进行调度,也能以Task为单位进行整体调度。在启动模式为standard或singleTop时,一般是在同一个任务中对Activity进行调度,而在启动模式为singleTask或singleInstance是,一般会对Task进行整体调度
     

      onNewIntent()非常好用,Activity第一启动的时候执行onCreate()---->onStart()---->onResume()等后续生命周期函数,也就时说第一次启动Activity并不会执行到onNewIntent(). 而后面如果再有想启动Activity的时候,那就是执行onNewIntent()---->onResart()------>onStart()----->onResume().
 如果android系统由于内存不足把已存在Activity释放掉了,那么再次调用的时候会重新启动Activity即执行onCreate()---->onStart()---->onResume()等。
 当调用到onNewIntent(intent)的时候,需要在onNewIntent()
中使用setIntent(intent)赋值给Activity的Intent.否则,后续的getIntent()都是得到老的Intent。

抱歉!评论已关闭.