ACRA源码分析总结
init()初始化
首次初始化
进行首次初始化,应用反射机制,分析app对象。
如果@ReportCrashes
中没有做设置则报错,否则做第二步初始化。
init(app, new ConfigurationBuilder(app))
,构造一个ConfigurationBuilder,放置@ReportCrashes
中声明的设置。
public static void init( Application app){ |
第二步初始化
进行第二步初始化,向第三步初始化增加一个布尔传入参数。
public static void init( Application app, ConfigurationBuilder builder){ |
第三步初始化
进行第三步初始化,builder.build()
,
Builds the ACRAConfiguration which will be used to configure ACRA.
public static void init(boolean checkReportsOnApplicationStart) Application app, ConfigurationBuilder builder, { |
最终初始化
init()的最终初始化
public static void init(boolean checkReportsOnApplicationStart) Application app, ACRAConfiguration config, |
init()流程
isACRASenderServiceProcess()
如果当前进程正在运行SenderService,返回True。
final boolean senderServiceProcess = isACRASenderServiceProcess(); |
isACRASenderServiceProcess()判断SenderService方法:
读取/proc/self/cmdline
文件内容,获取进程启动时的命令行参数,根据其末尾是否带有:acra
判断。
ACRA.java中与之相关的设置是:
private static final String ACRA_PRIVATE_PROCESS_NAME= ":acra" |
如果senderServiceProcess为True
,则说明SenderService在运行,不会捕捉异常,仅执行发送。
Not initialising ACRA to listen for uncaught Exceptions as this is the SendWorker process and we only send reports, we don’t capture them to avoid infinite loops
supportedAndroidVersion && config !=null && mApplication != null
一系列判断和检查。
ErrorReporter对象(核心部分)
In ACRA.java
建立ErrorReporter对象,传入上面的参数,捕获异常和发送报告。
errorReporterSingleton = new ErrorReporter(mApplication, configProxy, prefs, |
In ErrorReporter.java
UncaughtExceptionHandler接口
ErrorReporter实现了Thread.UncaughtExceptionHandler接口,在线程死亡之前,,可以将异常传递到ErrorReporter处理器。
public class ErrorReporter implements Thread.UncaughtExceptionHandler |
ErrorReporter构造器
enabled Whether this ErrorReporter should capture Exceptions and forward their reports.
listenForUncaughtExceptions Whether to listen for uncaught Exceptions.
ErrorReporter( Application context, ACRAConfiguration config, SharedPreferences prefs, |
创建一个CrashReportDataFactory对象和一个Thread.UncaughtExceptionHandler,然后设置默认的异常处理器。
//.. |
- UncaughtExceptionHandler对象
UncaughtExceptionHandler捕获全局异常
- setDefaultUncaughtExceptionHandler()
为所有线程安装一个默认的处理器,这里设置为this
。
- uncaughtException()
实现uncaughtException()方法,处理器中真正完成处理的部分。
线程的所有未被catch的Exception被传递到这个方法,将出错的Thread和对象Throwable传入一个新建的ReportBuilder对象。
新建的ReportBuilder对象执行build方法,传入上面创建的ReportExecutor的对象reportExecutor。
public void uncaughtException( Thread t, Throwable e){ |
In ReportBuilder.java
Assembles and sends the crash report.
build方法,调用reportExecutor的execute方法。
public void build( ReportExecutor reportExecutor){ |
ReportExecutor
ReportExecutor的构造器
ReportExecutor(android.content.Context context, ACRAConfiguration config, |
Try to send a report, if an error occurs stores a report file for a later attempt.
public void execute(final ReportBuilder reportBuilder) { |
In CrashReportDataFactory.java
- CrashReportDataFactory对象
负责给一个Exception创建CrashReportData.
Also responsible for holding the custom data to send with each report.
- createCrashData方法
Collects crash data, return CrashReportData
public CrashReportData createCrashData( ReportBuilder builder) |
In CrashReportData.java
Stores a crash reports data with ReportField enum values as keys.
This is basically the source of Properties adapted to extend an EnumMap(枚举映射)
instead of Hashtable and with a few tweaks to avoid losing crazy amounts
of android time in the generation of a date comment when storing to file.
CrashReportData构造器,应用反射机制,使用.class
获取ReportField的Class对象
public final class CrashReportData extends EnumMap<ReportField, String>{ |
createCrashData
收集数据部分。
CrashReportDataFactory.java中createCrashData方法,对于DROPBOX的收集数据部分是与logcat的收集数据部分放在一个代码块中的。
在这一部分,ACRA的代码注释中,有这样的描述:
Before JellyBean, this required the READ_LOGS permission
Since JellyBean,READ_LOGS is not granted to third-party apps anymore for security reasons.
Though, we can call logcat without any permission and still get traces related to our app.
可见,如果Android版本高于JellyBean,无需配置READ_LOGS权限,即可读取 logcat与当前App相关的信息;然而,对于是否能够读取Drop Box的信息,ACRA的源码中并没有提及。
实测,debug信息中有输出READ_LOGS granted! ACRA can include LogCat and DropBox data.,并且没有报错Error while retrieving DROPBOX data ,证明下面的代码可以正常执行到DropboxCollector().read()的地方。
final boolean hasReadLogsPermission = pm.hasPermission(Manifest.permission.READ_LOGS) || Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; |
- In DropBoxCollector.java
构造器中设置SYSYTEM_TAGS等。
final class DropBoxCollector { |
read()方法,提取drop box中的信息,返回字符串。主要步骤如下:
使用一个ArrayList对象保存设置的SYSYTEM_TAGS和additionalTags,进行提取信息的操作,如果捕捉到异常,则输出DropBoxManager not available.
public String read( Context context, ACRAConfiguration config){ |
执行for循环,先调用getNextEntry(String tag, long msec) 从drop box获取在特定时间mesc后的下一个entry。
如果entry不为null,则调用 getText(int maxBytes) 返回entry的uncompressed text contents,追加到最终返回的字符串中,MaxBytes为返回String的最大值。
final StringBuilder dropboxContent = new StringBuilder(); |
实测的Debug结果显示,运行过程输出DropBoxManager not available. 证明在read()方法中出现异常,导致无法正常获取Drop Box的信息。
OnSharedPreferenceChangeListener
We HAVE to keep a reference otherwise the listener could be garbage.
mPrefListener = new OnSharedPreferenceChangeListener() |