写点什么

一个普通 App 变成 Launcher 的故事

作者:Changing Lin
  • 2022 年 4 月 30 日
  • 本文字数:2398 字

    阅读完需:约 8 分钟

一个普通App变成Launcher的故事

1.背景

  • 公司最近开发了一款通信 APP,对网络和稳定性的依赖比较高,为了避免应用在后台被回收,影响实时通信的功能效果,故决定做成一个 Launcher,运行在我们定制的定制设备系统上面。

  • 项目名称:BeSmart

  • 项目架构:组件化

  • 复杂程度:中级

  • 功能:通讯录、消息、富文本消息、多媒体消息、组呼、单呼、PTT 等。

  • 一个应用打包成 Launcher 的方法?主要是通过修改 AndroidManifest.xml 文件,来具备系统桌面的能力。

<application        android:name=".App"        android:allowBackup="true"        android:icon="@drawable/icon"        android:label="@string/home_app_name"        android:networkSecurityConfig="@xml/network_security_config"        android:requestLegacyExternalStorage="true"        android:roundIcon="@drawable/icon"        android:supportsRtl="true"        android:theme="@style/AppTheme"        tools:replace="android:label">               <activity // 表示 这是应用的启动页面,也就是 我们在系统桌面中,点击应用图标,打开应用的第一个页面            android:name=".SplashActivity"            android:screenOrientation="portrait">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>
<activity android:name=".HomeActivity" android:launchMode="singleTask" android:excludeFromRecents="false" android:noHistory="false" android:clearTaskOnLaunch="true" android:screenOrientation="portrait" > // 表示 既是应用的启动的第一个页面,当前应用具备桌面的能力,可以响应Home键的点击事件 <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <activity android:name=".TestActivity"/>
</application>
复制代码

2.问题

  • 测试发现,非 Launcher 版本的功能比较稳定,基础业务都是运行正常的。但是编译成 Launcher 版本后,发现基础业务容易出现问题,比如多点登陆竞争失效、同一个时刻存在两个心跳、用户未知原因自动退出登录、异常崩溃。

  • 经过捕捉日志和对现象的分析,我们发现 出现问题的时候,往往存在多个 HomeActivity 实例或多个任务栈。而我们的部分启动顺序是放在该页面的 onCreate 去执行的,也就意味着 2 个实例之间存在互相影响,造成功能异常。

  • 但这种现在在非 Launcher 版本是没有出现过的。所以,我们初步怀疑,Launcher 版本的修改引入的,当然也与系统对不同类型应用的启动流程不同有关。

3.分析

  • 分享一个查看当前应用 activity、stack 的命令:

# adb shell dumpsys activity activities | sed -En -e '/Running activities/,/Run #0/p'
复制代码
  • 问题很清晰,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,从而解决多个实例的问题。

Intent intent = new Intent();intent.setClassName(this, "com.x.x.HomeActivity");intent.putExtras(bundle);startActivity(intent);====> 修改成下面Intent intent = new Intent(Intent.ACTION_MAIN);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.addCategory(Intent.CATEGORY_HOME);startActivity(intent);
复制代码

4.总结

  • 普通的 Launcher 都是单页应用,也就意味着打开 Home 页只有系统打开一个入口,Launcher 本身不会去改变 Home 页的生命周期,同时,Launcher 一般是打开其他应用,也是无状态的。

  • 由于我们的 APP 是多页应用,会对 Home 页的生命周期进行管理,开发成 Launcher 后,Home 页就会响应系统的 Home 键,又加上 Home 键打开的页面是在一个特殊的任务栈里面,所以,导致了在 APP 运行过程中存在 2 个 Home 页的情况。

  • 写这篇文章的原因是想记录下近日的工作经历,技术总结与心理变化过程。

发布于: 刚刚阅读数: 2
用户头像

Changing Lin

关注

获得机遇的手段远超于固有常规之上~ 2020.04.29 加入

我能做的,就是调整好自己的精神状态,以最佳的面貌去面对那些未曾经历过得事情,对生活充满热情和希望。

评论

发布
暂无评论
一个普通App变成Launcher的故事_android_Changing Lin_InfoQ写作社区