最近公司测试测出来这么一个问题
具体表现是:
1,用QQ将我们自己的apk包从电脑端传送到手机端,点击安装,安装成功后点击QQ浏览器的打开按钮(这时候的逻辑应该是触发闪屏页,然后finish掉闪屏页,接着跳转到主页Activity(主页Activity是SignleTask设计模式的))
2,然后点击Home键,回到桌面
3,再在桌面上点击app的icon,原谅耿直的我们都是觉得应该直接回到【主页Activity】,但是结果却是又一次触发 【闪屏页Activity】,亮瞎了24K钛合金狗眼的我们觉得这玩法不对吧?
4,但是在QQ安装器安装成功后,不点击QQ浏览器页面的打开按钮,而是回到桌面点击app的icon,接着执行2和3步骤,是不会有问题的
4,好吧,只能自己分析原因解决问题了
现象分析
上面两种操作方式的不同点在于也就是启动App的入口不一样。一者是平常的桌面Icon图标启动,一者是QQ安装这类第三方平台启动,我们都知道,桌面启动的话也是通过startActivity这个api通过特定的Intent向ActivityManageService发起启动任务,所以可以推测出的是QQ安装启动这类方式也是通过Intent启动对应的App,所以,可以肯定的是,通过QQ安装启动这类方式和通过桌面点击app的icon图标方式并不完全一致
背景知识
1,Activity的Task管理
我们都知道,当一个具有singleTask启动模式的Activity请求启动后,比如Activity A,系统首先会寻找是否存在A想要的任务栈,如果不存在,则就重新创建一个任务栈,然后创建A的实例把A放到任务栈中,如果存在A所需的任务栈,这时候要看A在栈中是否有实例存在,如果有实例存在,那么系统就会把A调到栈顶并调用它的onNewIntent,如果实例不存在,就创建A的实例并把A压入栈中,那什么是A想要的任务栈呢,这就涉及到Activity的TaskAffinity,可以翻译为任务相关性,这个参数标示了一个Activity所需要的任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名。当然,我们可以为每个Activity都单独制定TaskAffinity,这个属性值必须不能和包名相同,否则就相当于没有指定,需要指出的是TaskAffinity主要和singleTask启动模式配对使用,在其他情况下并没有意义。
一般来说,Android系统中App的启动与切换依赖于Activity的Task管理,下面简单分析一下不同应用之间切换时候相关Task的变化,根据上面的分析,一个应用中可能有多个Task,一个Task中也有可能有多个Activity,为了简单起见,下面假设A、B、C三个应用都是单Task的
桌面程序App(A):【TaskA】 ---- 存在Activity有【A1】 ---- 其栈的结构为 A1
应用程序B:【TaskB】 ---- 存在Activity有【B1】【B2】 ---- 其栈的结构为 B1B2
应用程序C: 【TaskC】 ---- 存在Activity有【C1】【C2】 ---- 其栈的结构为 C1C2
依次进行下面的操作
a、那么我们进入桌面时:Task之间的结构是 A1 ---- 也就是只有一个【TaskA】栈(桌面Task),并且位于最前端(这里表现为最后添加的末端)
b、然后我们点击应用程序B的图标,启动B :Task之间的结构是 A1B1B2 ---- 添加了一个【TaskB】,而且【TaskB】也是位于最前端,现在显示的是【TaskB】的B2的Activity的界面
c、接着点击home键: Android对于home做了特殊默认处理,就是会把桌面Task挪到所以Task最前端,Task结构应该变成 B1B2A1 ---- 【TaskA】挪到队列最前端,现在显示的是【TaskA】的A1的Activity的界面,也就是桌面
d、我们再在桌面点击应用程序C的图标,启动C : Task之间的结构变成 B1B2A1C1C2 ---- 添加了一个【TaskC】,而且【TaskC】也是位于最前端,现在显示的是【TaskC】的C2的Activity的界面
好了,说了这么多,下面接着分析bug
原理剖析
从此我们可以知道QQ安装器其实也就是使用Intent来启动其刚刚安装的那个App,但是问题所在的是:他们的启动Intent并没有跟桌面的启动Intent完全一致!
而从柯元旦所著的《android内核剖析》一书中有记录如下规则:
每次启动Intent导致新创建Task的时候,该Task会记录导致其创建的Intent;而如果后续需要有一个新的与创建Intent完全一致(完全一致定位为:启动类,action、category等等全部一样,不可多项也不可缺少),那么该Intent并不会触发Activity的新建启动,而只会将已经存在的对应Task移到前台;这也就是为什么桌面会在再次点击图标时将后台任务挪到前台而不是重新启动App的实现。
我们将桌面的Task记为【TaskL】,QQ安装器的Task记为【TaskQ】,我们应用的Task记为【TaskA】,那么分析如下:(L1是单纯的桌面)
打开QQ: L1Q1Q2 ---- Q2是安装完毕后询问是否启动对应程序的Activity
点击打开: L1Q1Q2A2 ---- A1是入口闪屏页,A2是主页Activity,启动后A1业务逻辑应该finish掉,所以从【TaskA】中挪去
返回桌面: Q1Q2A2L1 ---- 回到桌面页,也就是L1前置
点击A的图标: Q1Q2L1A2A1 -> Q1Q2L1A2A1 ---- 找到【TaskA】,挪到前台,由于比对Intent并不是完全一致,所以该请求是新启动Activity,那么把A1添加到对应的【TaskA】中,然后A1所再一次触发启动主页,但是主页是singleTask模式,所以又回到了上次对应的A2主页,所以现象为再一次出现闪屏页,然后回到原先的主页界面。
解决思路
1、让腾讯那些第三方平台修正其启动Intent的设置,使其与原声桌面启动Intent保持完全一致。(PS:基本不可能)
2、自身业务代码规避,我们可以知道,如果是多余的闪屏页入口Activity的话,其基本不可能位于Task的根部,而如果正常启动的话,闪屏页入口Activity必定在多对应的Task的根部位置,那么我们可以从这个地方对于这个bug进行规避,方法就是在闪屏页入口Activity的onCreate代码加入如下一段代码:
// 避免从桌面启动程序后,会重新实例化入口类的activity
if(!this.isTaskRoot()) {
Intent intent = getIntent();
if(intent !=null) {
String action = intent.getAction();
if(intent.hasCategory(Intent.CATEGORY_LAUNCHER) && Intent.ACTION_MAIN.equals(action)) {
finish();
return;
}
}
}
问题解决!
https://www.cnblogs.com/net168/p/5722752.html