阅读本文大概需要8分钟

作者:wangsj1992出处:https://www.jianshu.com/p/8676f7a05920

前言

安卓开发中,你是否遇到过如下困扰:

场景一

开发好一个功能后提交给测试小姑娘,测试中说“app停止运行”,然后你拿着他的测试机连到自己电脑上,重复操作一下,看看log找崩溃的原因。

如果是必现的bug还好,遇到偶现的bug的蛋疼了。

场景二

可能你的项目中接入了UncaughtExceptionHandler,崩溃日志会以文件的方式保存在sd卡,但是有的设备不支持直接查看这些文件,此时还得连上电脑找到这个文件。

场景三

可能你的项目中使用了三方统计,可以统计出app崩溃的日志,但是三方统计的数据一般不是及时的,可能要等一段时间数据才能同步。

推荐一个小工具

扯了这么多,就是少一个崩溃日志记录查看工具,那么接下来推荐一个安卓开发必备的工具。

CrashCanary是一个无侵入的安卓崩溃日志记录库,对你的代码没有任务侵入性,无需申请权限,只需要添加依赖,即可在程序崩溃时记录崩溃日志并可查看所有日志。

效果如下:

快速接入

allprojects {    repositories {        ...        maven { url 'https://jitpack.io' }    }}dependencies {    debugImplementation  'com.github.giswangsj:CrashCanary:1.0.0'}

是的,这样就接入了,你不需要添加任何代码,真正的无侵入。

不瞒你说,这里就是参考了LeakCanary2

LeakCanary2一样,程序安装后会多出来一个图标为CrashCanary的入口,名字和你的应用名相同(感觉同名不同图标更加人性化,因为如果你几个app都接入了LeakCanary时,应用列表就会有好几个名为Leaks的app入口,此时你可能就不知道哪个是哪个了)。

一旦你的app崩溃了,可以从这个同名的入口进入查看日志,或者通知栏的通知进入。如下:

enterance.png

log_list.png

点击日志item进入日志详情,详情中记录了崩溃的详细日志,同时还记录了设备的版本、型号、cpu以及软件版本等信息。

detail.png

原理及涉及知识点

1,UncaughtExceptionHandler

UncaughtExceptionHandler可以帮我们捕获我们代码中未捕获而导致崩溃的异常,源码如下:

public interface UncaughtExceptionHandler {   /**    * Method invoked when the given thread terminates due to the    * given uncaught exception.    * 

Any exception thrown by this method will be ignored by the
* Java Virtual Machine.
* @param t the thread
* @param e the exception
*/
void uncaughtException(Thread t, Throwable e);
}

它只是一个接口,系统的LoggingHandler就是它的实现类。

2,自定义UncaughtExceptionHandler

Thread类提供了设置UncaughtExceptionHandler的方法

/**

  * Set the default handler invoked when a thread abruptly terminates  * due to an uncaught exception, and no other handler has been defined  * for that thread.*/

public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {    defaultUncaughtExceptionHandler = eh;}

因此我们只需要实现UncaughtExceptionHandler,并调用当前线程的setDefaultUncaughtExceptionHandler方法即可。

主要代码如下:

public class CrashHandler implements UncaughtExceptionHandler {

    public static final String TAG = "CrashHandler";    // CrashHandler实例    private static CrashHandler instance    // 系统默认的UncaughtException处理类    private UncaughtExceptionHandler mDefaultHandler;    // 程序的Context对象    private Context mContext;

    /**     * 保证只有一个CrashHandler实例     */    private CrashHandler() {    }

    /**     * 获取CrashHandler实例 ,单例模式     */    public static CrashHandler getInstance() {        if (instance == null) {            instance = new CrashHandler();        }        return instance;    }

    /**     * 初始化     */    public void init(Context context) {        mContext = context;        // 获取系统默认的UncaughtException处理器        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();        // 设置该CrashHandler为程序的默认处理器        Thread.setDefaultUncaughtExceptionHandler(this);    }

    /**     * 当UncaughtException发生时会转入该函数来处理     */    @Override    public void uncaughtException(Thread thread, Throwable ex) {        if (!handleException(ex) && mDefaultHandler != null) {            //如果用户没有处理则让系统默认的异常处理器来处理            mDefaultHandler.uncaughtException(thread, ex);        } else {            try {                Thread.sleep(2000);            } catch (InterruptedException e) {                Log.e(TAG, "error : ", e);            }            // 退出程序            System.exit(0);        }    }

    /**     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.     *     * @param ex     * @return true:如果处理了该异常信息;否则返回false.     */    private boolean handleException(Throwable ex) {        // todo 记录日志        return true;    }}

主要逻辑为:首先获取线程默认的UncaughtExceptionHandler,当发生异常时在uncaughtException()方法中首先执行自己的处理异常的逻辑,如果自己未能处理,则调用系统默认的来处理,最后退出程序。

3,记录日志

1,首先获取日志详情

日志存在于Throwable中,从其中获取详情需要使用WriterPrintWriter来获取。

Writer writer = new StringWriter();

PrintWriter printWriter = new PrintWriter(writer);ex.printStackTrace(printWriter);Throwable cause = ex.getCause();while (cause != null) {    cause.printStackTrace(printWriter);    cause = cause.getCause();}printWriter.close();String result = writer.toString();

2,其次,要获取系统信息及apk信息。

// 获取app版本信息

PackageManager pm = ctx.getPackageManager();PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);String versionName = pi.versionName == null ? "null" : pi.versionName;

String versionCode = pi.versionCode + "";

// 获取设备型号等信息

Field[] fields = Build.class.getDeclaredFields();for (Field field : fields) {    field.setAccessible(true);    Log.d(TAG, field.getName() + " : " + field.get(null));}

3,保存数据

可以使用SQLite数据库来保存数据,这样既不需要申请读sd卡权限,又可以方便程序读取。

4,日志读取

日志是在一个和app同名的入口中查看的,那么如何生成这个入口呢?

首先要明确这不是另一个app,他们是一个app,不信你卸载这个同名app试试,你的app也会被卸载掉。

它其实就是一个配置了LauncherActivity,如下:

    android:name=".ui.CrashViewerActivity"    android:icon="@mipmap/ic_crash_icon"    android:taskAffinity="wsj.crash.lib">

需要注意的是该activity需要配置一个taskAffinity,不然这个activity会和你的应用在同一个栈中,影响正常的返回栈逻辑。

5,无侵入

如何做到无侵入呢?和LeakCanary2一样,使用ContentProvider,在其onCreate()方法中初始化我们的CrashHandler即可。

总结

目前业界已经存在不少更好的功能更全的工具。本文详细讲解了无侵入式的android崩溃日志记录流程,对新手提供一些思路,希望大佬们不要喷,根据自己的需要找到一款适合自己的工具才是最重要的。

国民程序员

android crash没有日志_Android开发必备神器CrashCanary相关推荐

  1. java安卓开发工具_Android开发必备那些工具

    工欲善其事,必先利其器,在Android项目的开发中,借助工具能使开发效率大幅提升,下面分享我经常使用的工具,欢迎各位同学补充. 1.Android Studio Android程序员的吃饭工具,可以 ...

  2. android 技能标签功能_android开发工程师必备技能

    厚 学 网 android 开发工程师必备技能 android 软件开发工程师是 Android 平台发展中不可或缺的人才要素. 据介 绍,就我国目前来说, Android 研发人才缺口至少 30 万 ...

  3. android 获取monkey日志_Android压力测试:monkey压力测试实战

    主要分享的主题是 Android App 专项测试, 通过 monkey 进行压力测试. 一.测试步骤 1.安装ADB 2.连接被测手机和电脑 3.打开CMD命令行 4.输入monkey命令 adb ...

  4. android java 调用栈_Android开发中打印方法调用栈

    概要 在日常开发和debug的过程中,我们都需要看看某一个方法的调用StackTrace,如果是crash或者异常被抛出的情况下会直接看到对应的StackTrace,如: 39 5939 E Andr ...

  5. android crash没有日志_App测试之monkey(四)-调试参数及日志

    由于monkey在测试app时,我们需要作长时间的稳定性测试,比如连续测试10小时(monkey不能指定时间,可以指定次数,时间可以在测试次数的日志基础上大概算出来),在测试过程中,app很可能测试时 ...

  6. android dialog 隐藏状态栏_Android开发不得不收集的工具类集合

    嗨,你终于来啦 ~ 等你好久啦~ 喜欢的小伙伴欢迎关注,我会定期分享Android知识点及解析,还会不断更新的BATJ面试专题,欢迎大家前来探讨交流,如有好的文章也欢迎投稿.更新了几天Android基 ...

  7. 开发必备神器,你值得拥有!

    1.ShowMore:一款简洁实用的高清在线录屏工具,支持Windows.mac,只需一个浏览器,即可免费录制电脑屏幕上一切活动.访问ShowMore网站(网站地址),第一次使用时需要安装一个小启动器 ...

  8. GitHub 又一开发必备神器问世,代号「LiteKit」

    目前,越来越多的 AI 场景将 AI 能力直接部署在移动端,其优势主要在于实时.省流.以及安全性等方面.这些 AI 能力给移动端产品带来巨大的想象空间,促进了移动互联网下半场的繁荣. 在移动端智能化的 ...

  9. android jni 人头检测_Android开发的技术层次

    任何一种移动开发生态系统其技术人员都是呈现金字塔式分布的.我借此也说说Developer和Programmer的区别: Programmer是真正意义上的程序员,写程序的.灵魂级 Developer是 ...

最新文章

  1. xhr请求python_python爬取boss直聘职位数据,并保存到本地
  2. javaScript-进阶篇(三)
  3. 程序猿接私活经验总结,来自csdn论坛语录
  4. vue路由query和params的区别
  5. Docker 搭建pxc集群 + haproxy + keepalived 高可用(二)
  6. 机器学习篇01:在线学习的支持向量机算法学习笔记
  7. centos安装 mysql_centos下安装MySQL数据库
  8. lucene.net 3.0.3、结合盘古分词进行搜索的小例子(转)
  9. linux系统在线搭建禅道
  10. hdu4027线段树
  11. 2022 Java面试题
  12. 关于 路标设置 的问题
  13. java技术与jsp技术简介_什么叫JSP技术
  14. 自学c语言买谭浩强,C语言自学最强版本(谭浩强).pdf
  15. Python实现回归分析之线性回归
  16. Python生成器教程
  17. Spring启动,constructor,@PostConstruct,afterPropertiesSet,onApplicationEvent执行顺序 原创 2016年09月29日 11:39:2
  18. Linux安装iso镜像中的软件
  19. vue项目如何部署?布署服务器后刷新404如何解决?
  20. MySQL查询时不加order by的时候默认排序规则是什么?

热门文章

  1. 一道Python面试题,设置一个动态变量名
  2. Python 3.9.0 首个迭代版本发布了
  3. C++生成一个随机网络
  4. Hive SQL 窗口函数
  5. Django自带的用户验证与事务管理的基本概念理解
  6. hadoop学习——Hadoop核心组件
  7. 大数据和人工智能时代下的运筹学
  8. jQuery模板和数据绑定
  9. python实现Queue和Stack
  10. 转载——Regression Tree 回归树