Android App常见的异常可分为三种:ANR,Crash及OOM。当异常发生时如何正确的获取日志定位问题非常重要。本文针对这三种异常分别给出了处理建议,并提供了一些日志收集框架及日志上传的思路。
ANR
ANR(Application No Response),俗称应用卡顿。在Android中所有KeyEvent和TouchEvent都是按照先后顺序放入队列中,依次执行,并且只有当前一个事件执行完毕,才能开始执行下一个事件。每个正在执行的事件都被被保存在waitQueue中,执行完毕之后从waitQueue中移除。
当用户触发一个事件的时候,首先判断waitQueue是否为空,如果为空,可以立即响应该事件。如果队列不为空,说明还有事件没有执行完毕。判断当前时间和上一个事件响应时间是否大于超时时间(一般5秒,broadCastReceiver 10秒),如果超时,则会通过ActivityManagerService以弹窗的形式通知用户App无响应。
发生这种异常时,需查看logcat日志和traces.txt文件定位原因。
logcat日志
通过logcat可定位ANR发生的程序
traces.txt文件
该文件需通过adb命令获取 adb pull /data/anr/traces.txt .
查看traces.txt文件,一般文件开头可看到导致ANR的堆栈信息。
android studio的analyze stacktrac工具可协助分析traces.txt文件
Crash
Crash即程序崩溃,一般是由于程序有未处理的异常导致。这种情况需要抓取程序崩溃的时的堆栈信息来定位。为了更优雅的来处理程序异常,我们需要捕获这种未定义的异常,捕获方式是实现uncaguhtExceptionHandler。捕获该异常后,我们可以打印出堆栈信息定位异常发生点,并做一些崩溃处理的操作,如日志收集并上传服务器等。
1.定义自己的异常处理类
public class CrashHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread thread, Throwable ex) {
//异常出现后会调用该函数,请在该函数中做异常处理操作
}
}
2.在Application中设置默认的异常处理Hanler为自定义Handler
Thread.setDefaultUncaughtExceptionHandler(crashHandler);
OOM
OOM(Out Of Memory),即内存溢出。了解OOM之前,先熟悉一下Java的强,软,弱,虚四种引用类型。
Java中的四种引用类型
如果一个无用对象(不需要再使用的对象)仍然被其他对象持有强引用,造成该对象无法被系统回收,以致该对象在堆中所占用的内存单元无法被释放而造成内存空间浪费,这中情况就是内存泄露。当内存泄漏超过虚拟机为APP分配的最大值,就会发生OOM,此时程序会崩溃。
Android程序中可能导致内存泄漏的场景:
非静态内部类导致内存泄露
非静态内部类(包括匿名内部类)默认就会持有外部类的强引用,当非静态内部类对象的生命周期比外部类对象的生命周期长时,就会导致内存泄露。如:
Activity中定义Handler,Handler会默认持有activity的强引用。当Activity生命周期结束但Handler还有未完成的任务时。
Activity开启线程Thread,AsyncTask,创建匿名内部类对象,默认就隐式的持有外部Activity的强引用。当Activity生命周期结束时,线程任务还未结束。
如果要使用内部类,但又要规避内存泄露,需采用静态内部类+弱引用的方式,代码如下:
private static class MyHandler extends Handler {
private WeakReferenceactivityWeakReference;
public MyHandler(MainActivity activity) {
activityWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = activityWeakReference.get();
if (activity != null) {
if (msg.what == 1) {
//通过activity.xxx来调用activity的变量和方法
}
}
}
}
静态变量导致内存泄露
静态变量存储在方法区,它的生命周期从类加载开始,到整个进程结束。一旦静态变量初始化后,它所持有的引用只有等到进程结束才会释放。如果静态变量持有了Activity的强引用,比如持有了Activity的context则会导致Activity泄漏。
广播使用后未取消,属性动画未取消。
文件流,网络流,数据流等使用后未正常关闭。
只要能够避免以上情况的发生,那就可以避免大多数的内存泄漏。
检测工具
StrictMode
Android性能调优利器StrictMode
StrictMode意思为严格模式,是用来检测程序代码中违例情况的开发者工具。可以检查程序中的ANR和内存泄漏。它能在开发阶段给你直观的提示,并需要在Release版本中关掉。StrictMode有两个策略:
ThreadPolicy
检查主线程耗时操作磁盘IO,Network,其他逻辑耗时
VmPolicy
检查Activity,Closable,sqlite对象泄漏
Android Lint
Lint 是 Android studio 自带的静态代码分析工具,能够帮助分析代码中的错误。使用入口顶部菜单Analyze -> Inspect Code。扫描完成后查看Performance 项,里面会有一些代码泄漏的建议和其他优化建议。
BlockCanary
BlockCanary是由markzhai大神发布在Github上的一个开源工具,该工具通过计算Looper事件循环流程中,事件执行前和事件执行完成后的时间差来检查该行为是否发生了卡顿。能够很好的帮助App定位耗时操作。详情参考:
BlockCanary原理及使用方式
LeakCanary
Android内存泄漏检测利器:LeakCanary
以下介绍几款常用的调试工具和开源库
facebook stetho
可以动态查看sqlite,并可以执行sql语句动态修改数据库
可查看sharepreferences数据
对于okhttp可以查看每条连接耗时
可以查看View视图的层级
其他查看sqlite的方法,用AS把sql数据导出,并利用工具SQLite Expert查看
Android Studio profiler
可动态查看,cpu,内存及网络使用情况。
timber
https://github.com/JakeWharton/timber
logger
https://github.com/orhanobut/logger
第三方日志收集,上传
腾讯 Bugly