安卓Activity的启动模式

Activity的启动模式

  Android中,Activity的存储结构是栈,栈是一种先进后出的数据结构,用栈存储Activity也符合我们的操作习惯(后打开的页面会覆盖先打开的页面,按下返回键时,我们希望看到之前的页面。
  为什么需要有Activity启动模式呢,正常来说,一个APP会有一个Activity返回栈,但是特殊场景下,也有一个APP需要两个返回栈的场景。再比如我们连续启动多个一样的Activity,那么系统会创建多个Activity的实例,并依次入栈,这样看起来有点多余,所以Android系统提供了四种启动模式:standand、singleTop、singleTask和singleInstance。

Standand

  标准模式,如果我们不指定Activity的启动模式,那么默认的Activity启动模式就是Standand(标准模式)。
  标准模式下,启动相同的Activity,系统会创建多个Activity,如下图:standard模式下启动activity
  多次创建Activity1,会出现多个实例,比如栈中有4个相同的Activity,需要按下4次back键才能退出APP。也就是说standard模式下,只要我们创建新的Activity,就会再栈中压入该Activity,不管该Activity是否已经存在,并且由于重新创建了Activity,生命周期函数onCreate、onStart、onResume也会被调用。

SingleTop

  即栈顶复用模式,如果要启动的目标Activity刚好在栈顶,那么不会重新创建Activity,并且会调用栈顶Activity的onNewIntent方法。

SingleTask

  栈内复用模式,启动一个singleTask的Activity时,首先判断该Activity指定的栈是否存在(不指定栈名为app包名),如果存在,只要栈内存在目标Activity,那么多次启动该Activity不会重新创建,而是将目标Activity之上的所有Activity出栈,让目标Activity处于栈顶,并调用onNewIntent方法,如果栈内不存在Activity,直接创建新的Activity实例压入栈中。如果栈不存在,则创建新的栈,并将该Activity实例压入新栈,如下流程图:
在这里插入图片描述
  思考:Activity1(standand)启动Activity2(singleTask),Activity2启动Activity3(standand),会产生多少个栈,栈内分别有多少个Activity?
  这里我们分情况讨论,除指定launchMode为singleTask之外还区分是否有指定taskAffinity属性:

  1. Activity2只指定为singleTask,不指定taskAffinity,不指定taskAffinity表示要启动的Activity2会进入默认的栈(默认栈名为app包名),那么运行结果如下:

    onCreate activity1, taskId 195
    onCreate activity2, taskId 195
    onCreate activity3, taskId 195
    
  2. 除了指定activity2的launchMode为singleTask之外,指定activity2的taskAffinity为"a.b.c",这里的taskAffinity只要不和包名相同既可。

    <activity
    	android:name=".MainActivity2"
    	android:taskAffinity="a.b.c"
    	android:launchMode="singleTask"
    	.../>
    

    运行结果:

    onCreate activity1, taskId 197
    onCreate activity2, taskId 198
    onCreate activity3, taskId 198
    

    不难看出,与情况1不同,启动activity2(singleTask+taskAffinity不是包名)后,activity2进入了新的栈,且在activity2上启动的activity3也进入了activity2所在的栈。

SingleInstance

  singleInstance是加强版的singleTask,除了具有singleTask模式的所有特征外,还有一点,singleInstance模式下的activity,会单独占有一个返回栈。并且,如果启动的activity是singleInstance模式,安卓系统的切换动画是task间的切换,这是安卓系统刻意的提醒。
  思考1:Activity1(standand)启动了Activity2(singleInstance)启动了Activity3,会产生多少个栈,栈内分别有多少个Activity?
  答案是有两个栈,栈1有activity1和activity3,栈2有activity2
在这里插入图片描述
  思考2:当前看到的activity是3,如果连续按下两次back键,会依次看到哪些activity。
  答案是activity3 -> activity1 -> activity2,按下back键后,会在当前的返回栈(stack1)进行回退,等stack1的元素空之后,再回退activity2。

Activity的Flags

  1. FLAG_ACTIVITY_NEW_TASK
    FLAG_ACTIVITY_NEW_TASK的作用,相当于在manifest.xml中指定launchMode为singleTask。
    1)在Activity中使用时,需要指定taskAffinity与包名不同:

    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            findViewById<TextView>(R.id.tv).setOnClickListener {
                val intent = Intent(this, MainActivity2::class.java)
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                startActivity(intent)
            }
    }
    

    AndroidManifest.xml

    	<activity 
    		android:name=".MainActivity2"
            android:taskAffinity="a.b"/>
    

    运行结果:
    onCreate MainActivity1 taskId:4730
    onCreate MainActivity2 taskId:4731

    2)在service中启动一个activity时,会出现异常:

    aused by: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

    我们知道如果从activity中打开另一个activity,默认会放入当前activity的栈,但是我们在service中使用的context是service,所以被打开的activity不知道进入哪个栈,所以需要加上FLAG_ACTIVITY_NEW_TASK

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            val i = Intent(this, MainActivity2::class.java)
            i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
            startActivity(i)
            return super.onStartCommand(intent, flags, startId)
        }
    

    运行结果:
    onCreate MainActivity1 taskId:4786
    onCreate MainActivity2 taskId:4786
    可见其实MainActivity1和MainActivity2也是进入同一个栈

  2. FLAG_ACTIVITY_SINGLE_TOP
    与在AndroidManifest.xml中设置singleTop一个效果

  3. FLAG_ACTIVITY_CLEAR_TOP
    假如现在已经启动了Activity1,2,3,现在从activity3中启动activity2,并设置FLAG_ACTIVITY_CLEAR_TOP标签,则栈内只剩下activity1,2,并且activity2重新onCreate,不会走onNewIntent。

    	val intent = Intent(this, MainActivity2::class.java)
    	intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
    	startActivity(intent)
    

    想要走onNewIntent不走onCreate,可以多加一个FLAG_ACTIVITY_SINGLE_TOP

参考书籍《android开发艺术探索》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值