Android开发高手课
张绍文
前微信高级工程师,Tinker负责人
立即订阅
12609 人已学习
课程目录
已完结 61 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 焦虑的移动开发者该如何破局?
免费
导读 (1讲)
导读 | 如何打造高质量的应用?
模块一 高质量开发 (25讲)
01 | 崩溃优化(上):关于“崩溃”那些事儿
02 | 崩溃优化(下):应用崩溃了,你应该如何去分析?
03 | 内存优化(上):4GB内存时代,再谈内存优化
04 | 内存优化(下):内存优化这件事,应该从哪里着手?
05 | 卡顿优化(上):你要掌握的卡顿分析方法
06 | 卡顿优化(下):如何监控应用卡顿?
06补充篇 | 卡顿优化:卡顿现场与卡顿分析
07 | 启动优化(上):从启动过程看启动速度优化
08 | 启动优化(下):优化启动速度的进阶方法
09 | I/O优化(上):开发工程师必备的I/O优化知识
10 | I/O优化(中):不同I/O方式的使用场景是什么?
11 | I/O优化(下):如何监控线上I/O操作?
12 | 存储优化(上):常见的数据存储方法有哪些?
13 | 存储优化(中):如何优化数据存储?
14 | 存储优化(下):数据库SQLite的使用和优化
15 | 网络优化(上):移动开发工程师必备的网络优化知识
16 | 网络优化(中):复杂多变的移动网络该如何优化?
17 | 网络优化(下):大数据下网络该如何监控?
18 | 耗电优化(上):从电量优化的演进看耗电分析
19 | 耗电优化(下):耗电的优化方法与线上监控
20 | UI 优化(上):UI 渲染的几个关键概念
21 | UI 优化(下):如何优化 UI 渲染?
22 | 包体积优化(上):如何减少安装包大小?
23 | 包体积优化(下):资源优化的进阶实践
24 | 想成为Android高手,你需要先搞定这三个问题
模块二 高效开发 (9讲)
25 | 如何提升组织与个人的研发效能?
26 | 关于编译,你需要了解什么?
27 | 编译插桩的三种方法:AspectJ、ASM、ReDex
28 | 大数据与AI,如何高效地测试?
29 | 从每月到每天,如何给版本发布提速?
30 | 数据评估(上):如何实现高可用的上报组件?
31 | 数据评估(下):什么是大数据平台?
32 | 线上疑难问题该如何排查和跟踪?
33 | 做一名有高度的移动开发工程师
模块三 架构演进 (9讲)
34 | 聊聊重构:优秀的架构都是演进而来的
35 | Native Hook 技术,天使还是魔鬼?
36 | 跨平台开发的现状与应用
37 | 移动开发新大陆:工作三年半,移动开发转型手游开发
38 | 移动开发新大陆:Android音视频开发
39 | 移动开发新大陆: 边缘智能计算的趋势
40 | 动态化实践,如何选择适合自己的方案?
41 | 聊聊Flutter,面对层出不穷的新技术该如何跟进?
42 | Android开发高手课学习心得
练习Sample跑起来 (8讲)
练习Sample跑起来 | 热点问题答疑第1期
练习Sample跑起来 | 热点问题答疑第2期
练习Sample跑起来 | 热点问题答疑第3期
练习Sample跑起来 | 热点问题答疑第4期
练习Sample跑起来 | ASM插桩强化练习
练习Sample跑起来 | 唯鹿同学的练习手记 第1辑
练习Sample跑起来 | 唯鹿同学的练习手记 第2辑
练习Sample跑起来 | 唯鹿同学的练习手记 第3辑
特别放送 (7讲)
Android JVM TI机制详解(内含福利彩蛋)
专栏学得苦?可能是方法没找对
专栏学得苦?可能你还需要一份配套学习书单
Native下如何获取调用栈?
聊聊Framework的学习方法
Android工程师的“面试指南”
程序员修炼之路 | 设计能力的提升途径
结束语 (1讲)
结束语 | 移动开发的今天和明天
Android开发高手课
登录|注册

02 | 崩溃优化(下):应用崩溃了,你应该如何去分析?

张绍文 2018-12-04
在侦探漫画《名侦探柯南》中,无论柯南走到哪里都会遇到新的“案件”,这也很像程序员的“日常”,我们每天工作也会遇到各种各样的疑难问题,“崩溃”就是其中比较常见的一种问题。
解决崩溃跟破案一样需要经验,我们分析的问题越多越熟练,定位问题就会越快越准。当然这里也有很多套路,比如对于“案发现场”我们应该留意哪些信息?怎样找到更多的“证人”和“线索”?“侦查案件”的一般流程是什么?对不同类型的“案件”分别应该使用什么样的调查方式?
“真相永远只有一个”,崩溃也并不可怕。通过今天的学习,希望你能成为代码届的名侦探柯南。

崩溃现场

崩溃现场是我们的“第一案发现场”,它保留着很多有价值的线索。在这里我们挖掘到的信息越多,下一步分析的方向就越清晰,而不是去靠盲目猜测。
操作系统是整个崩溃过程的“旁观者”,也是我们最重要的“证人”。一个好的崩溃捕获工具知道应该采集哪些系统信息,也知道在什么场景要深入挖掘哪些内容,从而可以更好地帮助我们解决问题。
接下来我们具体来看看在崩溃现场应该采集哪些信息。
1. 崩溃信息
从崩溃的基本信息,我们可以对崩溃有初步的判断。
进程名、线程名。崩溃的进程是前台进程还是后台进程,崩溃是不是发生在 UI 线程。
崩溃堆栈和类型。崩溃是属于 Java 崩溃、Native 崩溃,还是 ANR,对于不同类型的崩溃我们关注的点也不太一样。特别需要看崩溃堆栈的栈顶,看具体崩溃在系统的代码,还是我们自己的代码里面。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Android开发高手课》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(82)

  • 张绍文 置顶
    补充一下获得logcat和Jave堆栈的方法:
    一. 获取logcat
    logcat日志流程是这样的,应用层 --> liblog.so --> logd,底层使用ring buffer来存储数据。
    获取的方式有以下三种:
       1. 通过logcat命令获取。
       优点:非常简单,兼容性好。
       缺点:整个链路比较长,可控性差,失败率高,特别是堆破坏或者堆内存不足时,基本会失败。
       2. hook liblog.so实现。通过hook liblog.so 中__android_log_buf_write 方法,将内容重定向到自己的buffer中。
       优点:简单,兼容性相对还好。
       缺点:要一直打开。
       3. 自定义获取代码。通过移植底层获取logcat的实现,通过socket直接跟logd交互。
       优点:比较灵活,预先分配好资源,成功率也比较高。
       缺点:实现非常复杂

    二. 获取Java 堆栈
       native崩溃时,通过unwind只能拿到Native堆栈。我们希望可以拿到当时各个线程的Java堆栈
       1. Thread.getAllStackTraces()。
        优点:简单,兼容性好。
        缺点:
            a. 成功率不高,依靠系统接口在极端情况也会失败。
            b. 7.0之后这个接口是没有主线程堆栈。
            c. 使用Java层的接口需要暂停线程
       2. hook libart.so。通过hook ThreadList和Thread的函数,获得跟ANR一样的堆栈。为了稳定性,我们会在fork子进程执行。
       优点:信息很全,基本跟ANR的日志一样,有native线程状态,锁信息等等。
       缺点:黑科技的兼容性问题,失败时可以用Thread.getAllStackTraces()兜底

    获取Java堆栈的方法还可以用在卡顿时,因为使用fork进程,所以可以做到完全不卡主进程。这块我们在后面会详细的去讲。
    2018-12-04
    1
    63
  • 小龙虾
    老师可能还不了解学生的水平,读者都是做应用层程序的你讲这么深入恐怕没什么效果

    作者回复: 不会的,移动开发已经十年了,已经不是单独写个界面就可以了。应用层开发当你深入到后面,这些知识都是必须的。当你知道了更多,学习的更多深入的时候,你才能做一些别人做不了的东西。

    2018-12-04
    45
  • Stefan
    回答github上的问题:
    通过对比Android 6.0前后的源码发现,FinalizerWatchdogDaemon调用的是父类Deamon中的stop方法,区别在于:
    //6.0之前
    ...
    threadToStop.interrupt();
    ...
    //>=6.0
    ...
    interrupt(threadToStop);
    ...
    public synchronized void interrupt(Thread thread) {
        if (thread == null) {
            throw new IllegalStateException("not running");
        }
        thread.interrupt();
    }
    ...
    可以发现区别在于6.0之前调用threadToStop的interrupt方法是没有加锁的,所以可能会有线程同步的问题。
    PS:推荐一个在线看源码的网站,包括1.6到9.0全部的源码:http://androidxref.com/
    2018-12-07
    2
    17
  • arvinljw
    张老师好,我想问一下,感觉学习这些课有点理解不了,不知道为什么,应该怎么入手呢?

    作者回复: 坚持,可能跟着学习完整个课程再回头看会有不一样的感觉

    2018-12-04
    15
  • HI
    貌似目前NDK中对于错误的检查还不够完善,比如对于函数的返回值,写了一个函数本身要写返回值,后面忘记写了发现IDE没有提示,并且能编译通过,但是在运行的时候会出现崩溃,而且崩溃的信息非常少,貌似就记得最后一行signal 6 还是 signal -1,当时的我看到这个是非常蒙逼的,大量的回退代码,添加日志,勉强找到,要是早点知道signal ,拿着对应的code对比,可能没那么痛苦
    2018-12-04
    12
  • Find
    上一篇运行出啦后还没有达到最终效果,下一篇都出来了,Android开发5年都是应用层开发,突然一下这么深入感觉很难适应,真心建议由浅到深讲解下,针对例子可以讲解一下。
    2018-12-05
    11
  • 尛鬼
    文哥,对于大多在应用层上开发的程序员来说,涉及到native的代码难度高了好多,其实也不是我们不想学,很多时候是因为没有这样的环境锻炼我们,再多的自学,没有一个真正项目的磨炼,这些知识很难巩固和加深,缺少关键性的实战经验,我自学过C/C++好几个月,但是没有任何一个项目可以让我实践学习,没有一个项目告诉我的代码是否有问题,是否有性能问题等等,就是因为这样,让我不知道该如何学习,该向哪个方向进阶,文哥,可以指导一下么?
    2018-12-28
    1
    7
  • O_o
    感觉可以由浅入深的讲,这一脚下去有点深呀,可能很多人3,4年经验,没做过这方面,学完了这一章一脸蒙圈,也没太大帮助呀

    作者回复: 一脸懵逼没有关系,我们可以跟着文中提到的一些文件,去学习到linux的一些基础知识。这些文件记录了一些什么样的内容,对排查问题有什么帮助,怎么在应用获取它们。

    后面的内容会从浅入深,我们学习完整个专栏再回头看应该有不同的感觉

    2018-12-05
    7
  • Kenny
    张老师,看到你解决一些棘手的崩溃,会去翻看源码,然后对比版本的差异,然后寻找hook点去解决,这个要考虑机型适配吧?毕竟国产机型rom差异化严重。

    作者回复: Hook的时候,前几个版本一般会把hook失败的机型 rom 堆栈传上来做进一步分析。top的机型也会保证可以兼容

    2018-12-04
    5
  • mzh
    之前遇到过一个输入法的内存泄露,但是自己完全没有调用过输入法,后面查了一下 网上说是系统的一个bug 张老师 这种应该怎么解决呢?

    作者回复: 那个问题很经典,解决方法就是反射将输入法的两个view置空

    2018-12-06
    4
  • 久九
    1.通过Bugly查看崩溃日志,确定问题所在,google或者自行调试解决问题。2.通过sample的练习,梳理了反射和代理相关的知识,学会了通过寻找Hook点来处理Bug的这种思维(以前完全没有这方面的概念,汗颜!!)
    2019-02-18
    3
  • 山鬼
    感觉大量的问题,在被定位出来以后,解决办法就是去读代码就好了,包括课后习题的例子,读完Daemons这个类的源代码,解决方法就出来了。当然也不止老师的实现这一种方案,还有其他的点来绕过这个机制。个人觉得这个bug的修复重点不在怎么绕过,而是绕过之后到底会不会产生其他影响。
    2018-12-19
    3
  • 董晓航
    能给一下toast动态代理TN的demo吗?大神
    2018-12-14
    3
  • 、、cryAllen
    由于从事的工作内容涉及到底层Framwork比较多,目前日常工作中也是经常需要分析BUG,那么分析的素材自然就是log文件,整个Android 系统真的是很好旁观者,我们一切的操作行为基本都是有记录的。如果碰到崩溃的问题,我自己基本解决思路则是先在全局log中搜索是否有Die关键字,然后看进程号,进程号是否有重启过,然后去system_app_crash@1451577628472.txt看相关错误,很多都是java.lang.RuntimeException异常居多。
    2018-12-07
    3
  • Z
    学习了,最近也在学习linux 内核,很强大,作为一个一年的菜鸟受益匪浅,推荐大伙可以看看
    2018-12-05
    2
  • jason
    TimeoutException 直接调用stop存在什么安全问题?请大神指教一二。

    作者回复: Stop的时候有一定概率导致即使没有超时也会报timeoutexception

    2018-12-04
    2
  • Geek_f342a0
    崩溃主要分为java层crash,c/c++层crash,anr导致的crash
    1.java层crash:可以通过手机logcat日志分析,也可以继承UncaughtExceptionHandler,并通过Thread.setDefaultUncaughtExceptionHandler来处理系统的异常,基本能收集到所有java层的crash的问题。
    2.c/c++层的crash:通过logcat日志进行分析,也可以通过breakpad的来收集dump,分析dump文件,如果是内存堆或者栈出问题,可能收集不了任何堆栈,这要通过别的手段进行分析,通过分析代码,或者增加一些内存分配的判断,或者增加日志来解决。
    3.anr的crash:通过logcat日志分析,anr的日志也可以通过/data/anr/traces.txt进行分析,分析整个的堆栈,同时也要检查代码有没有处理耗时的操作,比如在主线程,或者广播处理或者Service处理是否会超时了。
    2019-10-09
    1
  • Geek_Yasin28
    请问有没有native崩溃后成功获取java堆栈的例子, 是只能用hook libart.so实现吗?
    这边参考实现了native崩溃获取, 但是在native崩溃时, 信号处理函数去唤醒之前等待的线程,去回调java或者抛出异常给java处理都没有反应。

    作者回复: 一般情况下直接jni调用也是可以拿到的,除了一些特殊情况

    2019-03-14
    1
  • less
    第三次听了.受益良多.这次研究下simple
    2019-03-08
    1
  • 大碗馄饨
    绍文大神,有幸拜读大作,有个问题请教:
    文中提到用ThreadList::ForEach遍历art线程,首先要拿到ThreadList指针,我查了符号表,GetThreadList并未导出,如何获取呢?如果用thread_list_在Runtime类中的偏移获取,会有兼容性问题,文中所说的黑科技,是否有更好的办法?

    前一阵在研究ANR监控,发现可以通过拦截SIGQUIT信号监听ANR,调用Runtime::DumpForSigQuit获取trace文件的信息,再通过tgkill发送SIGQUIT给Signal Catcher线程。线上观察了一段时间,基本没有兼容性问题,除了Android 7及以下谷歌本身的bug偶尔会造成崩溃: b/36445592 Don't use pthread_getschedparam since pthread may have exited.

    这个方法在Android 5会有问题,需要手动暂停线程并做锁状态检查,要用到几个未导出符号(其中ThreadList的resumeAll/suspendAll通过ScopedSuspendAll解决了),请问有什么好办法么?

    盼复

    作者回复: 的确用的是偏移的方法,但是我们这里是通过fork进程来做的,就算崩溃也不会影响主进程。而且我们的框架会做指令识别,以测试数据来看,大部分都可以成功拿到堆栈。具体失败率的话,我可以要去翻一下报表

    2019-03-01
    1
收起评论
82
返回
顶部