前言
公司使用 uniapp 开发的 App 端项目在红米 Note11T Pro 中出现了拍照后闪退的问题,也是折腾了挺久才研究出原因和解决方案,在这里记录和分享。
调试分析
首先可以肯定的是并非代码的问题,在调用拍摄 Api(uni.chooseImage)前、成功回调、失败回调中都打了断点,除了拍摄前的断点进入了,成功和失败回调是没有进入的,而且闪退只在少部分机型上出现,不是必现的行为。
在网上查了一些 uniapp 拍照闪退的相关资料,可以排除的原因有
- 没有配置权限
- 拍照像素过大,GPU 渲染崩溃
- 按下拍摄键还没确认的时候应用其实就已经闪退了,也就不存在 GPU 渲染问题
既然前端代码没有问题,那我们就得往低一层去调试分析了,使用离线打包配置,在 as 中运行项目到真机并开启 logcat:
目前的日志有点多,包含了整个系统的日志输出,所以我们需要做个过滤:
过滤包名为 com.android.simple
且等级为 warn
和 error
的日志输出。完整的过滤语法看官网文档 使用 Logcat 查看日志 。
接下来我们需要在设备(真机、模拟器)上进行操作来复现闪退的行为,然后查找可疑的 crash 日志。
这样操作了几次,虽然会复现闪退行为,但并没有找到相关的 crash 日志,得到的信息只有:按下拍摄键后应用进程被结束。
到这里信息基本算是断了,但是没有 crash 日志,闪退的可能性降低了,那是不是系统回收了应用资源呢?我们搜索对应的机型 + 关键词:
通过上面的两篇文章,我们可以发现一些共同点:
- 调用系统相机进行拍摄
- 拍摄时应用进入后台,此时可能会被系统回收资源
这两篇文章的问题与我们目前经历的很像,都是调用系统相机拍摄后应用被结束了进程,那么很大的可能是应用进入后台后被系统回收了资源。
如果是这样的话那我们需要降低应用进入后台后被系统回收资源的几率,也就是保活,为此找到了一篇 Android 保活相关的文章:
从这篇文章里知道了一个关键的信息:应用后台优先级 — oom_adj 值。
这个值是反映进程的优先级的,在系统内存不足时,会根据这个值去决定将哪些进程回收(kill),值越低表示优先级越高,越不可能被回收资源。常见的值有:
- 0:前台进程,应用目前在前台运行
- 1:可感知进程,比如播放音乐,通知栏有可交互的控件
- 负数:属于系统级别的进程
这个值是根据你的应用状态实时变化的,可以通过以下命令查看你的应用 oom_adj 值:
- adb shell "ps|grep your package name"
- adb shell
- cat /proc/pid/oom_adj
前台
后台
可以发现什么也没做的话(如果开通了厂商提供的通知服务,如 MiPush,会开启一个专属的通知进程,可以提升应用的后台优先级,但是测试发现红米 Note11T Pro Android13 里这个通知进程也很容易被 kill)应用进入后台时的优先级是很低的,调用拍摄又是个消耗大量内存的行为,也就不奇怪会出现应用被回收资源的问题了。
uniapp Android 原生插件开发
既然知道了可能的原因,我们就需要有针对的去解决,这里我采用 Android实现进程保活方案解析 中的前台服务方案,开发一个 Android 原生插件,尝试提升应用的优先级。参考 uniapp 文档:
关于环境的配置就不过多介绍,需要的可以看下面这篇文章:
module 配置
我们先新建一个 module:
可能会出现报错,我们直接 cv uniapp Android SDK 中 richalert
的 .gradle
配置并点击 Try Again
:
如果出现错误 package name not found
,我们直接点击进入对应的文件:
添加自己的包名并再次尝试:
删除这两个文件:
新建一个 uniapp module 类供其调用:
根据文档,这个需要和 uniapp 打交道的类需要继承 UniModule
:
我们可以定义在前端代码中调用的方法,这个方法需要加上 @UniJSMethod(uiThread = boolean)
注解,uiThread
标识是否运行在 ui 线程:
然后需要在 uniapp 的配置中配置这个类,并在主模块的 .gradle
配置中添加依赖,然后编译:
现在可以试试是否可以调用:
保活功能实现
新建一个类并继承自 Service
:
在这个类中创建一个前台服务:
在 AndroidManifest.json
中注册服务并添加前台服务权限:
修改之前的 startForeground
方法,开启一个服务:
得到的效果如下:
查看应用后台时的 oom_adj 值确实变小了,也没有再次测到拍照闪退的问题,现在可以肯定是系统回收资源导致的了:
可以使用手机的开发者选项,开启后台进程限制,选择最多不超过两个后台进程,然后开启你的应用和两个额外的应用,在这三个应用间切换,你会发现被杀死的总是另外两个应用,你的应用是一直存活的。
后话
本来到这里以为已经结束了,谁知道同事说以前做过保活功能,但是被应用商店给打回了,目前国内对后台运行、自启动、关联启动基本是 0 容忍。
也尝试过双进程守护等保活实现,但是现在的系统对于后台服务在后台运行超过一定时间后会直接杀死,可以说目前国内想实现保活基本是不可能了。
目前还没有更新发版,不确定这种轻量级的保活能不能审核通过;有大佬提供过一种思路:不调用系统相机,自定义拍照页面来完成拍照功能。这样应用还是在前台的,也就不会被系统杀死了。
最后附上另外一位热心大佬和我的沟通讨论:应用保活讨论
2023 年 12 月 4 日更新
尝试使用了插件市场中的 自定义相机 自动裁剪 自定义拍照 支持配置大小【更多自定义相机请联系作者】 插件,实测红米 Note11T Pro 未出现应用被系统资源回收的行为,且查看调用自定义相机时的应用 oom_adj 值为 0,即当前应用进程仍处于前台:
10 个评论
要回复文章请先登录或注册
DCloud_heavensoft
5***@qq.com
1***@qq.com
HRK_01
Sun_
yuanyxh (作者)
dev_pz
DCloud_heavensoft
yuanyxh (作者)
DCloud_heavensoft