Android-Activity启动模式详解

作者: c37d344afd22 | 来源:发表于2016-08-26 00:54 被阅读265次

    最近在写一个小Demo(然而现在也没写出来)。几天没更新,不知道有没有人还有一点点的小期待(估计没有)。让我边写文章边哭一会。。。


    Activity的LaunchMode

    为啥子需要启动模式?

    我们知道启动Activity时会把Activity放入一个任务栈,所以当我们按back键时就会从Activity从任务栈里清除,当任务栈里为空时,系统就会回收这个任务栈。举个例子,我们在当前Activity启动当前的Activity,那么任务栈里就全是当前Activity,这样当然很二逼了!所以启动模式就出来了,目前有四种启动模式

    • standard
    • singleTop
    • singleTask
    • singleInstance

    standard

    standard是标准模式,也就是默认模式。每次启动一个Activity就会创建一个新的实例,不管这个实例是否已经存在,这就是典型的多实例实现。一个任务栈可以有多个实例,每个实例也可以属于不同的任务栈,谁启动了这个Activity,这个Activity就运行在启动它的那个任务栈中,当我们用ApplicationContext去启动一个standard模式的Activity会报错,因为standard的Activity会进入启动他的Activity任务栈中,但是由于非Activity类型的Context并没有任务栈,所以这样就出问题了

    singleTop

    singleTop即栈顶复用模式,在这种模式下,如果新创建的Activity已经位于栈顶,那么就不会去创建新的实例,但是该Activity的onNewIntent会被调用,通过这个方法我们可以取出请求的信息。需要注意的是,该Activity的onCreate、onStart不会被调用,因为它没有发生改变。但是如果新的Activity不位于栈顶,那么就会创建一个新的实例。举个例子,比如现在一个任务栈有ABCD四个Activity,A位于栈底,D位于栈顶。这个时候如果去启动D,那么栈的情况还是ABCD四个实例,如果D的启动模式为standard,那么栈内就是ABCDD

    singleTask

    栈内复用模式,在这种模式情况下,只要Activity在一个栈中存在,那么启动这个Activity都不会重新创建实例,也会调用onNewIntent。当一个singleTask模式的Activity『A』被请求启动,首先系统会寻找是否有A所需的任务栈,如果不存在,那就重新创建一个任务栈,然后创建A的实例放入栈中。如果存在A所需的任务栈,这时再看A是否在这个栈中有实例存在,如果存在,那么久回把A调到栈顶并调用onNewIntent方法。如果不存在,那么就创建A的实例并且压入栈中,举个例子

    • 比如当前任务栈S1中有ABC三个实例,这个时候singleTask模式的D启动,但是其所需要的任务栈为S2,由于S2和D的实例都不存在,这个时候就会先创建S2任务栈,然后创建D的实例压栈到S2
    • 如果D需要的任务栈为S1,那么由于任务栈S1已经存在,所以直接会创建D的实例放到S1任务栈中
    • 如果D需要的任务栈为S1,并且S1的情况为ADBC,这时不会重新创建D的实例,会把D调用栈顶,同时singleTask默认具有clearTop效果,所以会导致D上面的Activity全部出栈,于是最终S1的情况为AD

    singleInstance

    单例模式,加强的singleTask,这种模式的Activity只能单独运行在一个单独的任务栈中,其他和singleTask一样

    所需的任务栈?

    在singleTask中提到『所需的任务栈』,什么是Activity所需的任务栈?这里就要提一个参数:TaskAffinity。这个参数标识了一个Activity所需的任务栈的名字,默认情况下所有的Activity所需的任务栈都是当前包名,当然我们也可以为每个Activity单独指定TaskAffinity,注意这个属性值必须不能和包名相同,TaskAffinity主要和singleTask配合使用,否则的话没什么意义。当启动一个被TaskAffinity标识了的Activity,那么该Activity就会运行在和TaskAffinity相同的任务栈中

    我们用两种方式设置Activity的启动模式

    1. 在AndroidManifest里为Activity设置
      android:launchMode="singleTop/singleTask/singleInstance"
    2. 通过Intent设置标志位来指定
      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

    这两种方式第二种的优先级要高于第一种,也就是说两者同时存在时,以第二种为准


    强行举个特殊的例子

    如图:

    首先我们看一下AndroidManifest里的配置

    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
    
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    
    <activity
        android:name=".SecondActivity"
        android:launchMode="singleTask"
        android:taskAffinity="com.task1" />
    
    <activity
        android:name=".ThirdActivity"
        android:launchMode="singleTask"
        android:taskAffinity="com.task1" />
    

    可以看到我们给SecondActivity和ThirdActivity设置了singleTask模式和taskAffinity,也就是所需的任务栈。从图里我们可以看出,首先由MainActivity启动SecondActivity,然后由SecondActivity启动ThirdActivity,再由ThirdActivity启动MainActivity,最后再启动SecondActivity。然后我们按两次回退键,发现回到了桌面了,这是为什么?

    首先我们把MainActivity设为A,SecondActivity为B,ThirdActivity为C。

    从Manifest文件中可以看到A为standard模式,任务栈为包名,而B和C是singleTask模式,任务栈为com.task1。

    所以当我们从A启动B的时候,系统会为我们新建一个任务栈,就是com.task1,从而把B进栈。

    从B启动C时,发现C所需要的任务栈com.task1已经存在,但是没有发现C的实例,这时候就创建C的实例并且进栈,这时com.task1里为BC

    然后我们又从C启动A,由于A为standard模式,所以A会进入到C的任务栈,这时有两个任务栈存在,一个是为包名的任务栈,一个是为com.task1的任务栈。包名的任务栈里只有一个A,而com.task1的任务栈里现在是BCA

    接着我们由A启动B,B是singleTask模式,而恰好当前栈里存在着B的实例,所以不会重新创建实例,而是把B调到栈顶,并且把前面的Activity通通出栈,此时com.task1的任务栈里就只剩下一个B了

    最后我们按下一次回退键,当前B被出栈,com.task1任务栈被销毁,调出包名任务栈,也就是A,然后再按一次就退出程序了,这也就间接的证明了切换singleTask模式的Activity会把在它之上的Activity出栈。


    写了这么多字累死我了

    最后

    爱生活,爱小丽,爱Android

    相关文章

      网友评论

        本文标题:Android-Activity启动模式详解

        本文链接:https://www.haomeiwen.com/subject/ahgasttx.html