欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

从浏览器打开一个本地应用的回退栈问题

程序员文章站 2022-07-15 10:53:14
...

1、首先,先理一下回退栈的问题。

开机启动后,HomeLauncher)所在的Activity在整个回退栈的栈底。

从浏览器打开一个本地应用的回退栈问题

Launcher上的图标点击进入一个应用(Activity)时,默认在启动整个ActivityIntentflag里面加入了FLAG_ACTIVITY_NEW_TASK标记(这个标记的作用是:首先会查找是否存在和被启动的Activity具有相同的亲和性的任务栈,如果有,刚直接把这个栈整体移动到前台,并保持栈中的状态不变,即栈中的activity顺序不变,如果没有,则新建一个栈来存放被启动的activity)。也就是说从launcher启动的Activity默认会在一个新的Task里面。比如我们启动了一个应用,ABC三个Activity是同一个应用(ABC没有设置亲和性,默认都是跟随启动它的那个activity的亲和性的),都归为Task2

从浏览器打开一个本地应用的回退栈问题

如果此时在C里面打开另一个应用另起一个Task

从浏览器打开一个本地应用的回退栈问题

从浏览器打开一个本地应用的回退栈问题

如果,此时按下了Home键,那么Task1将会移动到回退栈的栈顶

从浏览器打开一个本地应用的回退栈问题

从浏览器打开一个本地应用的回退栈问题

 Home里面点击A,那么Task2将会移动到栈顶,回退的时候就是C->B->A->Home

从浏览器打开一个本地应用的回退栈问题


2、应用场景:

A 应用打开一个浏览器,浏览器里面有个链接打开本地应用B(B有两个界面B.1主界面和B.2次界面)。

默认情况下,跳转的activity都在一个Task里面,实际情况是A在Task2(从home为Task1开始)、浏览器(Task3,从A中启动浏览器,浏览器的主页面应该是singleTask模式,所以会在新的task中启动浏览器)、B(B中有两个activity均是标准模式,从B.1启动了B.2)未设置flag标记时与浏览器在同一个Task3中,此时我们从B.2按返回键,则会经过B->浏览器->A;

从浏览器打开一个本地应用的回退栈问题从浏览器打开一个本地应用的回退栈问题

如果从浏览器中启动B.1,在B.1界面(该B.1界面的affinity跟随启动它的浏览器),按Home键,然后在从桌面点击B应用图标进入B.1界面(此时从launcher进入一个应用会默认给intent加上NEW_TASK 这个flag标记,那么新启动的这个B.1就在一个新的Task4中(该activity的affinity属于B所在的应用),也就是在目前的回退栈中存在两个B.1实例。此时,从B.1按返回键,则直接返回到桌面(在Task4中的B.1实例销毁,Task也跟着消亡。

从浏览器打开一个本地应用的回退栈问题

从浏览器打开一个本地应用的回退栈问题

然后我们从launcher里面点击浏览器图标,显示的就是B.1界面。而此时从B.1返回,就返回到浏览器再返回就到桌面了(说明从其他应用启动一个浏览器,浏览器本身做了启动模式处理即不会跟其他应用在一个Task中)

从浏览器打开一个本地应用的回退栈问题
理论上是如上面的情况,但是目前在我的手机的浏览器中打开B.1,按home键后,在点击浏览器,实际上只显示了浏览器的界面,而B.1界面被销毁了,之所以销毁了猜想是因为浏览器主界面是singleTask模式,必然会pop掉在其上面的B.1界面。为了验证这个猜想,我在B.1界面打印了taskID:
07-27 14:46:44.806 29463-29463/com.tongxt.browserapp E/MainActivity: onCreate:taskid = 83
07-27 14:46:44.845 29463-29463/com.tongxt.browserapp I/MainActivity: onStart
07-27 14:46:44.845 29463-29463/com.tongxt.browserapp E/MainActivity: onResume
这里的MainActivity就当做是B.1界面,上面的log是从桌面图标进入B所在的应用的,其taskid为83,进入后的界面如下:
从浏览器打开一个本地应用的回退栈问题
点击按钮,进入second界面(及B.2界面)
07-27 14:51:46.939 29463-29463/com.tongxt.browserapp E/SecondActivity: onCreate:taskid = 83
B.2的taskid也是83,说明这两个activity是在同个栈中的。
此时,我从浏览器的快捷方式中去启动B.1界面:
从浏览器打开一个本地应用的回退栈问题
07-27 14:55:08.259 29463-29463/com.tongxt.browserapp E/MainActivity: onCreate:taskid = 60
07-27 14:55:08.314 29463-29463/com.tongxt.browserapp I/MainActivity: onStart
07-27 14:55:08.314 29463-29463/com.tongxt.browserapp E/MainActivity: onResume
B.1的taskid变成了60,使用adb shell dumpsys activity查看栈信息
TaskRecord{42d480b8 #60 A com.baidu.browser.apps U 0}
    Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10600000 cmp=com.baidu.browser.apps/com.baidu.browser.framework.BdBrowserActivity }
      Hist #7: ActivityRecord{426d3ec8 com.tongxt.browserapp/.MainActivity}
        Intent { act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=znn://aa.bb:80/test?p=12&d=1 cmp=com.tongxt.browserapp/.MainActivity }
        ProcessRecord{42952048 29463:com.tongxt.browserapp/u0a62}
看有一个MainActivity(也就是B.1)是在百度浏览器的栈里面的。另外任务为83的栈中,也有一个B.1实例,和一个B.1启动的B.2实例
TaskRecord{4276a360 #83 A com.tongxt.browserapp U 0}
    Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.tongxt.browserapp/.MainActivity }
      Hist #4: ActivityRecord{426cc298 com.tongxt.browserapp/.SecondActivity}
        Intent { cmp=com.tongxt.browserapp/.SecondActivity }
        ProcessRecord{42952048 29463:com.tongxt.browserapp/u0a62}
      Hist #3: ActivityRecord{426d4508 com.tongxt.browserapp/.MainActivity}
        Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.tongxt.browserapp/.MainActivity bnds=[20,359][190,589] }
        ProcessRecord{42952048 29463:com.tongxt.browserapp/u0a62}

接着,我按home键回到桌面,在点击浏览器图标,直接进入了浏览器界面,同时日志中显示了B.1的onDestory方法,说明浏览器确实是singleTask模式的。
07-27 15:03:30.119 29463-29463/com.tongxt.browserapp E/MainActivity: onDestroy


从浏览器打开一个本地应用的回退栈问题

以上是一般情况下打开应用的一个回退栈实例。


如果我们把B.1的启动模式设置为singleTask,那么浏览器启动的B.1就和浏览器是不同的Task了。这时候按home键,在进入B.1会复用之前Task里面的实例。


另外一个应用场景:

在B应用已经启动并且当前在B.1界面(主界面),按home键回到桌面,再从浏览器中点击进入B应用的二级界面B.2,默认情况下按返回键会返回到浏览器界面,如何返回到B.1界面呢?

我们在B.2界面的activity属性设置为:

android:allowTaskReparenting="true"
android:launchMode="singleTask"

前一个表示会把当前启动的activity移动到具有相同affinity的task中,也就是说如果B.1从桌面启动了,那么B.1默认在一个task中,此时别的应用或task启动了B.2,那么系统会把B.2移动到B.1所在的task中。

所以如果我们在浏览器里面启动了B.2,在B.2中返回键,就会返回到B.1中(前提是B.1已经启动过),再返回(此时B.2和B.1都销毁了,那么其task也跟着销毁了)就返回到浏览器task中。如果我们在B.2中按home键,然后再点击B应用的图标,那么呈现出来的就是B.2在最前面,返回进入B.1,在返回进入桌面。


如果是B.1未启动的情况下,从浏览器启动B.2,那么从B.2返回键返回到浏览器,如果从B.2按home键,再从桌面点击B应用图标,会直接进入B.2界面,再返回直接回到桌面。这种情况下B.1就不会显示了。原因是:从launcher打开一个应用,会去判断该应用是否已经启动过(也就是有了task),如果存在就直接把该task移动到最前面,而此时我们B应用的task里面只有B.2这个activity,所以也就只显示了它了。

以上情况都是基于B.1是standard启动模式的。如果换成singleTask模式,那么在B.1已经存在的情况下,从浏览器启动B.2(这时候的task是B.1在B.2的下面),home键回到桌面,再从launcher点击启动B.1的时候,必然会导致B.2被销毁(只能看到B.1界面),原因是该模式启动的activity会在原来的task里面查找是否已经有这个实例,存在的话,就会把这个实例之上的其他activity都pop掉。而如果B.1没有启动的话,从launcher点击进入B应用,首先会看到B.1界面,按返回键会进入B.2界面,再返回就到桌面了。

相关标签: 回退栈