首次安装apk点击Home键后再次打开导致APP重启问题

问题描述:

开发者打包发布一个release版本app,将app包通过电脑QQ传送到手机QQ上面,点击安装,安装后选择打开app,页面结构如下:
1、闪屏页SplashActivity---> 登录页LoginActivity---> 主页MainActivity
2、用户下载app到手机,通过文件管理器找到并安装这个apk,安装后提示:“安装完成,你可以打开xxx应用了”,
3、用户打开app,输入账号密码跳转到了主页MainActivity。
4、用户按下Home键,然后在程序列表点击app,
先后显示:闪屏页SplashActivity---> 登录页LoginActivity,APP重启了!亮瞎了24K钛合金狗眼的我们觉得这玩法不对吧?
期待页面:显示原先的主页MainActivity。
奇怪的是:真机在debug开发调试时不会出现这个问题???

问题原因:

debug版本是通过adb安装启动或平常的桌面Icon图标启动,
release版本是安装这类第三方平台启动。
两者的启动intent不相同!(相同是指:启动类,action、category等等全部一样,不可多项也不可缺少)

在解决问题前,先了解一下相关:

1、Home主界面其实也是一个Activity。

当从APP界面按下Home键盘,实际是启动APP跳转到Home主界面,这样我们的程序就被置于后台,被这个Home主界面Activity覆盖。

2、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】 ---- 其栈的结构为 B1_B2
应用程序C: 【TaskC】 ---- 存在Activity有【C1】【C2】 ---- 其栈的结构为 C1_C2

a、那么我们进入桌面时:Task之间的结构是 A1 ---- 也就是只有一个【TaskA】栈(桌面Task),并且位于最前端(这里表现为最后添加的末端)

b、然后我们点击应用程序B的图标,启动B :Task之间的结构是 A1B1B2 ---- 添加了一个【TaskB】,而且【TaskB】也是位于最前端,现在显示的是【TaskB】的B2的Activity的界面

c、接着点击home键: Android对于home做了特殊默认处理,就是会把桌面Task挪到所以Task最前端,Task结构应该变成 B1_B2_A1 ---- 【TaskA】挪到队列最前端,现在显示的是【TaskA】的A1的Activity的界面,也就是桌面

d、我们再在桌面点击应用程序C的图标,启动C : Task之间的结构变成 B1B2A1C1C2 ---- 添加了一个【TaskC】,而且【TaskC】也是位于最前端,现在显示的是【TaskC】的C2的Activity的界面

从上面的例子,我们可以知道:
  我们编写任何一个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启动的操作呢?

3、桌面的启动管理:

回头研究下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对于这个启动方式的干扰。

原理剖析:

文件管理器虽然使用Intent来启动刚刚安装的那个App,但:它的启动Intent并没有跟桌面的启动Intent完全一致!
我们将桌面的Task记为【TaskDesktop】,文件管理器的Task记为【TaskFile】,我们应用的Task记为【TaskApp】,分析如下:

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

打开文件管理器: D1_F1_F2 ---- F2是安装完毕后询问是否启动对应程序的Activity

点击打开: D1_F1_F2_A1_A2 ---- A1是入口闪屏页,A2是登录Activity

返回桌面: F1_F2_A1_A2_D1 ---- 回到桌面页,也就是D1前置

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

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

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

打开文件管理器: D1_F1_F2 ---- F2是安装完毕后询问是否启动对应程序的Activity

点击打开: D1_F1_F2_A2 ---- A1是入口闪屏页,A2是登录Activity,启动后A1业务逻辑应该finish掉,所以从【TaskA】中挪去

返回桌面: F1_F2_A2_D1 ---- 回到桌面页,也就是D1前置

点击A的图标: F1_F2_D1_A2_A1 -> 找到【TaskA】,挪到前台,由于比对启动的Intent不完全一致,所以新创建一个A1 Activity,那么把A1添加到对应的【TaskA】中,然后A1所再一次触发启动登录页 A2,但是登录页是singleTask模式,所以又回到了上次对应的A2登录页,所以现象为再一次出现闪屏页,然后回到原先的登录页界面。

解决方法

1、让腾讯那些第三方平台修正其启动Intent的设置,使其与原声桌面启动Intent保持完全一致。(PS:基本不可能)
2、正常启动的闪屏页Activity必定在【TaskA】的最底部(实际已finish掉被登录页取代),而第二次闪屏Activity不可能位于Task的最底部,所以在闪屏页Activity的onCreate代码:

protected void onCreate(@Nullable Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     
     // 避免从桌面启动程序后,会重新实例化入口类的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;
             }
         }
     }
 }

或者

   protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //首次启动 Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT 为 0,再次点击图标启动时就不为零了
        if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
            finish();
            return;
        }
    }

读到这里,细心的读者一定会问:你上面说的情形只适用闪屏页和登录页,如果登录进去主页MainActivity按Home键,如何处理呢?
1、闪屏页的OnCreate方法根据登录状态判断跳转:

        if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
            if (UserInfo.getInstance().isLogined) { // 若已登录,跳转到主页
                readyGoThenKill(MainActivity.class);
            } else { // 若未登录,引导用户登录
                readyGoThenKill(UserLoginActivity.class);
            }
            return;
        }

2、设定MainActivity的launchMode="singleTask",在AndroidManifest.xml修改

<activity
    android:name="com.emp.frame.MainActivity"
    android:launchMode="singleTask" />

转载:https://blog.csdn.net/LVXIANGAN/article/details/82870762

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容