Activity的启动模式如今说起来似乎是个古老的话题,可又不得不承认它是个很重要的内容,从android诞生到现在,这些启动模式一直在发挥着作用,只不过不容易被人感觉到它们的存在。我们随手敲下一行startActivity代码就伴随着一个启动该activity的启动模式。这些启动模式将会影响到应用的返回行为。从A启动B,再按back键,会返回到A吗?不一定。为什么呢?请往下看。
在介绍启动模式之前有必要先介绍一下android中Task的概念,因为它与activity息息相关。引用 官网 的话来说:
Task是用户在执行某项工作时与之互动的一系列 Activity 的集合。这些 Activity 按照每个 Activity 打开的顺序排列在一个返回堆栈中。
通俗地讲,Task就是一个栈,里面放了很多activity,当你按back键的时候系统会将当前task的栈顶activity出栈,然后在屏幕上显示新的栈顶activity。看一下下面这个图:
介绍完task,接下来具体说一说activity的4种启动模式:
standard是最常用的一种启动模式,使用该模式启动Activity,每次启动一个Activity都会重写创建一个新的实例,不管这个实例存不存在,这种模式下,谁启动了该模式的Activity,该Activity就属于启动它的Activity的任务栈中。
但是,如果直接在Application中以标准模式启动Activity,则会报出以下错误(Android7、Android8除外):
Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
at android.app.ContextImpl.startActivity(ContextImpl.java:912)
at android.app.ContextImpl.startActivity(ContextImpl.java:888)
at android.content.ContextWrapper.startActivity(ContextWrapper.java:379)
应用场景:
应用最广,绝大多数app内的activity都是这种启动方式,比如在首页点一个菜单,进入具体的功能页
使用这个模式启动activity,如果要启动的activity已经位于栈顶,那么这个Activity不会被重新创建,它的onCreate(),onStart()方法不会被调用,只有它的onNewIntent方法会被调用,通过此方法的参数我们可以去处理当前请求的信息。
如果栈顶不存在该Activity的实例,则情况与standard模式相同。会新建这个activity的实例,并入栈。
应用场景:
网易新闻。 假设主界面为 MainActivity,显示新闻的界面是 DetailActivity,显然显示任何一条新闻都会使用 DetailActivity,即把新闻内容通过 Intent 传给 DetailActivity 就可以了。 假设你正在看新闻1(即在 DetailActivity),此时手机收到服务器的推送:收到一条通知(新闻2),点击通知就会跳转到 DetailActivity 并显示新闻2,当你点击通知时,因为目前栈顶的 Activity 就是 DetailActivity,因此这里就是使用 SingleTop 的地方,即点击通知后以 SingleTop 加载模式打开 DetailActivity 并显示新闻2,因此新闻1的 DetailActivity 就被覆盖掉了。 此后你点击返回键会回到主界面。
这个模式的行为较复杂,它还可以与其它标志位组合出多种行为。
此时启动一个activity,如果栈中存在这个Activity的实例就会复用这个Activity,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,并且会回调该实例的onNewIntent方法。如果栈中不存在这个Activity的实例就会新建一个实例并入栈。
这里又分为两种情况:
a、假设从appA启动一个activity,启动时指定的taskAffinity的值为aapA的包名,那么行为跟3.1一样,实际上如果不指定,默认值就是aapA的包名,因此它们的行为是一致的。
b、假设从appA启动一个activity,启动时指定的taskAffinity的值不是aapA的包名,比如是taskB,那么系统首先会去寻找当前是否存在一个叫“taskB”的任务栈,
如果不存在,则会创建一个新的Task,名字叫“taskB”。并创建新的Activity实例入栈到新创建的Task中去。
如果存在,则得到该任务栈,查找该任务栈中是否存在该Activity实例,如果存在实例,则将它上面的Activity实例都出栈,然后回调启动的Activity实例的onNewIntent方法,如果不存在该实例,则新建Activity,并入栈。
此外,我们可以将两个不同App中的Activity设置为相同的taskAffinity,这样虽然在不同的应用中,但是Activity会被分配到同一个Task中去。
以上几种情况,目标task都会被调到前台,这样可以实现从appA中调起AppB。
应用场景:
从appA调起小程序,在小程序处理完事情之后返回app,此时小程序是通过调用我们app里面的WXEntryActivity来唤起原本处于后台的app的,而WXEntryActivity正是标记了singleTask,使得app可以从后台回到前台。
该模式具备singleTask模式的所有特性外,与它的区别就是,这种模式下的Activity会单独占用一个Task栈,具有全局唯一性,即整个系统中就这么一个实例,由于栈内复用的特性,后续的请求均不会创建新的Activity实例,除非这个特殊的任务栈被销毁了。以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。
应用场景:
闹铃的响铃界面。 你以前设置了一个闹铃:上午6点。在上午5点58分,你启动了闹铃设置界面,并按 Home 键回桌面;在上午5点59分时,你在微信和朋友聊天; 在6点时,闹铃响了,并且弹出了一个对话框形式的 Activity(名为 AlarmAlertActivity) 提示你到6点了(这个 Activity 就是以 SingleInstance 加载模式打开的),你按返回键,回到的是微信的聊天界面,这是因为 AlarmAlertActivity 所在的 Task 的栈只有他一个元素, 因此退出之后这个 Task 的栈空了。如果是以 SingleTask 打开 AlarmAlertActivity,那么当闹铃响了的时候,按返回键应该进入闹铃设置界面。
allowTaskReparenting的主要作用是activity的迁移,即从一个task迁移到另一个task,这个迁移跟activity的taskAffinity有关。当allowTaskReparenting的值为“true”时,则表示Activity能从启动的Task移动到有着相同affinity的Task(当这个Task进入到前台时),当allowTaskReparenting的值为“false”,表示它必须呆在启动时所在的那个Task里。默认值为“false”。
应用场景:
在某外卖 App 中下好订单后,跳转到支付宝进行支付。当在支付宝中支付成功之后,页面停留在支付宝支付成功页面。
按 Home 键,在主页面重新打开支付宝,页面上显示的并不是支付宝主页面,而是之前的支付成功页面。
再次进入外卖 App,可以发现支付宝成功页面已经消失。
由于水平有限,如果文中存在错误之处,请大家批评指正,欢迎大家一起来分享、探讨!
博客:http://blog.csdn.net/MingHuang2017
GitHub:https://github.com/MingHuang1024
Email: [email protected]
itHub:https://github.com/MingHuang1024
Email: [email protected]
微信:724360018