Android Bug分析系列:第三方平台安装app启动后,home键回到桌面后点击app启动时会再次启动入口类bug的原因剖析...

前言

  前些天,测试MM发现了一个比较奇怪的bug。

  具体表现是:

  1、将app包通过电脑QQ传送到手机QQ上面,点击安装,安装后选择打开app (此间的应用逻辑应该是要触发 【闪屏页Activity】, 然后跳转 【主页Activity】)

  2、然后MM在 【主页Activity】 时按下了 【Home键】,回到桌面

  3、再点击app的icon图标,原谅耿直的我们都是觉得应该直接回到【主页Activity】,但是结果却是又一次触发 【闪屏页Activity】,亮瞎了24K钛合金狗眼的我们觉得这玩法不对吧?

  4、然后,收拾收拾心情开始定位之路吧~

 

现象分析

  先说说项目结构吧,我们这边的项目需求逻辑是 先进入 【闪屏页Activity】(普通的Activity,启动模式为standard),然后根据一堆初始化操作和判断,一般是接着进入【主页Activity】(Activity的启动模式为singleTask);点击home键不做任何拦截处理,按照系统默认逻辑返回Lanuch桌面。

  也就是说,app的整体交互逻辑并没有特殊之处,并非业务逻辑导致的bug。那么回顾下不同的地方,也就是启动App的入口的区别了,一者是平常的桌面Icon图标启动,一者是QQ安装这类第三方平台启动。我们都知道,桌面启动的话也是通过startActivity这个api通过特定的Intent向ActivityManagerServer发起启动任务;所以我们可以推导出QQ安装启动这类方式也是通过Intent启动对应的App。

  再往下分析的话,可能需要一些前置知识需要了解才能更好的理解。

 

前置知识

1、Activity的Task管理

  一般来说,整个Android系统的App启动与切换管理依赖于相关Activity的Task的管理。一个Task之中可能含有若干个Activity,为了简便起见,我们这里记录【Task A】的Activity分别为 【A1】 、【A2】等,【Task B】的Activity分别为 【B1】 、【B2】。

那么我们来分析下App之间是怎么切换的。

 

  假设应用都是单Task应用(相对于大部分的普通App来说,都是采用单一Task来管理的)

  桌面程序App:【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的界面

 

从上面的例子,我们可以大致了解到Android是怎么管理不同app之间切换的逻辑:

  我们编写任何一个Activity的时候,都可以在AndroidManifest里面显式指定一个taskAffinity的属性,也就是说该Activity归属于对应taskAffinity的栈;如果没有指定任何taskAffinity,那么该Activity将会直接归属于包名所在的Task之下。而我们启动一个Activity时(这里只讨论standard启动模式),那么回去先搜寻对应的Task是否存在,如果不存在,新建一个Task并将Activity入栈,如果已经存在对应的Task,那么直接在对应Task入栈即可。

那么问题来了:如果我们在上面第d步点击的图片并不是程序C的图标,而是重新点击了程序B的图标,此时【TaskB】是已经存在的了,那么为了不会讲B的入口activity(B1)直接在【TaskB】入栈,而是将【TaskB】挪到前台并不做任何Activity启动的操作呢?

 

2、桌面的启动管理:

  回头研究下AndroidManifest这个文件,我们轻而易举发现,但凡是App入口Activity,那么一定会包含 

<intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

这几行代码。这里到底有什么玄机呢?其实这个就是跟桌面约定好的启动拦截过滤器。因为桌面有一个很明显的需求就是,如果我们再次点击已经在后台的App图标时,是应该将该后台任务挪到前台而不是再次启动该App程序。

 

而从柯元旦所著的《android内核剖析》一书中有记录如下规则:

  每次启动Intent导致新创建Task的时候,该Task会记录导致其创建的Intent;而如果后续需要有一个新的与创建Intent完全一致(完全一致定位为:启动类,action、category等等全部一样,不可多项也不可缺少),那么该Intent并不会触发Activity的新建启动,而只会将已经存在的对应Task移到前台;这也就是为什么桌面会在再次点击图标时将后台任务挪到前台而不是重新启动App的实现。

 

  那么为啥要指定入口Activity特定的action和category呢,有一个原因我们可以确定,就是为了让桌面启动app所用的Intent具有特殊性,也就是添加了特别的拦截器,避免其他应用内或者应用间的Intent对于这个启动方式的干扰。

说了这么多,我们可以着手分析上续bug的产生原因了。

 

原理剖析

  从此我们可以知道QQ安装器其实也就是使用Intent来启动其刚刚安装的那个App,但是问题所在的是:他们的启动Intent并没有跟桌面的启动Intent完全一致!

我们将桌面的Task记为【TaskL】,QQ安装器的Task记为【TaskQ】,我们应用的Task记为【TaskA】,那么分析如下:

 

进入桌面: L1 ---- L1是单纯的桌面

打开QQ: L1Q1Q2 ---- Q2是安装完毕后询问是否启动对应程序的Activity

点击打开: L1Q1Q2A1A2 ---- A1是入口闪屏页,A2是主页Activity

返回桌面: Q1Q2A1A2L1 ---- 回到桌面页,也就是L1前置

点击A的图标: Q1Q2L1A1A2A1 ---- 找到【TaskA】,挪到前台,由于比对Intent并不是完全一致,所以该请求是新启动Activity,那么把A1添加到对应的【TaskA】中

所以bug出现了,出现了再一次的闪屏页【A1】,问题定位成功!

 

PS:这里我稍微变种一下,因为一般我们闪屏页都是在启动主页后finish的,而主页一般是singleTask模式

 

打开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;}}
}

 问题解决!

 

作者:码农叔叔(enjoy风铃)
出处:http://www.cnblogs.com/net168/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则下次不给你转载了

转载于:https://www.cnblogs.com/net168/p/5722752.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/271234.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL语言基础:SQL授权相关知识笔记​

1、数据控制数据控制是控制对用户存取的权力&#xff0c;由DBA来决定。DBMS数据控制与功能&#xff1a;1、通过GRANT和REVOKE将授权通知系统&#xff0c;并存入数据字典。2、当用户发起请求时&#xff0c;根据授权情况检查是否执行操作请求。SQL标准包括 delete、insert、selec…

JVM的位置

对于JVM的位置&#xff1a; JVM是运行在操作系统之上的,它与硬件没有直接的交互JVM是运行在操作系统之上的&#xff0c;与硬件没有直接的交互&#xff0c;但是可以调用底层的硬件&#xff0c;用JIN &#xff08;Java本地接口调用底层硬件接口&#xff0c;了解下就好&#xff0…

word2010添加b5纸张大小_纸张幅面规格尺寸你了解吗?

点击蓝色文字关注|十七的锚|(单位&#xff1a;mm)关于印前调整若纸张规格标记字母的前面加一个字母R(或S)时&#xff0c;是表示纸张没有切毛边&#xff0c;经过切边修整后&#xff0c;将减少到标准尺寸&#xff0c;例如RA4(或SA4)表示不切边纸张的尺寸为240mm*330mm&#xff0c…

future promise java_第四章 Future和Promise

Netty是一个异步网络处理框架&#xff0c;在实现中大量使用了Future机制&#xff0c;并在Java自带Future的基础上&#xff0c;增加了Promise机制。这两者的目的都是使异步编程更加方便使用。在阅读源码之前&#xff0c;我们需要对Future的机制有很清楚的认识。## 4.1 异步编程模…

在页面最上面显示当前登陆的状态

首先先判断一下session <li> <?php if(session(m_id)) : ?> 您好, <?php echo session(m_username); ?>[<a href"<?php echo U(Member/logout) ; ?>">退出</a>] <?php else : ?> 您好&#xff0c;欢迎来到京西&a…

python入门之运算符的使用的答案_python入门教程之基本算术运算符

一、算术运算符运算符-*/%**(幂)求次方//(取整除&#xff0c;向下取整)如&#xff1a;9//2 4二、比较运算符运算符!<>(不等于&#xff0c;类似&#xff01;)<>><#举例说明&#xff1a;x 10y 20print(x > y) # Falseprint(x < y) # Trueprint(x > …

JVM体系结构

JVM体系结构 Java虚拟机主要分为五大模块&#xff1a; 类装载器子系统运行时数据区执行引擎本地方法接口垃圾收集模块1、JVM一共分为五个区&#xff0c;分别为方法区、堆、Java栈、本地方法栈、程序计 2、方法区、堆为线程共享区域&#xff0c;GC会对二个区进行垃圾回收&…

找数据?这几个数据源网站就够用了?

来源&#xff1a;经管学苑转自&#xff1a;经管学苑大家好&#xff0c;我是小z在这个用数据说话的时代&#xff0c;能够打动人的往往是用数据说话的理性分析&#xff0c;无论是对于混迹职场的小年轻&#xff0c;还是需要数据进行分析和研究的同学&#xff0c;能够找到合适的数据…

solaris11-text-安装GUI(gnome)

http://blog.chinaunix.net/xmlrpc.php?rblog/article&uid45057&id3018467 1.下载所需的资源Text Installer CDRepository Image(a,b)http://www.oracle.com/technetwork/server-storage/solaris11/downloads/index.html2.用text CD安装solaris11安装那是相当的快呀&a…

java replaceall 大小写_Java replaceAll不区分大小写

Java 中replaceAll如何忽略大小写呢?方式一:在正则表达式前面添加(?i)Testpublic void test_replaceAll33(){String input "I like Java,jAva is very easy and jaVa is so popular.";String replacement"cccc";System.out.println(input);System.out.p…

jdk环境变量配置_jmeter及jdk的环境变量配置

jmeter是apache公司基于java开发的一款开源压力测试工具&#xff0c;其内部原理都是源于java的运行&#xff0c;并支持多种外部插件用于接口及性能测试&#xff0c;最主要的还是开源免费&#xff0c;在安装jmeter前必须配置jdk环境。jdk下载地址&#xff1a;https://www.oracle…

jvm是运行在操作系统之上的,他和硬件没有直接的交互

jvm是运行在操作系统之上的&#xff0c;他和硬件没有直接的交互

SQL语言基础:触发器相关知识介绍​

1、触发器 Trigger介绍触发器可以实现完整性规则和保证一些复杂业务规则的实施。针对示警或满足特定 条件下自动执行某项任务来说&#xff0c;触发器是十分有用的机制。触发器是由事件1驱动的特殊过程&#xff0c;一旦由某个用户定义&#xff0c;任何用户对该触发器指定的数据新…

socket与socketServer通信

服务端代码&#xff1a; public class TalkServer {public static void main(String args[]){try {//创建服务器&#xff0c;开放7777端口ServerSocket server new ServerSocket(7777);while(true){Socket socket server.accept();System.out.println("客户端与服务端已…

java struts2值栈ognl_Struts2的值栈和OGNL牛逼啊

Struts2的值栈和OGNL牛逼啊一 值栈简介&#xff1a;值栈是对应每个请求对象的一套内存数据的封装&#xff0c;Struts2会给每个请求创建一个新的值栈&#xff0c;值栈能够线程安全的为每个请求提供公共的数据存取服务。二 OGNL介绍&#xff1a;(1)基本数据&#xff1a;OGNL 是对…

还在用 Notepad++吗? 盘点五款更好用的文本编辑器

来自&#xff1a;oschina.net/news/110987/no-notepad-plus-plus | 责编&#xff1a;乐乐正文 Notepad 一直饱受争议&#xff0c;2018年开始然后引发了大家的关注&#xff0c;这里师长作为技术媒体&#xff0c;不方便带啥节奏&#xff0c;具体事件内容请大家自行百度。其实作…

python3列表_Python3 列表List(十一)

list是一种有序可重复的集合&#xff0c;可以随时添加和删除其中的元素。序列是Python中最基本的数据结构。序列中的每个元素都分配一个数字 - 它的位置&#xff0c;或索引&#xff0c;第一个索引是0&#xff0c;第二个索引是1&#xff0c;依此类推。Python有6个序列的内置类型…

ubuntu14.04 boost动态库找不到 libboost_system.so.1.58.0

error while loading shared libraries: : cannot open shared object file: No such file or directory 解决办法&#xff1a;向ld.so.conf文件中添加libboost_system.so.1.58.0所在的目录路径sudo vim /etc/ld.so.conf sudo ldconfig http://blog.csdn.net/smallfish0315/ar…

SQL语言:嵌入式SQL知识笔记

1、概念SQL提供了将SQL语句嵌入到某种高级语言中的使用方式&#xff0c;通常采用预编译的方法将SQL语句嵌入高级语言中。采用的方法由DBMS的预处理程序对源程序进行扫码、识别出SQL语句&#xff0c;把它们转换为主语言调用语句&#xff0c;这样可以让主语言编译程序能识别它&am…