通过这个异常退出的检测,可以反映如 ANR、low memory killer、系统强杀、死机、断电等其他无法正常捕获到的问题。当然异常率会存在一些误报,比如用户从系统的任务管理器中划掉应用。对于线上的大数据来说,还是可以帮助我们发现代码中的一些隐藏问题。
根据应用的前后台状态,我们可以把异常退出分为前台异常退出和后台异常退出。“被系统杀死” 是后台异常退出的主要原因,当然我们会更关注前台的异常退出的情况,这会跟 ANR、OOM 等异常情况有更大的关联。
二、崩溃处理
我们每天工作也会遇到各种各样的疑难问题,“崩溃”就是其中比较常见的一种问题。解决问题跟破案一样需要经验,我们分析的问题越多越熟练,定位问题就会越快越准。当然这里也有很多套路,比如对于 “案发现场” 我们应该留意哪些信息?怎样找到更多的 “证人” 和 “线索” ? “侦查案件” 的一般流程是什么?对不同类型的 “案件” 分别应该使用什么样的调查方式?
要相信 “真相永远只有一个”,崩溃也并不可怕。
2.1 崩溃现场
崩溃现场是我们的“第一案发现场”,它保留着很多有价值的线索。现在可以挖掘到的信息越多,下一步分析的方向就越清晰,而不是去靠盲目猜测。
崩溃信息
从崩溃的基本信息,我们可以对崩溃有初步的判断。进程名、线程名。崩溃的进程是前台进程还是后台进程,崩溃是不是发生在 UI 线程。
崩溃堆栈和类型。崩溃是属于 Java 崩溃、Native 崩溃,还是 ANR,对于不同类型的崩溃关注的点也不太一样。特别需要看崩溃堆栈的栈顶,看具体崩溃在系统的代码,还是 APP 代码里面。
关键字:FATAL
FATAL EXCEPTION: main
Process: com.cchip.csmart, PID: 27456
java.lang.NullPointerException: Attempt to invoke virtual method ‘void android.widget.TextView.setText(int)’ on a null object reference
at com.cchip.alicsmart.activity.SplashActivity
1.
h
a
n
d
l
e
M
e
s
s
a
g
e
(
S
p
l
a
s
h
A
c
t
i
v
i
t
y
.
j
a
v
a
:
67
)
a
t
a
n
d
r
o
i
d
.
o
s
.
H
a
n
d
l
e
r
.
d
i
s
p
a
t
c
h
M
e
s
s
a
g
e
(
H
a
n
d
l
e
r
.
j
a
v
a
:
102
)
a
t
a
n
d
r
o
i
d
.
o
s
.
L
o
o
p
e
r
.
l
o
o
p
(
L
o
o
p
e
r
.
j
a
v
a
:
179
)
a
t
a
n
d
r
o
i
d
.
a
p
p
.
A
c
t
i
v
i
t
y
T
h
r
e
a
d
.
m
a
i
n
(
A
c
t
i
v
i
t
y
T
h
r
e
a
d
.
j
a
v
a
:
5672
)
a
t
j
a
v
a
.
l
a
n
g
.
r
e
f
l
e
c
t
.
M
e
t
h
o
d
.
i
n
v
o
k
e
(
N
a
t
i
v
e
M
e
t
h
o
d
)
a
t
c
o
m
.
a
n
d
r
o
i
d
.
i
n
t
e
r
n
a
l
.
o
s
.
Z
y
g
o
t
e
I
n
i
t
1.handleMessage(SplashActivity.java:67) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:179) at android.app.ActivityThread.main(ActivityThread.java:5672) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit
1.handleMessage(SplashActivity.java:67)atandroid.os.Handler.dispatchMessage(Handler.java:102)atandroid.os.Looper.loop(Looper.java:179)atandroid.app.ActivityThread.main(ActivityThread.java:5672)atjava.lang.reflect.Method.invoke(NativeMethod)atcom.android.internal.os.ZygoteInitMethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:674)
系统信息
系统的信息有时候会带有一些关键的线索,对我们解决问题有非常大的帮助。
Logcat。这里包括应用、系统的运行日志。由于系统权限问题,获取到的 Logcat 可能只包含与当前 APP 相关的。其中系统的 event logcat 会记录 APP 运行的一些基本情况,记录在文件 /system/etc/event-log-tags 中。
//system logcat:
10-25 17:13:47.788 21430 21430 D dalvikvm: Trying to load lib …
//event logcat:
10-25 17:13:47.788 21430 21430 I am_on_resume_called: 生命周期
10-25 17:13:47.788 21430 21430 I am_low_memory: 系统内存不足
10-25 17:13:47.788 21430 21430 I am_destroy_activity: 销毁 Activty
10-25 17:13:47.888 21430 21430 I am_anr: ANR 以及原因
10-25 17:13:47.888 21430 21430 I am_kill: APP 被杀以及原因
机型、系统、厂商、CPU、ABI、Linux 版本等。通过采集多达几十个维度,这对寻找共性问题会很有帮助。
内存信息
OOM、ANR、虚拟内存耗尽等,很多崩溃都跟内存有直接关系。如果把用户的手机内存分为“2GB 以下”和“2GB 以上”两个区,就会发现“2GB 以下”用户的崩溃率是“2GB 以上”用户的几倍。
系统剩余内存。关于系统内存状态,可以直接读取文件 /proc/meminfo。当系统可用内存很小(低于 MemTotal 的 10%)时,OOM、大量 GC、系统频繁自杀拉起等问题都非常容易出现。
应用使用内存。包括 Java 内存、RSS(Resident Set Size)、PSS(Proportional Set Size),我们可以得出应用本身内存的占用大小和分布。PSS 和 RSS 通过 /proc/self/smap 计算,可以进一步得到例如 apk、dex、so 等更加详细的分类统计。
虚拟内存。虚拟内存可以通过 /proc/self/status 得到,通过 /proc/self/maps 文件可以得到具体的分布情况。有时候我们一般不太重视虚拟内存,但是很多类似 OOM、tgkill 等问题都是虚拟内存不足导致的。
Name: com.xmamiga.name // 进程名
FDSize: 800 // 当前进程申请的文件句柄个数
VmPeak: 3004628 kB // 当前进程的虚拟内存峰值大小
VmSize: 2997032 kB // 当前进程的虚拟内存大小
Threads: 600 // 当前进程包含的线程个数
一般来说,对于 32 位进程,如果是 32 位的 CPU,虚拟内存达到 3GB 就可能会引起内存申请失败的问题。如果是 64 位的 CPU,虚拟内存一般在 3~4GB 之间。当然如果我们支持 64 位进程,虚拟内存就不会成为问题。Google Play 要求 2019 年 8 月一定要支持 64 位,在国内虽然支持 64 位的设备已经在 90% 以上了,但是商店都不支持区分 CPU 架构类型发布,普及起来需要更长的时间。
资源信息
有的时候会发现应用堆内存和设备内存都非常充足,还是会出现内存分配失败的情况,这跟资源泄漏可能有比较大的关系。
文件句柄 fd。文件句柄的限制可以通过 /proc/self/limits 获得,一般单个进程允许打开的最大文件句柄个数为 1024。但是如果文件句柄超过 800 个就比较危险,需要将所有的 fd 以及对应的文件名输出到日志中,进一步排查是否出现了有文件或者线程的泄漏。
opened files count 812:
0 -> /dev/null
1 -> /dev/log/main4
2 -> /dev/binder
3 -> /data/data/com.xmamiga.sample/files/test.config
…
线程数。当前线程数大小可以通过上面的 status 文件得到,一个线程可能就占 2MB 的虚拟内存,过多的线程会对虚拟内存和文件句柄带来压力。根据我的经验来说,如果线程数超过 400 个就比较危险。需要将所有的线程 id 以及对应的线程名输出到日志中,进一步排查是否出现了线程相关的问题。
threads count 412:
1820 com.xmamiga.crashsdk
1844 ReferenceQueueD
1869 FinalizerDaemon
…
JNI。使用 JNI 时,如果不注意很容易出现引用失效、引用爆表等一些崩溃。
应用信息
除了系统,其实我们的应用更懂自己,可以留下很多相关的信息。崩溃场景。崩溃发生在哪个 Activity 或 Fragment,发生在哪个业务中; 关键操作路径,不同于开发过程详细的打点日志,我们可以记录关键的用户操作路径,这对我们复现崩溃会有比较大的帮助。其他自定义信息。不同的应用关心的重点可能不太一样。
2.2 崩溃分析
有了这么多现场信息之后,就可以开始真正的“破案”之旅了。绝大部分的 “案件” 只要肯花功夫,最后都能真相大白。不要畏惧问题,经过耐心和细心地分析,总能敏锐地发现一些异常或关键点,并且还要敢于怀疑和验证。
第一步:确定重点
确认和分析重点,关键在于终过日志中找到重要的信息,对问题有一个大致判断。一般来说,我建议在确定重点这一步可以关注以下几点。
- 确认严重程度。解决崩溃也要看性价比,我们优先解决 Top 崩溃或者对业务有重大影响,例如主要功能的崩溃。不要花几天去解决了一个边角的崩溃,有可能下个版本就把功能删除了。
- 崩溃基本信息。确定崩溃的类型以及异常描述,对崩溃有大致的判断。
一般来说,大部分的简单崩溃经过这一步已经可以得到结论。
Java 崩溃。Java 崩溃类型比较明显,比如 NullPointerException 是空指针,OutOfMemoryError 是资源不足,这个时候需要去进一步查看日志中的 “内存信息”和“资源信息”。
Native 崩溃。需要观察 signal、code、fault addr 等内容,以及崩溃时 Java 的堆栈。关于各 signal 含义的介绍,你可以查看崩溃信号介绍。比较常见的是有 SIGSEGV 和 SIGABRT,前者一般是由于空指针、非法指针造成,后者主要因为 ANR 和调用 abort() 退出所导致。
ANR。先看看主线程的堆栈,是否是因为锁等待导致。接着看看 ANR 日志中 iowait、CPU、GC、system server 等信息,进一步确定是 I/O 问题,或是 CPU 竞争问题,还是由于大量 GC 导致卡死。
第二步:查找共性
如果使用了上面的方法还是不能有效定位问题,我们可以尝试查找这类崩溃有没有什么共性。找到了共性,也就可以进一步找到差异,离解决问题也就更进一步。
机型、系统、ROM、厂商、ABI,这些采集到的系统信息都可以作为维度聚合,共性问题例如是不是只出现在 x86 的手机,是不是只有三星这款机型,是不是只在 Android 8.0 的系统上。应用信息也可以作为维度来聚合,比如正在打开的链接、正在播放的视频、国家、地区等。
找到了共性,可以对你下一步复现问题有更明确的指引。
第三步:尝试复现
如果我们已经大概知道了崩溃的原因,为了进一步确认更多信息,就需要尝试复现崩溃。如果我们对崩溃完全没有头绪,也希望通过用户操作路径来尝试重现,然后再去分析崩溃原因。
“只要能本地复现,我就能解”,相信这是很多开发跟测试说过的话。有这样的底气主要是因为在稳定的复现路径上面,我们可以采用增加日志或使用 Debugger、GDB 等各种各样的手段或工具做进一步分析。
我们可能会遇到了各种各样的奇葩问题。比如某个厂商改了底层实现、新的 Android 系统实现有所更改,都需要去 Google、翻源码,有时候还需要去抠厂商的 ROM 或手动刷 ROM。很多疑难问题需要我们耐得住寂寞,反复猜测、反复发灰度、反复验证。–但这种问题还是要看问题的严重程序,不可捡了芝麻丢了西瓜。
2.3 系统崩溃
系统崩溃常常令我们感到非常无助,它可能是某个 Android 版本的 Bug,也可能是某个厂商修改 ROM 导致。这种情况下的崩溃堆栈可能完全没有我们自己的代码,很难直接定位问题。能做的有:
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!