Android启动模式

启动模式出现的必然性


说启动模式肯定要讨论栈,因为Activity之间的写作规则在android中即是基于栈这种数据结构来协作的,目的是实现一个(栈这种结构)符合用户使用习惯的"界面流转系统"。

四种启动模式


Standard 标准模式

  • 默认模式,每启动一次都新建一个当前启动对象的实例,无论这个实例是否已存在
  • 被创建的实例生命周期稳定,它的create,start,resume都会被调用
  • 典型的多实例实现,即:一个任务栈中可以有多个实例,每个实例可以属于不同的任务栈
  • 标准模式下,谁启动了这个Activity,那么这个Activity就运行在启动它的Acitivty任务栈中

SingleTop 栈顶复用模式

  • 当打开它时,若Activity实例已经存在并且位于任务栈的栈顶,那么此activity不会被重新创建,同时它的onNewIntent方法会被回调。
  • 通过onNewIntent方法我们能够取出当前请求信息。
    @Override
    protected void onNewIntent(Intent intent) {
   
        super.onNewIntent(intent);
    }
  • 此时此Activity的onCreate \ onStart 不会被调用,因它并没有发生改变。
  • 若Acitivity实例已存在,但是他并没有在栈顶,那么新的Activity实例仍然会被重建。

SingleTask 栈内复用模式

  • 这是一种单例模式
  • 只要Acitivity在一个栈中存在,那么多次启动该Acitivty都不会重建该Acitivty实例,并且会回调其onNewIntent
  • 它具有clearTop的特性

一个测试:

根据《Android开发艺术探索》所述,我们使用Application的Context来启动Activity会报错,这里我去试验一下,加深印象。

测试代码:

getApplication().startActivity(new Intent(LoginActivity.this,ModifyPasswordAcivity.class));

运行结果:

Error:Execution failed for task ‘:usermodule:transformClassesAndResourcesWithPrepareIntermediateJarsForDevelopDebug’.
java.lang.RuntimeException: java.io.IOException: Failed to delete D:\android\project\wisdomcloud\usermodule\build\intermediates\intermediate-jars\develop\debug\classes.jar

修改:

<activity android:name=".ui.activity.ModifyPasswordAcivity" android:launchMode="singleTask"/>

分析:
standrd模式的activity默认会进入启动它的activity所属的任务栈中,但是由于非Activity类型的Context(applicationContext)并没有所谓任务栈,所以这就有问题了。那么为什么在singgleTask下面又能启用这个Acitivty呢?这里根据《Android开发艺术探索》所述,我再复述一次:

当一个singleTask模式的Activity被启动,比如Activity——A,系统会首先寻找是否存在A想要的任务栈(注意这句话,什么叫做想要的任务栈?这句话是个重点,会在下一小节进行讲解),若没有,就重新创建一个任务栈,然后再把A的实例放进去,所以到这里,这个问题就解决了,我们拿applicationContext去请求这个Activiy,当这个Activity的启动模式为singleTask的时候,系统检测到applicationContext没有任务栈,于是乎就去创建了一个任务栈。

但到这儿还没完,上面是说了没检测到任务栈,那么当检测到若有任务栈之后,就会去检查有没有该Activity的实例了。若有就去clearTop且置顶该实例,若没,新建实例

SingleInstance 单实例模式

  • 《Android开发艺术探索》内称其为一种加强的singleTask模式。
  • 加强在哪?他除了具有singleTask的一切特点之外,加强了一点,就是具有此模式的Activity只能单独的存在于一个栈中。
  • 假设Activity——A是singleInstance模式,当A启动后,系统会为他创建一个新的任务栈,而又由于它继承了SingleTask的特性,所以在这个Acitivty-A的栈没有被摧毁之前,他不会被销毁。

TaskAffinity—任务相关性,任务亲和关系


在分析singleTask小节的时候,红字里提到了一句,“Activity想要的任务栈”,我们请求打开一个目标Activity的时候,目标Acitivity启动之前会去查找自己想要的任务栈,用application Context去打开一个在standard模式下的目标Activity会报错,是因为这个目标Activity没有找到自己**“想要的任务栈”,而当目标Acitivity处于singleTask模式,在没有找到"想要的任务栈"时会主动的去创建一个任务栈作为自己"想要的任务栈"**。

由此可见,一个Activity被启动的时候,它寻址的原则是:先找自己想要的任务栈,再入栈。

废话了这么多,那么这个TaskAffinity究竟是个什么玩意,该怎么用?

TaskAffinity是什么

Activity的归属,也就是Activity应该在哪个Task中,Activity与Task的依附关系。我们知道,一般情况下在同一个应用中,启动的Activity都在同一个Task中,它们在该Task中度过自己的生命周期,这些Activity是从一而终的好榜样。

那么为什么我们创建的Activity会进入这个Task中?它们会转到其它的Task中吗?如果转到其它的Task中,它们会到什么样的Task中去?

解决这些问题的关键,在于每个Activity的taskAffinity属性。

TaskAffinity该怎么用

我们可以为每个Activity都单独指定TaskAffinity属性,这个属性值必须不能和包名相同,否则就相当于没有指定。TaskAffinity属性主要和singleTask启动模式或者allowTaskReparenting属性配对使用,在其他情况下没有意义。

TaskAffinity & singleTask

当TaskAffinity和singleTask启动模式配对使用的时候,它是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中。 ——摘自《Android开发艺术探索》

验证


    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:launchMode="singleInstance">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".OtherAcitivty"
            android:taskAffinity="haha.hehe"
            android:allowTaskReparenting="true"
            android:launchMode="singleTask"></activity>
    </application>

将OtherActivity置于前台,然后查看其任务栈信息。

	ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
	Display #0 (activities from top to bottom)://【看这里~看这里~看这里~】从上至下的显示Activity
	  Stack #1:
	  mResumedActivity=ActivityRecord{e8093a1 u0 demo.greendaotest.com.greendaotestdemo/.OtherAcitivty t1973}
	  mFullscreen=true
	  mBounds=null
	    Task id #1973//【看这里~看这里~看这里~】栈id
	    ......
	    Task id #1971//【看这里~看这里~看这里~】栈id
	     ......
	    Running activities (most recent first):
	      TaskRecord{ba435ca #1973 A=haha.hehe U=0 StackId=1 sz=1}
	        Run #3: ActivityRecord{e8093a1 u0 demo.greendaotest.com.greendaotestdemo/.OtherAcitivty t1973}
	      TaskRecord{65aa58 #1971 A=demo.greendaotest.com.greendaotestdemo U=0 StackId=1 sz=1}
	        Run #2: ActivityRecord{681c30b u0 demo.greendaotest.com.greendaotestdemo/.MainActivity t1971}
	
	    mResumedActivity: ActivityRecord{e8093a1 u0 demo.greendaotest.com.greendaotestdemo/.OtherAcitivty t1973}
	    mFocusedActivity: ActivityRecord{e8093a1 u0 demo.greendaotest.com.greendaotestdemo/.OtherAcitivty t1973}

观察结果:

有俩个任务栈,分别是如下所示,注意他们的TaskRecord和task_Id

   Task id #1973
    mFullscreen=true
    mBounds=null
    mMinWidth=-1
    mMinHeight=-1
    mLastNonFullscreenBounds=null
      TaskRecord{
   ba435ca #1973 A=haha.hehe U=0 StackId=1 sz=1}
      Intent {
    flg=0x10000000 cmp=demo.greendaotest.com.greendaotestdemo/.OtherAcitivty }
	Task id #1971
	    mFullscreen=true
	    mBounds=null
	    mMinWidth=-1
	    mMinHeight=-1
	    mLastNonFullscreenBounds=null
	      TaskRecord{
   65aa58 #1971 A=demo.greendaotest.com.greendaotestdemo U=0 StackId=1 sz=1 }

TaskAffinity & allowTaskReparenting

当TaskAffinity和allowTaskReparenting结合的时候,这种情况比较复杂,会产生特殊的效果。当一个应用A启动了应用B的某个Activity后,如果这个Activity的allowTaskReparenting属性为true的话,那么当应用B被启动后,此Activity会直接从应用A的任务栈转移到应用B的任务栈中。这还是很抽象,再具体点,比如现在有2个应用A和B,A启动了B的一个Activity C,然后按Home键回到桌面,然后再单击B的桌面图标,这个时候并不是启动了B的主Activity,而是重新显示了已经被应用A启动的Activity C,或者说,C从A的任务栈转移到了B的任务栈中。可以这么理解,由于A启动了C,这个时候C只能运行在A的任务栈中,但是C属于B应用,正常情况下,它的TaskAffinity值肯定不可能和A的任务栈相同(因为包名不同)。所以,当B被启动后,B会创建自己的任务栈,这个时候系统发现C原本所想要的任务栈已经被创建了,所以就把C从A的任务栈中转移过来了。这种情况读者可以写个例子测试一下,这里就不做示例了。 ——摘自《Android开发艺术探索》

分析:

  • A,B俩应用
  • A应用启动B应用的一个Activity_C(呼至前台) => 这时候Activity_C存在于Task_A当中
  • 按home键,然后单击B的桌面icon
  • **发现:**这个时候并不是启动了B的主Activity,而是重新显示了已经被应用A启动的Activity C

原因: 由于A启动了C,这个时候C只能运行在A的任务栈中,但是C属于B应用,正常情况下,它的TaskAffinity值肯定不可能和A的任务栈相同(因为包名不同)。所以,当B被启动后,B会创建自己的任务栈,这个时候系统发现C原本所想要的任务栈已经被创建了,所以就把C从A的任务栈中转移过来了。

验证:

操作路线: 打开数据库app,切到OtherActivity —> 打开地图app的目标页面Activity_c(跨应用) —> home回桌面 —> 打开地图App

此时观察地图app的任务栈:


	ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)
	  (nothing)
	
	ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)
	  (nothing)
	
	ACTIVITY MANAGER BROADCAST STATS STATE (dumpsys activity broadcast-stats)
	  Last stats (from -1d0h6m23s228ms to -6m16s50ms, +14h37m37s998ms uptime):
	    permission check ok:
	      Number received: 0, skipped: 0
	      Total dispatch time: 0, max: 0
	      Package com.mobike.mobikeapp: 3 times
	      Package com.sound.haolei.android: 31 times
	      Package zj.shitmap: 28 times
	      Package com.tencent.qqhouse: 1 times
	
	  Current stats (from -6m16s50ms to now, +6m16s52ms uptime):
	    permission check ok:
	      Number received: 0, skipped: 0
	      Total dispatch time: 0, max: 0
	      Package zj.shitmap: 2 times
	
	ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)
	  (nothing)
	
	ACTIVITY MANAGER URI PERMISSIONS (dumpsys activity permissions)
	  (nothing)
	
	ACTIVITY MANAGER SERVICES (dumpsys activity services)
	  (nothing)
	
	ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)
	  (nothing)
	
	ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
	Display #0 (activities from top to bottom):
	  Stack #1:
	  mResumedActivity=null
	  mFullscreen=true
	  mBounds=null
	    Task id #2128
	    mFullscreen=true
	    mBounds=null
	    mMinWidth=-1
	    mMinHeight=-1
	    mLastNonFullscreenBounds=null
	      TaskRecord{
   b583ce4 #2128 A=zj.shitmap U=0 StackId=1 sz=1}
	      Intent {
    act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=zj.shitmap/.MainActivity (has extras) }
	        Hist #0: ActivityRecord{
   e69d835 u0 zj.shitmap/.MainActivity t2128}
	          Intent {
    act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=zj.shitmap/.MainActivity bnds=[324,1277][506,1459] (has extras) }
	          ProcessRecord{
   377954d 5202:zj.shitmap/u0a657}
	          Local Activity b232a5a State:
	            mResumed=false mStopped=true mFinished=false
	            mChangingConfigurations=false
	            mCurrentConfig={
   1.0 460mcc1mnc [zh_CN] ldltr sw360dp w360dp h620dp 480dpi nrml long port finger -keyb/v/h -nav/h s.12 themeChanged=0 themeChangedFlags=0}
	            mLoadersStarted=true
	            FragmentManager misc state
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值