一个普通 App 变成 Launcher 的故事
![一个普通App变成Launcher的故事](https://static001.geekbang.org/infoq/31/31ced0e31e5800a5422da5f04ce96322.jpeg)
1.背景
公司最近开发了一款通信 APP,对网络和稳定性的依赖比较高,为了避免应用在后台被回收,影响实时通信的功能效果,故决定做成一个 Launcher,运行在我们定制的定制设备系统上面。
项目名称:BeSmart
项目架构:组件化
复杂程度:中级
功能:通讯录、消息、富文本消息、多媒体消息、组呼、单呼、PTT 等。
一个应用打包成 Launcher 的方法?主要是通过修改 AndroidManifest.xml 文件,来具备系统桌面的能力。
2.问题
测试发现,非 Launcher 版本的功能比较稳定,基础业务都是运行正常的。但是编译成 Launcher 版本后,发现基础业务容易出现问题,比如多点登陆竞争失效、同一个时刻存在两个心跳、用户未知原因自动退出登录、异常崩溃。
经过捕捉日志和对现象的分析,我们发现 出现问题的时候,往往存在多个 HomeActivity 实例或多个任务栈。而我们的部分启动顺序是放在该页面的 onCreate 去执行的,也就意味着 2 个实例之间存在互相影响,造成功能异常。
但这种现在在非 Launcher 版本是没有出现过的。所以,我们初步怀疑,Launcher 版本的修改引入的,当然也与系统对不同类型应用的启动流程不同有关。
3.分析
分享一个查看当前应用 activity、stack 的命令:
问题很清晰,launcher 版本带来的任务栈混乱问题,思路是,新建一个组件,开发单任务栈模式的应用,来解决任务栈混乱的问题。
系统什么时候 新建一个任务栈并把首页放进去,是没有规律的。当存在 2 个任务栈,且每个任务栈维护自己的首页的时候,就会出现问题。最后找到原因是,APP 内部打开了 HomeActivity,同时,系统也会自己检索 StackId 为 0 的栈里面,是否存在 HomeActivity,如果不存在就会去启动它。解决方法也很简单,应用内部不去启动首页,把启动首页的任务交给系统,统一启动首页的流程和入口。这样就可避免出现问题
原因:由于 APP 原先是普通的非 Launcher 应用,启动的第一个页面是 SplashActivity,然后才进入 HomeActivity。点击系统 Home 键会返回系统设置的桌面。当修改成 Launcher 后,也就是把 HomeActivity 作为了系统桌面,按 Home 键后系统会帮我们打开 HomeActivity;而我们 APP 的跳转流程里面也有打开 HomeActivity 的操作。也就意味着,存在 2 个打开 HomeActivity 的触发点,理论上来说,设置页面的 launchMode 为 singleTask,系统会检测当前任务栈里面是否存在该页面,如果存在会自动把它从栈底部移动到顶部,也就只会存在一个实例。但,由于 Home 键打开的应用是在一个 StackId 为 0 的栈里面,而其他应用打开的栈是在其他 StackId 里面,如:StackId=1 里面。这是导致出现 2 个实例的原因。
解决方法:在清楚了出现 2 个首页的原因之后,处理方法也很简单,就是确保首页统一一个启动入口,避免把 HomeActivity 放置到不同任务栈产生 2 个对象;即使用打开系统桌面的方法,在 APP 中需要跳转到 HomeActivity 的地方,让系统帮我们打开 HomeActivity,从而解决多个实例的问题。
4.总结
普通的 Launcher 都是单页应用,也就意味着打开 Home 页只有系统打开一个入口,Launcher 本身不会去改变 Home 页的生命周期,同时,Launcher 一般是打开其他应用,也是无状态的。
由于我们的 APP 是多页应用,会对 Home 页的生命周期进行管理,开发成 Launcher 后,Home 页就会响应系统的 Home 键,又加上 Home 键打开的页面是在一个特殊的任务栈里面,所以,导致了在 APP 运行过程中存在 2 个 Home 页的情况。
写这篇文章的原因是想记录下近日的工作经历,技术总结与心理变化过程。
版权声明: 本文为 InfoQ 作者【Changing Lin】的原创文章。
原文链接:【http://xie.infoq.cn/article/862adf5d9642d27a4c025d267】。文章转载请联系作者。
评论