崩潰處理,ACRA核心流程簡析

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);
......
  }
......
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市店乐,隨后出現(xiàn)的幾起案子惭载,更是在濱河造成了極大的恐慌,老刑警劉巖响巢,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件描滔,死亡現(xiàn)場離奇詭異,居然都是意外死亡踪古,警方通過查閱死者的電腦和手機含长,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伏穆,“玉大人拘泞,你說我怎么就攤上這事≌砩ǎ” “怎么了陪腌?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長烟瞧。 經(jīng)常有香客問我诗鸭,道長,這世上最難降的妖魔是什么参滴? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任强岸,我火速辦了婚禮,結果婚禮上砾赔,老公的妹妹穿的比我還像新娘蝌箍。我一直安慰自己,他們只是感情好暴心,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布妓盲。 她就那樣靜靜地躺著,像睡著了一般专普。 火紅的嫁衣襯著肌膚如雪悯衬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天脆诉,我揣著相機與錄音甚亭,去河邊找鬼贷币。 笑死,一個胖子當著我的面吹牛亏狰,可吹牛的內容都是我干的役纹。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼暇唾,長吁一口氣:“原來是場噩夢啊……” “哼促脉!你這毒婦竟也來了?” 一聲冷哼從身側響起策州,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤瘸味,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后够挂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體旁仿,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年孽糖,在試婚紗的時候發(fā)現(xiàn)自己被綠了枯冈。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡办悟,死狀恐怖尘奏,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情病蛉,我是刑警寧澤炫加,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站铺然,受9級特大地震影響俗孝,放射性物質發(fā)生泄漏。R本人自食惡果不足惜探熔,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一驹针、第九天 我趴在偏房一處隱蔽的房頂上張望烘挫。 院中可真熱鬧诀艰,春花似錦、人聲如沸饮六。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卤橄。三九已至绿满,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窟扑,已是汗流浹背喇颁。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工漏健, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人橘霎。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓蔫浆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親姐叁。 傳聞我的和親對象是個殘疾皇子瓦盛,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內容