Android 下使用 Crashlytics 遇到的问题
国内 Android 开发者应该主要使用友盟做数据统计分析,但个人在开发中发现这货越来越不靠谱,错误分析和事件统计数据时有时无,或者数据大起大落;甚至统计到的错误信息还包含友盟 SDK 自己的错误。跟他们客服和技术也沟通过,要么是让你发 apk 包,要么是让你自己看文档,问题始终没得到解决,然后很自然想到国外的类似工具。
Crashlytics 在国外用的比较多,特别是 iOS 平台。Square 、Walmart 、Paypal 、Yammer、Yelp 、Path 、Expedia 、Waze 、Groupon 等众多大咖级别的 APP 都是其用户,而且目前也开始支持 Android 平台。而其他同类产品 localytics 、geckoboard 等要么不支持 Android 、要么收费、要么没什么人用。
然要使用除错误统计外的其他功能还是得自己添加代码;
不像友盟等国内同类产品,将固定的序列号直接写入 xml 文件,而是动态自动生成的;当然这个存放序列号的 xml 文件也是不能修改和提交到版本控制系统的;
后台可以设置邮件提醒,当然这个最好不要开启,Android 开发那数量惊人、千奇百怪的错误信息你懂的。
不仅能统计到
UncaughtException
这种未捕获的 Crash 异常信息,只要在try
/catch
代码块的catch
中调用Crashlytics.logException(e)
就能统计到任何异常;
第一次用过之后的感受就是:高端、大气、上档次:
注册需要审核通过才能使用,国内同类产品顶多发个邮箱激活链接;
支持 Eclipse 、Intellij IDEA 和 Android Studio 等三大 IDE ;
Eclipse 插件是 iOS 主题风格 UI ,跟其他 plugin 在一起简直是鹤立鸡群;
只要登录帐号并选择项目,会自动导入 jar 包并生成一个序列号,然后在
AndroidManifest.xml
和启动Activity
的入口添加初始化代码,可以说是一键式操作,当相当详细的错误信息,不仅仅是简单的打印 StackTrace 信息;并且能看到最近一次 crash 的机器可用内存等信息,而不仅仅是简单统计机型和版本号。
虽然看起来很强大,一开始使用还是遇到了两个问题。
自定义 UncaughtExceptionHandler 和 Crashlytics 相冲突
一开始肯定想看看错误统计的即时性,通过手动添加代码 throw new RuntimeException
模拟 crash ,但是后台始终看不到错误数据。
然后只能看官方文档上找答案。发现有一篇讲怎么强制 crash 的文章,但仔细一看也没什么,只要在 Crashlytics.start()
调用之后就行了。
正在茫然的时候,想到了 UncaughtExceptionHandler
,[之前有篇文章](/blog/2013/07/02/exception/)专门讲怎么通过它捕获应用全局异常信息。很显然,这种错误统计工具应该也是这么干的。
这样的话,很有可能是我们自定义的 UncaughtExceptionHandler
跟 Crashlytics 的 SDK 中定义的相冲突了,于是注释掉了自己的这部分代码,最后终于从后台看到错误日志了。
虽然友盟也是默认下次启动时上传错误信息,但感觉 Crashlytics 可靠多了,而且还是墙外的东西,做到这水平真心不容易了。
不过有个小问题,当初自己捕获异常只是 kill 掉当前的进程:
android.os.Process.killProcess(android.os.Process.myPid());
这样做不会影响之前的页面,但默认的处理方式是直接杀掉整个应用,这样对用户体验肯定有些影响,但为了能可靠的统计到所有异常信息,也只能做点牺牲了。
经过几天观察,Crashlytics 统计到的错误信息数量确实比友盟更多,看来没选错。
Crashlytics 初始化的时机
默认情况下,Eclipse 插件会自动通过 AndroidMafifest.xml
找到用于 launch 的 Activity
,然后在其 onCreate()
方法中调用 Crashlytics.start()
完成初始化。显然这是个单例模式,只需要初始化一次。当然,你在每个 Activity
中都掉用一次也是没有问题的。
那如果我们自定义了 Application
子类,能否直接在其中初始化呢,答案是肯定的,详见这篇文章。
但后面还是出现了一个问题,有用户反馈新版本安装后移动时就 crash 了,而 Crashlytics 后台却看不到该信息。那么很显然是在 Crashlytics 初始化之前就出现了异常。
我们在 Library Project 中定义了一个 BaseApplication extends Application
,然后在实际项目中又定义了一个 MyApplication extends BaseApplication
,而我们是在 MyApplication
的 onCeate()
方法中初始化 Crashlytics 的,而且还是在调用父类 BaseApplication
的 onCreate()
方法之后,通过 logcat 看到异常正是在 BaseApplication
的 onCreate()
方法中发生的,这时候 Crashlytics 还没初始化,当然也就统计不到数据了。
这说明我们 Crashlytics 初始化的时机不对,需要将 Crashlytics 代码放到 BaseApplication
的 onCreate()
中执行。