栈的原理:先进后出,后进先出。所有操作都发生在栈顶。
首先介绍一下任务栈:
(1)程序打开时就创建了一个任务栈, 用于存储当前程序的activity,所有的activity属于一个任务栈。
(2)一个任务栈包含了一个activity的集合, 去有序的选择哪一个activity和用户进行交互:只有在任务栈栈顶的activity才可以跟用户进行交互。
(3)任务栈可以移动到后台, 并且保留了每一个activity的状态. 并且有序的给用户列出它们的任务, 而且还不丢失它们状态信息。
(4)退出应用程序时:当把所有的任务栈中所有的activity清除出栈时,任务栈会被销毁,程序退出。
任务栈的缺点:
(1)每开启一次页面都会在任务栈中添加一个Activity,而只有任务栈中的Activity全部清除出栈时,任务栈被销毁,程序才会退出,这样就造成了用,户体验差, 需要点击多次返回才可以把程序退出了。
(2)每开启一次页面都会在任务栈中添加一个Activity还会造成数据冗余, 重复数据太多, 会导致内存溢出的问题(OOM)。
为了解决任务栈的缺点,我们引入了启动模式。
一 .standard - 正常模式(默认情况下,不设置LauchMode时)
谁启动了该模式的Activity,该Activity就属于启动它的Activity的任务栈中。每次启动一个Activity都会重新创建一个新的实例
二 .singleTop - 栈顶复用模式
分三种情况:
1.当前栈中已有该Activity的实例,并且位于栈顶。不会创建实例,而是复用栈顶的实例。回调onNewIntent方法。
2.当前栈中已有该Activity的实例,但是不在栈顶。则会创建实例,其行为跟standard启动模式一样。
3.当前栈中不存在该Activity实例,其行为同standard启动模式一样。
上面两种启动模式都是在原任务栈中新建Activity实例,不会启动新的Task,即使你指定了taskAffinity属性(任务相关性)
taskAffinity:
⑴这个参数表示了一个Activity所需要任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名。
⑵我们可以单独指定每一个Activity的taskAffinity属性覆盖默认值
⑶一个任务的affinity决定于这个任务的根activity(root activity)的taskAffinity
⑷在概念上,具有相同的affinity的activity(即设置了相同taskAffinity属性的activity)属于同一个任务
⑸为一个activity的taskAffinity设置一个空字符串,表明这个activity不属于任何task
三.singleTask - 栈内复用模式
启动Activity时,首先根据taskAffinity去寻找当前是否存在一个对应名字的任务栈。
1.如果不存在,则会创建一个新的Task,并创建新的Activity实例入栈到新创建的Task中去。
2.如果存在,则得到该任务栈,查找该任务栈中是否存在该Activity实例。
如果存在实例,则将它上面的Activity都出栈,然后回调启动的Activity实例的onNewIntent方法。
如果不存在该实例,则新建Activity,并入栈。
此外,我们可以将两个不同App中的Activity设置为相同的taskAffinity,这样虽然在不同的app中,但是Activity会分配到同一个Task中去。
四.singleInstance - 全局唯一模式
该模式具备singleTask模式的所有特性外,与它的区别是:这种模式下的Activity会独占一个Task栈,具有全局唯一性,即整个系统中就这么一个实例,由于栈内复用的特性,后续的请求均不会创建新的Activity的实例,除非这个特殊的任务栈被销毁了。以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activity时,已经存在了一个实例,那么会把他所在的任务调度到前台,重用这个实例。
应用场景:
singleTop适合接收通知启动的内容显示页面。例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很烦人的。
singleTask适合作为程序入口点。例如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。之前打开过的页面,打开之前的页面就ok,不再新建。
singleInstance适合需要与程序分离开的页面。例如闹铃提醒,将闹铃提醒与闹铃设置分离。singleInstance不要用于中间页面,如果用于中间页面,跳转会有问题,比如:A -> B (singleInstance) -> C,完全退出后,在此启动,首先打开的是B。
相信细心的同学已经看到onNewIntent这个方法了,接下来献上小弟的拙见:
大家遇到一个应用的Activity供多种方式调用启动的情况,多个调用希望只有一个Activity的实例存在,这就需要Activity的onNewIntent(Intent intent)方法了。只要在Activity中加入自己的onNewIntent(intent)的实现加上Manifest中对Activity设置lanuchMode=“singleTask”就可以。
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。