ACRA
2016/10/02
上海
Read the fucking source code担巩。
ACRA Application Crash Reports for Android 方援。
GitHub:https://github.com/ACRA
(文章基于ACRA v4.8.0)
ACRA目的簡單明了,收集App崩潰堆棧信息涛癌,生成報告并發(fā)送到指定端犯戏。
通俗的崩潰處理流程:
1送火,實現(xiàn)Thread.UncaughtExceptionHandler接口,自定義崩潰處理器先匪。
2种吸,保留系統(tǒng)默認Thread.UncaughtExceptionHandler接口(通過Thread.getDefaultUncaughtExceptionHandler()獲得),
當自定義處理器無法分析崩潰異常時呀非,提交給默認處理器解決坚俗。
3,綁定自定義處理器(通過Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler)方法)
4岸裙,崩潰猖败,自定義處理器捕捉崩潰異常,生成報表降允。
5恩闻,處理報表(我習慣發(fā)送到靜態(tài)BroadcastReceiver處理報表并重啟應用,嘗試將報表發(fā)送到服務器剧董,若發(fā)送失敗幢尚,則寫入本地文件)
ACRA核心類:
ACRA ACRA入口類
ACRAConfiguration ACRA配置器
ConfiguraitonBuilder 配置器的構造器
ErrorReporter 崩潰堆棧捕捉線程(實現(xiàn)Thread.UncaughtExceptionHandler)
ReportBuilder 錯誤報表生成器
SendService 報表提交服務(處于ErrorReporter與ReportSender之間)
SenderServiceStarter SendService幫助類
ReportSender 錯誤報表發(fā)送器
ReportSenderFactory ReportSender工廠類
ACRA初始化:
//初始化三種方式
ACRA.init(Application app);
ACRA.init(Application app, ACRAConfiguration config);
//ACRAConfiguration是ACRA配置器,與注解是相同功能翅楼,ACRAConfiguration優(yōu)先于注解
//checkReportsOnApplicationStart尉剩,這個參數(shù)決定了三件事:
1,是否刪除上個版本未發(fā)送的錯誤報告犁嗅,
2边涕,是否刪除用戶未批準發(fā)送的錯誤報告晤碘,
3褂微,是否嘗試發(fā)送之前發(fā)送失敗的錯誤報表。
ACRA.init(Application app, ACRAConfiguration config, boolean checkReportsOnApplicationStart);
//省略版本適配园爷,優(yōu)化等代碼
public static void init(Application app, ACRAConfiguration config, boolean checkReportsOnApplicationStart){
//判斷ACRA是否處于運行狀態(tài)
final boolean senderServiceProcess = isACRASenderServiceProcess(app);
//android 系統(tǒng)版本是否>=8
boolean supportedAndroidVersion = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO);
mApplication = app;
configProxy = config;
try {
...
省略ACRA版本適配相關宠蚂,ACRA版本升級,本地錯誤報表轉移
...
//checkCrashResources()方法檢查注釋中的相關字段是否完整童社,Toast模式必須設置ToastText字段等..
config.checkCrashResources();
//是否加載Acra
final boolean enableAcra = supportedAndroidVersion && !shouldDisableACRA(prefs);
//創(chuàng)建ErrorReporter實例
errorReporterSingleton = new ErrorReporter(mApplication, configProxy, prefs, enableAcra, supportedAndroidVersion, !senderServiceProcess);
//
if (checkReportsOnApplicationStart && !senderServiceProcess) {
//ApplicationStartupProcessor 用于每次ACRA初始化時求厕,查找任何現(xiàn)有的報告并發(fā)送。
final ApplicationStartupProcessor startupProcessor = new ApplicationStartupProcessor(mApplication, config);
//如果配置器中設定刪除App上個版本未發(fā)送的錯誤報表
if (config.deleteOldUnsentReportsOnApplicationStart()) {
//則刪除
startupProcessor.deleteUnsentReportsFromOldAppVersion();
}
//如果配置器中設定刪除用戶未批準發(fā)送的錯誤報告
if (config.deleteUnapprovedReportsOnApplicationStart()) {
//則刪除
startupProcessor.deleteAllUnapprovedReportsBarOne();
}
//如果可以啟動ACRA
if (enableAcra) {
//嘗試發(fā)送之前發(fā)送失敗的錯誤報表扰楼。
startupProcessor.sendApprovedReports();
}
}
} catch (ACRAConfigurationException e) {
log.w(LOG_TAG, "Error : ", e);
}
.......
}
ErrorReporter呀癣,崩潰堆棧捕捉線程
public class ErrorReporter implements Thread.UncaughtExceptionHandler
ErrorReporter初始化:
//省略版本適配,優(yōu)化等代碼
ErrorReporter(Application context, ACRAConfig config, SharedPreferences prefs, boolean enabled, boolean supportedAndroidVersion, boolean listenForUncaughtExceptions) {
this.context = context;
this.config = config;
this.supportedAndroidVersion = supportedAndroidVersion;
......
//獲取系統(tǒng)默認異常捕捉線程
defaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
//將ErrorReporter設置為默認異常捕捉線程
Thread.setDefaultUncaughtExceptionHandler(this);
......
}
ErrorReporter異常處理:
@Overridepublic void uncaughtException(Thread t, Throwable e) {
......
//出現(xiàn)異常弦赖,利用錯誤報表構造器项栏,生成錯誤報表
new ReportBuilder()
//ReportBuilder內部持有崩潰數(shù)據(jù)t實例
.uncaughtExceptionThread(t)
//ReportBuilder內部持有崩潰數(shù)據(jù)e實例
.exception(e)
//ReportBuilder內部有app是否結束的標示
.endApplication()
//構建
.build(reportExecutor);
......
}
public void build(ReportExecutor reportExecutor) {
.....
//通過錯誤報表構造器,分析錯誤堆棧蹬竖,并寫入本地文件
reportExecutor.execute(ReportBuilder.this);
}
public void execute(final ReportBuilder reportBuilder) {
...分析并寫入本地沼沈,再根據(jù)配置流酬,顯示對應的提示...
//發(fā)送錯誤報表
startSendingReports(是否是靜默發(fā)送, 是否直接批準發(fā)送);
}
private void startSendingReports(boolean onlySendSilentReports, boolean approveReportsFirst) {
......
//創(chuàng)建SenderServiceStarter 對象
final SenderServiceStarter starter = new SenderServiceStarter(context, config);
//調用startService方法
starter.startService(onlySendSilentReports, approveReportsFirst);
......
}
}
//啟動SenderService服務
public void startService(boolean onlySendSilentReports, boolean approveReportsFirst) {
final Intent intent = new Intent(context, SenderService.class);
//存入是否靜默發(fā)送
intent.putExtra(SenderService.EXTRA_ONLY_SEND_SILENT_REPORTS, onlySendSilentReports);
//存入是否直接批準發(fā)送
intent.putExtra(SenderService.EXTRA_APPROVE_REPORTS_FIRST, approveReportsFirst);
//存入發(fā)射器工廠類的Class對象,此對象為ReportSenderFactory實例
intent.putExtra(SenderService.EXTRA_REPORT_SENDER_FACTORIES, config.reportSenderFactoryClasses());
//存入ACRA配置器
intent.putExtra(SenderService.EXTRA_ACRA_CONFIG, config);
//啟動SenderService
context.startService(intent);
}
SenderService繼承自IntentService
public class SenderService extends IntentService
ReportSenderFactory接口
public interface ReportSenderFactory {
//返回ReportSender 實例
ReportSender create(Context context, ACRAConfig config);
}
ReportSender 接口
public interface ReportSender {
//處理錯誤報表
void send(Context context, CrashReportData errorContent) throws ReportSenderException;
}
//啟動IntentService列另,調用startService(intent);intent都會傳入此方法
@Overrideprotected void onHandleIntent(final Intent intent) {
//是否靜默發(fā)送
final boolean onlySendSilentReports = intent.getBooleanExtra(EXTRA_ONLY_SEND_SILENT_REPORTS, false);
//是否直接批準發(fā)送
final boolean approveReportsFirst = intent.getBooleanExtra(EXTRA_APPROVE_REPORTS_FIRST, false);
//發(fā)射器工廠類
final Class<? extends ReportSenderFactory>[] senderFactoryClasses = (Class<? extends ReportSenderFactory>[]) intent.getSerializableExtra(EXTRA_REPORT_SENDER_FACTORIES);
//ACRA配置器
final ACRAConfig config = (ACRAConfig) intent.getSerializableExtra(EXTRA_ACRA_CONFIG);
try {
//配置器ReportSenderFactory可以配置多個芽腾,所以ReportSender實例的個數(shù)與ReportSenderFactory個數(shù)相同,getSenderInstances方法通過ReportSenderFactory.Class創(chuàng)建ReportSenderFactory實例页衙,并創(chuàng)建ReportSender摊滔,返回ReportSender列表
final List<ReportSender> senderInstances = getSenderInstances(config, senderFactoryClasses);
......
// 獲取本地所有錯誤報表
final File[] reports = locator.getApprovedReports();
//ReportDistributor 用于向所有ReportSender實例分發(fā)報告
final ReportDistributor reportDistributor = new ReportDistributor(this, config, senderInstances);
for (final File report : reports) {
......
//向所有ReportSender實例分發(fā)報告
reportDistributor.distribute(report);
......
}
} catch (Exception e) {
......
}
}
public void distribute(File reportFile) {
......
sendCrashReport(reportFile格式化后);
......
}
//CrashReportData 為錯誤信息格式化后的對象
public void sendCrashReport(CrashReportData errorContent) {
......
//遍歷所有ReportSender 實例
for (ReportSender sender : reportSenders) {
......
//通過ReportSender 實例發(fā)送錯誤報告
sender.send(context, errorContent);
......
}
......
}