最近在处理Crash问题总结了一些心得,这里记录下。
Crash问题处理流程
Unity App Crash问题主要分为几类:
- 系统资源(包括内存,CPU等)
- 业务逻辑(主要是死循环这类,因为是C#写的没有空指针这种问题,Unity为数不多的有点)
- 引擎自己的代码逻辑(空指针,死循环等)
- 第三方SDK造成(这类最难查)
- 其他原因
针对不同平台,我们定位Crash需要的数据信息也不一样:
- Android: adb logcat日志 + so符号文件
- iOS:ips日志 + dSYM符号文件
- Window:dump日志 + pdb符号文件
可以看到无论哪个平台都需要符号文件,所以如果是标准化流程的话,构建包的流水线每次都应该生成符号文件,并且上传到云服务器,保证每个包都能取到对应的符号文件。另外这些平台都需要提供崩溃日志,但是如果是外网玩家出现的取不到日志的情况呢?一个实用的Crash工具CrashSight,App继承了CrashSight之后能够捕获到发生的Crash日志并且上报的服务器,我们只需要把包的符号文件上传到CrashSight服务器,CrashSight能够把每个上报的Crash堆栈自动找到对应的符号文件还原成符号化的堆栈。整个过程是这样的:
没有用CrashSight的可以按照这个流程来构建一个自己项目的解决Crash问题工作流。另外客户端发生的Crash问题 CrashSight 并不是每次都能捕获到的,其他工具也是如此。这种情况我们只能通过内存的适配和功能测试,有案例发生时,我们拿到日志文件首先符号化来还原堆栈,并且定位问题。CrashSight用户端的页面提供了一个符号化的工具,没有接入CrashSight的话我们也可以用自己做的工具。
说完了整体处理流程,再看下一些具体的Crash处理经验。
常见Crash问题具体案例
OOM (Out Of Memory)
oom和字面意思一样,就是系统内存不够或者给定当前的app的内存超出上限了,然后分配不到内存Crash了。
iOS系统有Jetsam机制,当内存不够时Jetsam会终止App,并且会生成Jetsam的ips日志,日志名字一般为 JetsamEventxxx.ips。所以当Crash时我们发现系统生成了Jetsam ips日志时,基本上可以判断是由于oom造成的。Jetsam日志里面会记录下当前App所有进程分配的内存情况,日志文件里我们可以看到如下内容(省略了很多内容的):
1 |
|
算出来内存大小可以帮助反馈App的内存性能状况,来做内存优化的参考。
Android系统在App内存过高时终止进程也会输出日志信息(Android系统各个厂商都有自己的定制,不一样每台手机每次都会生成,遇到过低端设备疑似没有生成的情况),Android系统的日志是直接输出的系统的调试Log中,我们可以通过 adb工具拿到。存储系统的日志本身是个循环利用的一块内存,我们需要在Crash发生之后就拿日志,否则系统如果继续运行最后存储Crash日志的那块内存会被重复利用,日志信息会被覆盖。
所以当测试同学遇到Crash问题,第一个动作就是通过adb工具获取log。几个常用的adb命令行:
1 |
|
拿到日志之后,可以直接在日志里面搜索关键字 lowmemorykiller 或者 kill,找到自己App的包名:
如果能找到自己包名的 lowmemorykiller (不同设备可能这个关键字不一样)可以表明就是oom问题了。
ANR(Android Not Responding)
ANR问题是安卓的特有问题,Android系统中,ActivityManagerServer(简称AMS) 和 WindowManagerServie(简称WMS)会检测App的响应时间,如果App在特定时间无法响应屏幕触摸或者键盘输入,或者特定事件没有处理完毕,就会触发ANR。出现以下任何情况时,系统都会针对您的应用触发 ANR:
- Input dispatching timed out:如果您的应用在 5 秒内未响应输入事件(例如按键或屏幕触摸)。
- Executing service::如果应用声明的服务无法在几秒内完成
Service.onCreate()
和Service.onStartCommand()
/Service.onBind()
执行。 - Service.startForeground() not called:如果您的应用使用
Context.startForegroundService()
在前台启动新服务,但该服务在 5 秒内未调用startForeground()
。 - Broadcast of intent:如果
BroadcastReceiver
在设定的一段时间内没有执行完毕。如果应用有任何前台 activity,此超时期限为 5 秒。 - JobScheduler interactions:如果
JobService
未在几秒钟内从JobService.onStartJob()
或JobService.onStopJob()
返回,或者如果user-initiaed job启动,而您的应用在调用JobService.onStartJob()
后的几秒内未调用JobService.setNotification()
。对于以 Android 13 及更低版本为目标平台的应用,ANR 会保持静默状态,且不会报告给应用。对于以 Android 14 及更高版本为目标平台的应用,ANR 会保持活动状态,并会报告给应用。
发生ANR拿到logcat日志之后,可以在日志里面看到:
这里是5秒钟未响应事件,对于Unity的App来说其实没有给到明确的原因,发生这种情况一般是游戏的线程卡住了,导致未响应。此时我们需要根据Unity日志里面匹配时间点来看我们在处理什么逻辑导致卡住的(或者是App里面接入的SDK卡住的)。
后台限制
游戏运行过程中,有时候会被动切换到后台的情况,遇到几种常见的:
- 登陆的时候切换到QQ确认,此时游戏会被被切换到后台。
- 活动界面使用的H5做的(UniWebView) ,切换到H5界面时游戏会被切换到后台。
- 系统权限提示框弹出来时,游戏也会被切换到后台。
Android系统对后台运行的App限制比较严格,当然不同的定制Android系统处理规则又不一样。比如这段时我最近遇到的,这是一台6GBRAM的手机出现Crash的日志:
可以看到尽管这台手机有6GB的RAM,但是切换到后台10秒钟之后就被系统终止掉了,这种情况我们只能通过系统保活方案来处理。
业务逻辑
业务逻辑造成的,有日志的话看Crash堆栈可以定位到具体代码,这个方式很明确,不需要多讲。
引用: