测试同学近日在测试中发现了这么一个 Bug,复现步骤如下:
- 打开应用
- 任意开启一个页面
- Home 键回退到 Launcher
- 重装应用
- 打开应用
- 任意开启一个页面
- Home 键回退到 Launcher
- 点击应用图标启动应用
- Bug 出现了:此时应用并没有如预期般直接将 Task 带到前台,而是开屏页一闪而过,然后就回到了应用的主页面
我们知道,每次发起 Intent 导致新创建 Task 的时候,该 Task 会记录该 Intent 的信息;如果后续有一个新的 Intent 出现并与该 Task 的启动 Intent 完全一致(启动类,action、category 等等全部一样,不可多项也不可缺少),那么该 Intent 并不会触发 Activity 的新建与启动,而只会将已经存在的 Task 移到前台。
那么为什么会出现如 bug 所述那样的问题呢?下面按照 Bug 浮现步骤来捋一捋:
首先定义一下,L 为 Launcher,P1 为开屏页,P2 为主页面,P3 为任意一个其他页面。不同 task 用 | 分割,相同 task 的页面用 / 分割。
- 打开应用:L -> L|P1|P2 -> L|P2
- 任意开启一个页面:L|P2/P3
- Home 键回退到 Launcher:P2/P3|L
- 重装应用:L
- 打开应用:L -> L|P1|P2 -> L|P2
- 任意开启一个页面:L|P2/P3
- Home 键回退到 Launcher:P2/P3|L
- 点击应用图标启动应用,Bug 复现
OK,这里我们看到的现象是,一个新的 Task 被创建了,并且新的页面立刻就销毁了,然后由于以 singleTask 启动主页面的时候发现已经存在了这个 Task,于是把原来的 Task 立刻带到了前台,并出栈了所有栈顶的页面:
- 启动:P2/P3|L -> P2/P3|L|P1
- 以 singleTask 启动新的 P2,退出 P1,将原有的 P2/P3 带到前台并将 P3 出栈:P2/P3|L|P1 -> L|P2
回想我们之前提到过的一个很重要的一点:如果创建 Task 的 Intent 和某个期望启动应用的 Intent 一模一样,那么该 Task 会被带到前台,否则会重新创建 Task。那么这里会不会是在这里出问题呢?
我仔细回想了 Bug 的复现步骤,发现在「5. 打开应用」这一步中遗漏了一个非常重要的细节:我们似乎是在安装完应用之后直接点击「打开应用」启动的应用 —— 问题就出在这里,「打开应用」对应的 Intent 和 Launcher 中的 Intent 其实是不一致的 —— 至少在某些方面是无法匹配的,那么通过这种方式启动的 Task 和点击 Launcher 图标启动的 Task 自然是无法相容了,出现 Bug 中的现象也就解释的通了。
为了验证想法的正确性,我再次重复了一次上述的步骤,只不过在第五步「5. 打开应用」中,并没有直接点击「打开应用」,而是退回到桌面,点击 Launcher 中应用的 icon 来启动应用。果然,这一次没有再发生之前的问题。
那么,我们该如何在应用中防止这种状况的发生呢?在网上寻求解答的时候发现了这篇文章,他也遇到了和我一样的问题,而他的解决思路则是在开屏页另外发起一个与 Launcher 相同的 Intent 请求启动应用。这个方法是否有效以及有没有副作用这里不做探讨也没有尝试过,如果有读者(哦我的文章没有读者)感兴趣的话,可以自己试一试嗯。
这是次很神奇可能也很微妙的 Bug 复现及追踪记录,这个问题并不算什么大问题,但是在追踪这个问题的过程中也让我对于 App 的启动方式有了新的理解,所以这里稍微记录一下,留以存档。