yuanyxh
yuanyxh
  • 发布:2023-12-03 23:44
  • 更新:2023-12-27 17:25
  • 阅读:1040

应用闪退分析与 uniapp 安卓原生插件开发

分类:uni-app

前言

公司使用 uniapp 开发的 App 端项目在红米 Note11T Pro 中出现了拍照后闪退的问题,也是折腾了挺久才研究出原因和解决方案,在这里记录和分享。

调试分析

首先可以肯定的是并非代码的问题,在调用拍摄 Api(uni.chooseImage)前、成功回调、失败回调中都打了断点,除了拍摄前的断点进入了,成功和失败回调是没有进入的,而且闪退只在少部分机型上出现,不是必现的行为。

在网上查了一些 uniapp 拍照闪退的相关资料,可以排除的原因有

  • 没有配置权限
  • 拍照像素过大,GPU 渲染崩溃
    • 按下拍摄键还没确认的时候应用其实就已经闪退了,也就不存在 GPU 渲染问题

既然前端代码没有问题,那我们就得往低一层去调试分析了,使用离线打包配置,在 as 中运行项目到真机并开启 logcat:

20231203154344

目前的日志有点多,包含了整个系统的日志输出,所以我们需要做个过滤:

20231203155600

过滤包名为 com.android.simple 且等级为 warnerror 的日志输出。完整的过滤语法看官网文档 使用 Logcat 查看日志

接下来我们需要在设备(真机、模拟器)上进行操作来复现闪退的行为,然后查找可疑的 crash 日志。

a51f4013-6f9a-4a80-a69e-10a88a34f338

这样操作了几次,虽然会复现闪退行为,但并没有找到相关的 crash 日志,得到的信息只有:按下拍摄键后应用进程被结束。

20231203173635

到这里信息基本算是断了,但是没有 crash 日志,闪退的可能性降低了,那是不是系统回收了应用资源呢?我们搜索对应的机型 + 关键词:

20231203174504

通过上面的两篇文章,我们可以发现一些共同点:

  • 调用系统相机进行拍摄
  • 拍摄时应用进入后台,此时可能会被系统回收资源

这两篇文章的问题与我们目前经历的很像,都是调用系统相机拍摄后应用被结束了进程,那么很大的可能是应用进入后台后被系统回收了资源。

如果是这样的话那我们需要降低应用进入后台后被系统回收资源的几率,也就是保活,为此找到了一篇 Android 保活相关的文章:

从这篇文章里知道了一个关键的信息:应用后台优先级 — oom_adj 值。

这个值是反映进程的优先级的,在系统内存不足时,会根据这个值去决定将哪些进程回收(kill),值越低表示优先级越高,越不可能被回收资源。常见的值有:

  • 0:前台进程,应用目前在前台运行
  • 1:可感知进程,比如播放音乐,通知栏有可交互的控件
  • 负数:属于系统级别的进程

这个值是根据你的应用状态实时变化的,可以通过以下命令查看你的应用 oom_adj 值:

- adb shell "ps|grep your package name"  

- adb shell  
- cat /proc/pid/oom_adj

前台

20231203181714

后台

20231203182001

可以发现什么也没做的话(如果开通了厂商提供的通知服务,如 MiPush,会开启一个专属的通知进程,可以提升应用的后台优先级,但是测试发现红米 Note11T Pro Android13 里这个通知进程也很容易被 kill)应用进入后台时的优先级是很低的,调用拍摄又是个消耗大量内存的行为,也就不奇怪会出现应用被回收资源的问题了。

uniapp Android 原生插件开发

既然知道了可能的原因,我们就需要有针对的去解决,这里我采用 Android实现进程保活方案解析 中的前台服务方案,开发一个 Android 原生插件,尝试提升应用的优先级。参考 uniapp 文档:

关于环境的配置就不过多介绍,需要的可以看下面这篇文章:

module 配置

我们先新建一个 module:

20231203184854

20231203185003

20231203185030

可能会出现报错,我们直接 cv uniapp Android SDK 中 richalert.gradle 配置并点击 Try Again

20231203185332

如果出现错误 package name not found,我们直接点击进入对应的文件:

20231203185630

添加自己的包名并再次尝试:

20231203185801

删除这两个文件:

20231203190230

新建一个 uniapp module 类供其调用:

20231203190442

20231203190548

根据文档,这个需要和 uniapp 打交道的类需要继承 UniModule:

20231203190821

我们可以定义在前端代码中调用的方法,这个方法需要加上 @UniJSMethod(uiThread = boolean) 注解,uiThread 标识是否运行在 ui 线程:

20231203191508

然后需要在 uniapp 的配置中配置这个类,并在主模块的 .gradle 配置中添加依赖,然后编译:

20231203191950

20231203192121

现在可以试试是否可以调用:

20231203192749

20231203193312

保活功能实现

新建一个类并继承自 Service:

20231203194231

在这个类中创建一个前台服务:

20231203202148

AndroidManifest.json 中注册服务并添加前台服务权限:

20231203202427

修改之前的 startForeground 方法,开启一个服务:

20231203202324

得到的效果如下:

edd3fa92-afe3-4c75-af14-39240c39e1d0

查看应用后台时的 oom_adj 值确实变小了,也没有再次测到拍照闪退的问题,现在可以肯定是系统回收资源导致的了:

20231203204159

可以使用手机的开发者选项,开启后台进程限制,选择最多不超过两个后台进程,然后开启你的应用和两个额外的应用,在这三个应用间切换,你会发现被杀死的总是另外两个应用,你的应用是一直存活的。

后话

本来到这里以为已经结束了,谁知道同事说以前做过保活功能,但是被应用商店给打回了,目前国内对后台运行、自启动、关联启动基本是 0 容忍。

也尝试过双进程守护等保活实现,但是现在的系统对于后台服务在后台运行超过一定时间后会直接杀死,可以说目前国内想实现保活基本是不可能了。

目前还没有更新发版,不确定这种轻量级的保活能不能审核通过;有大佬提供过一种思路:不调用系统相机,自定义拍照页面来完成拍照功能。这样应用还是在前台的,也就不会被系统杀死了。

最后附上另外一位热心大佬和我的沟通讨论:应用保活讨论

2023 年 12 月 4 日更新

尝试使用了插件市场中的 自定义相机 自动裁剪 自定义拍照 支持配置大小【更多自定义相机请联系作者】 插件,实测红米 Note11T Pro 未出现应用被系统资源回收的行为,且查看调用自定义相机时的应用 oom_adj 值为 0,即当前应用进程仍处于前台:

oom_adj

9 关注 分享
DCloud_heavensoft 1***@qq.com DCloud_UNI_OttoJi 小枫同学 DCloud_UNI_CHB 1***@qq.com dev_pz DCloud_UNI_HRK Sun_

要回复文章请先登录注册

1***@qq.com

1***@qq.com

希望能尽快解决
2023-12-27 17:25
DCloud_UNI_HRK

DCloud_UNI_HRK

分析的逻辑思路很棒,感谢你为DCLOUD社区做出的贡献
2023-12-27 12:01
Sun_

Sun_

回复 DCloud_heavensoft :
期待早日解决
2023-12-27 09:22
yuanyxh

yuanyxh (作者)

回复 dev_pz :
使用最新版本就可以
2023-12-25 09:33
dev_pz

dev_pz

android studio 是什么版本
2023-12-25 00:22
DCloud_heavensoft

DCloud_heavensoft

回复 yuanyxh :
感谢,我们后续更新版本解决
2023-12-04 16:59
yuanyxh

yuanyxh (作者)

回复 DCloud_heavensoft :
文章已经更新了,还是希望官方能够进行内置统一的解决,这个对很多项目影响还是挺大的。
2023-12-04 14:37
DCloud_heavensoft

DCloud_heavensoft

很专业的分析,点赞。插件市场有不少自定义拍照插件,可以快速试用一个测试下,如果见了成效,欢迎更新文章。官方也会持续跟踪这个问题,争取是内置产品中解决
2023-12-04 02:51