Google Acra源碼研究報(bào)告

作者:lds(lds2012@gmail.com)

日期:2017-03-24

一. 項(xiàng)目簡(jiǎn)介

ACRA是Google推出的開源Android應(yīng)用Crash reports框架。本文主要針對(duì)其最新的源碼(v4.9.2)進(jìn)行學(xué)習(xí)研究移怯,目的是了解在Android平臺(tái)上處理未捕獲異常,并在崩潰時(shí)收集各種設(shè)備及上下文信息,并生成崩潰報(bào)告肉渴,保存到本地文件柔滔,并使用合適的機(jī)制將所有的崩潰報(bào)告上報(bào)給服務(wù)器等一整套開源解決方案泡徙。

項(xiàng)目主頁及源碼:https://github.com/ACRA/acra

二. 模塊及關(guān)鍵類

模塊劃分及關(guān)鍵類說明

package class
org.acra ACRA 入口
ErrorReporter 異常處理器
org.acra.builder ReportBuilder 異常報(bào)告構(gòu)建器
ReportExecutor 異常報(bào)告執(zhí)行器
org.acra.collector CrashReportDataFactory 異常報(bào)告收集數(shù)據(jù)工廠
org.acra.config ACRAConfiguration ACRA配置
org.acra.collections - 集合類
org.acra.dialog CrashReportDialog 崩潰Dialog
org.acra.file ReportLocator 報(bào)告文件定位
org.acra.http DefaultHttpRequest HTTP網(wǎng)絡(luò)請(qǐng)求
org.acra.legacy LegacyFileHandler 傳統(tǒng)舊文件升級(jí)處理器
org.acra.log ACRALog 日志接口
org.acra.model Element 日志報(bào)告字段元素
org.acra.prefs SharedPreferencesFactory Pref工廠
org.acra.security KeyStoreHelper https KeyStore幫助類
org.acra.sender ReportSender 異常報(bào)告發(fā)送器接口
org.acra.util - 工具類

三. ACRA類

首先看如何初始化ACRA:

public static void init(Application app, ACRAConfiguration config, boolean checkReportsOnApplicationStart);

參數(shù):

  • application:應(yīng)用實(shí)例该肴,這個(gè)實(shí)例會(huì)被ACRA以靜態(tài)變量hold住葛假,看起來會(huì)有內(nèi)存泄漏問題障陶,但其實(shí)沒問題。
  • config:自定義配置
  • checkReportsOnApplicationStart:是否在應(yīng)用啟動(dòng)的時(shí)候檢查并上傳報(bào)告桐款。

具體的異常處理都是由 ErrorReportor 來處理的咸这,并以靜態(tài)變量被ACRA hold住,保證其生命周期和application一致魔眨。它其實(shí)是一個(gè) Thread.UncaughtExceptionHandler , 可以捕獲到未捕捉的異常媳维。

在初始化的時(shí)候,為了兼容舊版本的報(bào)告文件遏暴,提供了一個(gè)將舊版本文件升級(jí)到新版本文件的機(jī)制侄刽。

并且提供配置,允許在啟動(dòng)的時(shí)候刪除舊應(yīng)用版本遺留下來的舊異常報(bào)告文件朋凉。(升級(jí)app后眨补,之前版本遺留下來的文件被視為舊文件。)

也提供選項(xiàng)珍昨,允許在啟動(dòng)的時(shí)候刪除所有不合法的報(bào)告文件木蹬。

在啟動(dòng)的時(shí)候,也會(huì)上傳所有合法的異常報(bào)告文件亲怠。注意使用了獨(dú)立的線程里的Service進(jìn)行發(fā)送操作所计,以實(shí)現(xiàn)和應(yīng)用主進(jìn)程進(jìn)行隔離區(qū)分。

四. 捕捉崩潰(ErrorReporter)

ErrorReporter實(shí)現(xiàn)了Thread.UncaughtExceptionHandler接口团秽,并自己注冊(cè)到應(yīng)用主線程上主胧,它是一個(gè)單例對(duì)象,用于手機(jī)崩潰上下文數(shù)據(jù)习勤,并發(fā)送崩潰報(bào)告踪栋。

當(dāng)崩潰發(fā)生時(shí),它收集崩潰時(shí)的上下文數(shù)據(jù)(例如設(shè)備图毕,系統(tǒng)夷都,崩潰stack trace等信息),并將其寫入應(yīng)用私有目錄(/data/data/packageName) 下的一個(gè)崩潰報(bào)告文件中予颤。

這個(gè)崩潰報(bào)告文件會(huì)被發(fā)送的時(shí)機(jī)由:

  • 如果崩潰配置為silent模式或toast模式损肛,崩潰報(bào)告文件會(huì)立即被發(fā)送
  • 每次程序啟動(dòng)的時(shí)候,上一次沒有被發(fā)送則被發(fā)送
  • 如果崩潰配置為notification荣瑟,則會(huì)彈出對(duì)話框治拿,用戶點(diǎn)擊發(fā)送才會(huì)被發(fā)送。

首先會(huì)收集收集的 Configuration 數(shù)據(jù)笆焰,這個(gè)數(shù)據(jù)收集起來消耗比較大劫谅,因此只有當(dāng)準(zhǔn)備report的時(shí)候才進(jìn)行收集。

所有的崩潰報(bào)告都是由各種不同的收集器來收集起來的,然后通過 CrashReport DataFactory 報(bào)告工廠來生成報(bào)告捏检。

在設(shè)置新的 Thread uncaught Exception Handler 之類荞驴,先保存舊的handler,因?yàn)閔andler只能設(shè)置一個(gè)贯城,保存舊的熊楼,今后可以去調(diào)用舊的handler,讓它也有可能來處理異常能犯。

4.1 當(dāng)崩潰發(fā)生時(shí)的處理工作

當(dāng)崩潰發(fā)生的時(shí)候鲫骗,被 ErrorReporter 的捕獲到,會(huì)使用 ReportBuilder 來構(gòu)建崩潰報(bào)告踩晶。其包括了崩潰的異常执泰,線程,自定義數(shù)據(jù)等信息渡蜻。

具體的崩潰處理术吝,是交給 ReportExecutor 來處理的。

4.2 當(dāng)崩潰發(fā)生后的清理工作

在崩潰發(fā)生以后茸苇,ACRA還會(huì)去幫助application正確的關(guān)閉上一個(gè)activity排苍,清理并關(guān)閉進(jìn)程。

清理工作包括:

  1. 關(guān)閉上一個(gè)activity(調(diào)用其finish方法)
  2. 關(guān)閉所有services
  3. 殺掉進(jìn)程并退出應(yīng)用

首先需要記錄上一個(gè)activity学密。原理是在4.0版本以后調(diào)用 application 的方法來注冊(cè) activity 的生命周期監(jiān)聽函數(shù)纪岁,并使用weakReference來記錄上一次啟動(dòng)的activity(最后一個(gè)onCreate的activity)

  public LastActivityManager(Application app) {
    application.registerActivityLifecycleCallbacks(() -> {
        void onActivityCreated(Activity act, Bundle bundle) {
          lastActivityCreated = new WeakReference<Activity>(act);
        }
    });
  }

然后需要找到當(dāng)前進(jìn)程里的所有service,并調(diào)用stopService來關(guān)閉则果。

最后kill掉線程,并退出漩氨。

android.os.Process.killProcess(android.os.Process.myPid());
System.exit(10);

參見:org.acra.builder.LastActivityManager

參見:org.acra.util.ProcessFinisher

五. 異常報(bào)告處理器(ReportExecutor)

ReportExecutor負(fù)責(zé)收集和處理異常報(bào)告西壮。

5.1 生成崩潰報(bào)告

當(dāng)崩潰發(fā)生時(shí),崩潰的異常等信息交給 ReportExecutor叫惊,然后轉(zhuǎn)交給 CrashReportDataFactory 來生成具體的崩潰報(bào)告款青。

5.2 保存崩潰報(bào)告到文件

之后將報(bào)告保存到本地文件中。

注意:ACRA寫報(bào)告文件是用的同步操作霍狰,在當(dāng)前線程進(jìn)行阻塞IO操作抡草,而不是在開啟一個(gè)工作線程來寫文件。TODO:為什么蔗坯?

具體寫文件的邏輯是有 CrashReportPersister 來實(shí)現(xiàn)康震。具體存到哪個(gè)目錄是由 ReportLocator 來決定的。而具體存成什么文件名宾濒,是根據(jù)崩潰的時(shí)間戳來決定的:

/data/data/packageName/files/{UnapprovedFolder}/{Timestamp}-{IS_SILENT}.stacktrace

例如:

/data/data/test.lds.com.androidtest/app_ACRA-unapproved/2017-03-25T03:34:45.633+00:00-IS_SILENT.stacktrace

ACRA會(huì)將報(bào)告存在不同的兩個(gè)目錄下:

  • app_ACRA-unapproved
  • app_ACRA-approved

崩潰報(bào)告首先都會(huì)存到 unapproved 目錄下腿短,在該目錄下的報(bào)告不會(huì)發(fā)送出去,而允許發(fā)送以后則會(huì)將報(bào)告移動(dòng)到 approved 目錄下,在該目錄下的崩潰報(bào)告才會(huì)被發(fā)送出去橘忱。

5.3 崩潰報(bào)告文件格式

ACRA的崩潰報(bào)告以JSON格式保存在本地中赴魁。

報(bào)告是鍵值對(duì)結(jié)構(gòu),包含各種字段的信息钝诚,具體的字段可參考:

https://github.com/ACRA/acra/wiki/ReportContent

注意:需要上報(bào)哪些字段颖御,不需要上報(bào)哪些字段,這都是可以手動(dòng)配置的凝颇,避免上報(bào)的數(shù)據(jù)過多潘拱。

5.4 崩潰發(fā)送模式

  • 如果模式為silent或toast,則立即將報(bào)告發(fā)送出去祈噪。
  • 如果模式為notification泽铛,則在通知欄顯示一個(gè)notification,來通知用戶該應(yīng)用崩潰了辑鲤,點(diǎn)擊這個(gè)通知會(huì)彈出一個(gè)對(duì)話框盔腔,用戶可選擇是否發(fā)送崩潰報(bào)告,如果用戶選擇發(fā)送月褥,則立即將報(bào)告發(fā)送出去弛随。
  • 如果模式為dialog,則直接顯示dialog給用戶選擇是否發(fā)送宁赤。

六. 崩潰報(bào)告數(shù)據(jù)工廠(CrashReportDataFactory)

ACRA支持各種數(shù)據(jù)的收集舀透,每個(gè)收集器都收集不同類型的數(shù)據(jù),并且它支持配置需要發(fā)送什么類型的數(shù)據(jù)决左,而不發(fā)送什么數(shù)據(jù)愕够。

CrashReportDataFactory負(fù)責(zé)將崩潰時(shí)的數(shù)據(jù),以及根據(jù)配置來收集不同的上下文數(shù)據(jù)佛猛,將其封裝成一個(gè)崩潰報(bào)告對(duì)象(CrashReportData)惑芭。

崩潰報(bào)告其實(shí)是一個(gè)關(guān)鍵對(duì),包含各種不同字段的數(shù)據(jù)继找,并可以在寫到文件的時(shí)候輸入json格式數(shù)據(jù)遂跟。

七. 崩潰報(bào)告發(fā)送器(SenderServiceStarter)

當(dāng)崩潰報(bào)告生成以后,并允許發(fā)送時(shí)婴渡,會(huì)交由 SenderServiceStarter 來負(fù)責(zé)啟動(dòng) SenderServie 服務(wù)來在厚愛發(fā)送報(bào)告幻锁。

八. 后臺(tái)發(fā)送崩潰報(bào)告服務(wù)(SenderService)

SenderService 負(fù)責(zé)具體的將崩潰報(bào)告發(fā)送出去的工作。

注意該Service是在另一個(gè)進(jìn)程中(名為acra的進(jìn)程)來和應(yīng)用的主進(jìn)程進(jìn)行隔離边臼。

其繼承于 IntentService 哄尔,非常適合于短時(shí)間的后臺(tái)操作,并在工作線程執(zhí)行柠并。每次執(zhí)行完畢后則會(huì)關(guān)閉自己究飞。

首先它會(huì)將所有 unapproved 目錄下的報(bào)告都移動(dòng)到 approved 目錄置谦,然后將該目錄下的所有報(bào)告進(jìn)行批量分發(fā)。因?yàn)榭赡軙?huì)注冊(cè)多種報(bào)告發(fā)送方法(如郵件亿傅、自定義服務(wù)器腳本等)媒峡,所以具體的報(bào)告分發(fā)工作都交由 ReportDistributor 來處理。

每一次Service啟動(dòng)的時(shí)候葵擎,都只會(huì)最多發(fā)送 MAX_SEND_REPORTS 即5封報(bào)告來避免網(wǎng)絡(luò)負(fù)載過大谅阿。

九. 崩潰報(bào)告分發(fā)器(ReportDistributor)

ReportDistributor 負(fù)責(zé)將某一個(gè)崩潰報(bào)告分發(fā)給所有注冊(cè)的報(bào)告發(fā)送器。具體支持哪些發(fā)送器是由application上的注解來決定的酬滤。

一共有三種發(fā)送器(對(duì)應(yīng)注解ReportsCrashes):

  1. 沒有發(fā)送器 NullSender
  2. 郵件發(fā)送器 EmailIntentSender (對(duì)應(yīng)注解 mailTo )
  3. 自定義服務(wù)器發(fā)送器 HttpSender(對(duì)應(yīng)注解 fromUri )

分發(fā)的時(shí)候會(huì)將崩潰報(bào)告從文件讀入到內(nèi)存签餐,即將文件里的JSON反映射為 CrashReportData 對(duì)象。然后再將其分發(fā)出去盯串。

崩潰報(bào)告分發(fā)以后則會(huì)立即刪除崩潰文件氯檐。

注意:如果在從文件解析JSON的過程中出現(xiàn)異常,即無法解析JSON文件体捏,則會(huì)直接刪除該報(bào)告冠摄,因?yàn)檫@個(gè)報(bào)告很可能是不合法的格式。而如果是分發(fā)過程中出現(xiàn)異常几缭,則不會(huì)刪除崩潰文件河泳。

十. 崩潰報(bào)告發(fā)送器

ACRA支持多種發(fā)送崩潰報(bào)告的方法。發(fā)送器支持在錯(cuò)誤情況下重試的機(jī)制 RetryPolicy 年栓。

10.1 發(fā)送給自定義服務(wù)器(fromUri)

可以將崩潰報(bào)告發(fā)送給自定義服務(wù)器上的某個(gè)腳本拆挥,腳本的url在注解中的 fromUri 中配置。

支持在config中配置某抓,是否需要使用 BasicAuth 來進(jìn)行用戶認(rèn)證纸兔,可配置 username/password 。

還可以在config中配置http的超時(shí)時(shí)間否副,headers等信息汉矿。

具體的HTTP網(wǎng)絡(luò)請(qǐng)求是由 DefaultHttpRequest 類來實(shí)現(xiàn)的。它的內(nèi)部實(shí)現(xiàn)是使用 HttpURLConnection 來實(shí)現(xiàn)HTTP請(qǐng)求的副编。并實(shí)現(xiàn)了https,BasicAuth的支持流强。

崩潰報(bào)告的內(nèi)容會(huì)直接以輸出流的形式傳給服務(wù)器痹届。

10.2 發(fā)送給指定郵箱(mailTo)

可以將報(bào)告以郵件的形式發(fā)送給指定的郵箱地址,但是需要注意的是這里并沒有實(shí)現(xiàn)郵件發(fā)送功能打月,而僅僅是使用Intent來喚起設(shè)備上直接發(fā)送郵件的郵件客戶端來進(jìn)行發(fā)送队腐。即對(duì)于用戶而言,發(fā)送的時(shí)候程序會(huì)跳到郵件客戶端編寫新郵件的頁面奏篙,并且收件人和發(fā)送的內(nèi)容都可以填寫好了柴淘,點(diǎn)擊發(fā)送即可迫淹。

崩潰報(bào)告會(huì)在郵件的正文(純文本)的形式發(fā)送,每一條數(shù)據(jù)一行为严,以“=”進(jìn)行key和value的分隔符, 例如:

APP_VERSION_CODE=1
USER_CRASH_DATE=2017-03-25T03:34:45.633+00:00
PRODUCT=sdk_google_phone_x86

十一. Collector信息收集器

ACRA可以收集各種各樣的崩潰上下文數(shù)據(jù)敛熬,則從架構(gòu)上它將所有信息進(jìn)行分類,交給不同的收集器去收集第股,也支持配置去過濾需要的收集器來收集不同的信息应民。

11.1 應(yīng)用日志收集器(LogCatCollector)

收集崩潰時(shí)最近300行的Logcat日志輸出內(nèi)容∠ξ牵可支持收集不同緩存區(qū)的日志诲锹。

緩存區(qū):

  • main,查看主要日志緩沖區(qū)(默認(rèn)值)
  • events涉馅,查看包含事件相關(guān)消息的緩沖區(qū)归园。
  • radio, 查看包含無線裝置/電話相關(guān)消息的緩沖區(qū)稚矿。

收集這些信息需要使用執(zhí)行終端命令 logcat 來實(shí)現(xiàn)庸诱,并支持根據(jù)當(dāng)前的pid進(jìn)行過濾。

List<String> commandLine = new ArrayList<>();
commandLine.add("logcat");
// ...  

final Process process = 
  new ProcessBuilder().command(commandLine).redirectErrorStream(true).start();
// process.getInputStream()

收集到的logcat日志會(huì)以 \n 作為分隔符盐捷,拼接成一個(gè)字符串偶翅。

收集logcat信息需要權(quán)限:android.permission.READ_LOGS, 并且系統(tǒng)版本在Android 4.1及以上碉渡。

logocat命令官方文檔:https://developer.android.com/studio/command-line/logcat.html

11.2 系統(tǒng)日志收集器(DropBoxCollector)

Android內(nèi)有一個(gè)名為DropBoxManager的Service聚谁,用于持久化的記錄系統(tǒng)日志或程序崩潰日志。

DropBoxManager 是 Android 在 Froyo(API level 8) 引入的用來持續(xù)化存儲(chǔ)系統(tǒng)數(shù)據(jù)的機(jī)制, 主要用于記錄 Android 運(yùn)行過程中, 內(nèi)核, 系統(tǒng)進(jìn)程, 用戶進(jìn)程等出現(xiàn)嚴(yán)重問題時(shí)的 log, 可以認(rèn)為這是一個(gè)可持續(xù)存儲(chǔ)的系統(tǒng)級(jí)別的logcat.

DropBox可以記錄到如下的日志:

  • crash: 崩潰時(shí)
  • native_crash: 當(dāng)調(diào)用 NativeCrashReporter.run() 時(shí)滞诺。
  • WTF:調(diào)用 Log.wtf()Log.wtfQuiet()時(shí)
  • ANR:應(yīng)用程序未響應(yīng)時(shí)
  • watchdog: 如果WatchDog 檢測(cè)到系統(tǒng)進(jìn)程(system_server)出現(xiàn)問題時(shí)形导。
  • lowmem: 內(nèi)存不足時(shí)
  • 等等

收集這些信息需要獲取 DropBoxManager 的 Service 對(duì)象即可:

final DropBoxManager dropbox = 
  (DropBoxManager) context.getSystemService(Context.DROPBOX_SERVICE);
// ...
DropBoxManager.Entry entry = dropbox.getNextEntry(tag, time);

當(dāng)系統(tǒng)出現(xiàn)各種情況時(shí),都會(huì)寫入各種日志习霹,日志類型清單如下(不完全):

SYSTEM_TAGS NOTE
system_app_anr 系統(tǒng)app無響應(yīng)
system_app_wtf 系統(tǒng)app發(fā)生嚴(yán)重錯(cuò)誤
system_app_crash 系統(tǒng)app崩潰
system_app_strictmode 系統(tǒng)app嚴(yán)格模式
system_server_anr system進(jìn)程無響應(yīng)
system_server_wtf system進(jìn)程發(fā)生嚴(yán)重錯(cuò)誤
system_server_crash system進(jìn)程崩潰
data_app_anr 普通應(yīng)用無響應(yīng)
data_app_wtf 普通應(yīng)用發(fā)生嚴(yán)重錯(cuò)誤
data_app_creash 普通應(yīng)用崩潰
data_app_strictmode 普通應(yīng)用嚴(yán)格模式
BATTERY_DISCHARGE_INFO 系統(tǒng)充電日志( BatteryService )
SYSTEM_RECOVERY_LOG 系統(tǒng)恢復(fù)而重啟( /cache/recovery/log文件存在時(shí))
SYSTEM_BOOT 系統(tǒng)開機(jī)日志
SYSTEM_LAST_KMSG 內(nèi)核錯(cuò)誤( /proc/last_kmsg文件存在時(shí))
APANIC_CONSOLE 內(nèi)核錯(cuò)誤( /data/dontpanic/apanic_console文件存在時(shí))
APANIC_THREADS 內(nèi)核錯(cuò)誤( /data/dontpanic/apanic_threads文件存在時(shí))
SYSTEM_RESTART 系統(tǒng)重啟日志
SYSTEM_TOMBSTONE Native進(jìn)程崩潰日志
event_data
netstats_error 網(wǎng)絡(luò)狀態(tài)異常日志( NetworkStatsService )

所有dropbox的文件都以 TAG@timestrap.txt 為名稱保存在系統(tǒng)目錄:

/data/system/dropbox

每次有新文件寫入的時(shí)候朵耕,都會(huì)有系統(tǒng)廣播:

DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED

例如:data_app_crash@1490172449967.txt 文件就記錄了一個(gè)普通app崩潰的日志信息:

Process: com.example.dumptestbyjava
Flags: 0xa8be46
Package: com.example.dumptestbyjava v1 (1.1)
Build: Android/sdk_google_phone_x86/generic_x86:5.1.1/LMY48X/3728910:userdebug/test-keys

java.lang.ArrayIndexOutOfBoundsException: length=3; index=10
    at com.example.dumptestbyjava.MainActivity$3.onClick(MainActivity.java:167)
    at android.view.View.performClick(View.java:4780)
    at android.view.View$PerformClick.run(View.java:19866)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:135)
    at android.app.ActivityThread.main(ActivityThread.java:5254)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

收集logcat信息需要權(quán)限:android.permission.READ_LOGS, 并且系統(tǒng)版本在Android 4.1及以上淋叶。

dropBox介紹參考:http://blog.csdn.net/ljchlx/article/details/8559963
DropBoxManagerService啟動(dòng)說明:http://gityuan.com/2016/06/12/DropBoxManagerService/

DropBox介紹:http://www.reibang.com/p/f9174a8d0a10

11.3 崩潰日志收集器(StacktraceCollector)

收集崩潰時(shí)引發(fā)的異常exception的stack trace阎曹。

這個(gè)比較簡(jiǎn)單,因?yàn)閑xception對(duì)象里包含了stack trace煞檩,直接將其轉(zhuǎn)換為字符串即可处嫌。

// Throwable
public void void printStackTrace(PrintWriter err);

11.4 時(shí)間收集器(TimeCollector)

收集應(yīng)用啟動(dòng)的時(shí)間,和崩潰發(fā)生的時(shí)間斟湃。

應(yīng)用啟動(dòng)的時(shí)間就是調(diào)用 init 方法的時(shí)候熏迹。

崩潰的時(shí)間就是崩潰當(dāng)前的時(shí)間戳。

時(shí)間格式:

yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ

11.5 鍵值對(duì)收集器(SimpleValuesCollector)

收集一些簡(jiǎn)單的鍵值對(duì)凝赛,如:

  • is_silent: 配置中的選項(xiàng)
  • report_id: 每個(gè)崩潰報(bào)告會(huì)生成一個(gè)報(bào)告的唯一識(shí)別碼UUID UUID.randomUUID().toString()
  • installation_id: 每次安裝的時(shí)候會(huì)生成一個(gè)機(jī)器的唯一識(shí)別碼UUID(安裝的時(shí)候會(huì)將其寫入到文件)
  • package_name: 應(yīng)用的包名 context.getPackageName())
  • phone_model: 手機(jī)型號(hào) Build.MODEL
  • android_version: android版本號(hào) Build.VERSION.RELEASE , 例如 5.1.1
  • brand: 手機(jī)品牌 Build.BRAND
  • product: 手機(jī)產(chǎn)品名 Build.PRODUCT
  • file_path: 應(yīng)用私有目錄路徑: /data/data/packageName/files
  • user_ip: 用戶IP , 從networkInterface中獲取的當(dāng)前所有ip

11.6 系統(tǒng)配置收集器(ConfigurationCollector)

收集的信息比較多注暗,包括屏幕尺寸坛缕,觸摸屏類型,系統(tǒng)語言捆昏,鍵盤赚楚,導(dǎo)航,UI模式等信息屡立。

這些信息收集起來其實(shí)比較簡(jiǎn)單直晨,只需要調(diào)用context的方法即可。

getContext().getResources().getConfiguration();

收集信息實(shí)例如下:

{
"compatScreenHeightDp": 511,
"compatScreenWidthDp": 319,
"compatSmallestScreenWidthDp": 320,
"densityDpi": 420,
"fontScale": 1,
"hardKeyboardHidden": "HARDKEYBOARDHIDDEN_YES",
"keyboard": "KEYBOARD_NOKEYS",
"keyboardHidden": "KEYBOARDHIDDEN_NO",
"locale": "en_US",
"mcc": 310,
"mnc": 260,
"navigation": "NAVIGATION_NONAV",
"navigationHidden": "NAVIGATIONHIDDEN_YES",
"orientation": "ORIENTATION_PORTRAIT",
"screenHeightDp": 658,
"screenLayout": "SCREENLAYOUT_SIZE_NORMAL+SCREENLAYOUT_LONG_NO+SCREENLAYOUT_LAYOUTDIR_LTR",
"screenWidthDp": 411,
"seq": 6,
"smallestScreenWidthDp": 411,
"touchscreen": "TOUCHSCREEN_FINGER",
"uiMode": "UI_MODE_TYPE_NORMAL+UI_MODE_NIGHT_NO",
"userSetLocale": false
},

具體參數(shù)含義可參考官方文檔: https://developer.android.com/reference/android/content/res/Configuration.html

11.7 內(nèi)存信息收集器(MemoryInfoCollector)

收集當(dāng)前應(yīng)用最大內(nèi)存(total)膨俐,可用內(nèi)存(available)勇皇,以及dumpsys里的內(nèi)存信息。

內(nèi)存信息使用的就是終端命令焚刺,來獲取跟內(nèi)存有關(guān)的信息敛摘。

dumpsys meninfo

注意dumpsys在很多機(jī)器上使不行的,需要android.permission.DUMP權(quán)限乳愉,但該權(quán)限只有系統(tǒng)應(yīng)用才允許使用兄淫。

Total PSS by process:
    52818 kB: com.google.android.gms (pid 4071)
    50785 kB: system (pid 3455)
    40850 kB: com.google.android.gms.persistent (pid 3672)
    31394 kB: com.google.android.googlequicksearchbox (pid 3720 / activities)
    31273 kB: com.android.systemui (pid 3545)
    19930 kB: com.google.android.googlequicksearchbox:search (pid 3849)
    17980 kB: com.google.android.gms.unstable (pid 4354)
    15836 kB: com.android.phone (pid 3703)
    14464 kB: zygote (pid 3173)
    12383 kB: com.google.android.gms.ui (pid 4423)
     9932 kB: com.android.inputmethod.latin (pid 3618)
     7867 kB: android.process.acore (pid 3746)
     7264 kB: com.google.process.gapps (pid 3797)
     6428 kB: mediaserver (pid 3175)
     5776 kB: test.lds.com.androidtest:acra (pid 7266)
     5254 kB: com.google.android.googlequicksearchbox:interactor (pid 3598)
     4926 kB: com.android.providers.calendar (pid 4179)
     4131 kB: com.android.keychain (pid 4917)
     3638 kB: com.android.defcontainer (pid 4884)
     3166 kB: com.svox.pico (pid 4943)
     2562 kB: logd (pid 1157)
     1906 kB: surfaceflinger (pid 1162)
     1583 kB: drmserver (pid 1171)
      995 kB: vold (pid 1161)
      876 kB: netd (pid 3174)
      763 kB: keystore (pid 1174)
      742 kB: debuggerd (pid 1169)
      702 kB: rild (pid 1170)
      556 kB: adbd (pid 1167)
      456 kB: healthd (pid 1158)
      453 kB: sh (pid 8276)
      437 kB: lmkd (pid 1159)
      437 kB: sh (pid 1166)
      423 kB: sdcard (pid 1856)
      408 kB: /init (pid 1)
      406 kB: dumpsys (pid 8411)
      358 kB: logcat (pid 5268)
      320 kB: ueventd (pid 829)
      299 kB: installd (pid 1173)
      292 kB: servicemanager (pid 1160)

11.8 反射信息收集器(ReflectionCollector)

通過反射獲取到一些常量信息,一般和環(huán)境有關(guān)蔓姚。

  • Build 的常量信息:包括BRAND捕虽,DEVICE等。
  • BuildConfigs 的常量信息:包括debug坡脐,application_id, flavor, version_code, version_name等
  • Environment 的常量信息:包括 getExternalStorageDirectory 等環(huán)境變量(這個(gè)是通過調(diào)用其一系列g(shù)et方法的獲取的)

11.9 屏幕信息收集器(DisplayManagerCollector)

收集屏幕相關(guān)的數(shù)據(jù)泄私,如sizerotation备闲,width晌端,heightMetrics恬砂,RealMetrics咧纠,refreshRate等。

使用 DisplayManager 來獲取 Display 對(duì)象(可能有多個(gè)泻骤,但一般只有一個(gè))

11.10 自定義數(shù)據(jù)收集器(CustomDataCollector)

收集自定義數(shù)據(jù)漆羔,由用戶主動(dòng)插入的自定義數(shù)據(jù)。

11.11 Pref信息收集器(SharedPreferencesCollector)

收集 SharedPreferences 信息狱掂。

有兩種情況:

  1. Application作為context獲得的 default SharedPreferences 對(duì)象里的數(shù)據(jù)演痒。
  2. 在配置中指定 name 的 SharedPreferences 對(duì)象里的所有鍵值對(duì)。

11.12 設(shè)備特性收集器(DeviceFeaturesCollector)

收集設(shè)備支持的特性符欠,如是否有GPS嫡霞,有藍(lán)牙瓶埋,有加速器希柿,openGL es的版本號(hào)等信息诊沪。

收集方法比較簡(jiǎn)單,使用以下代碼即可:

final PackageManager pm = context.getPackageManager();
final FeatureInfo[] features = pm.getSystemAvailableFeatures();

// 獲取 OpenGL Es 版本號(hào)
if (feature.name == null) {
  feature.getGlEsVersion();
}

11.13 手機(jī)設(shè)置收集器(SettingsCollector)

收集手機(jī)設(shè)置相關(guān)信息:

  • SETTINGS_GLOBAL 全局設(shè)置曾撤,如WiFi是否開啟端姚,鈴聲,藍(lán)牙是否開啟挤悉,GPS是否開啟等手機(jī)設(shè)置渐裸。
  • SETTINGS_SYSTEM 系統(tǒng)設(shè)置,音量大小装悲,是否開啟震動(dòng)等設(shè)置昏鹃。
  • SETTINGS_SECURE 隱私設(shè)置,

這些信息也是通過反射來獲取的

// SETTINGS_GLOBAL 
Global.class.getFields();

// SETTINGS_SYSTEM 
System.class.getFields();

// SETTINGS_SECURE 
Secure.class.getFields();

11.14 包信息收集器(PackageManagerCollector)

收集應(yīng)用版本信息诀诊,如versionCode和versionName洞渤。

11.15 設(shè)備唯一標(biāo)識(shí)收集器(DeviceIdCollector)

獲取設(shè)備唯一識(shí)別碼,如IMEI號(hào)信息

final TelephonyManager tm =
  (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
tm.getDeviceId();

需要權(quán)限:android.permission.READ_PHONE_STATE

更多設(shè)備唯一識(shí)別碼獲取參見:http://www.cnblogs.com/lvcha/p/3721091.html

11.16 自定義日志收集器(LogFileCollector)

收集自定義log文件属瓣,可以在配置中指定自定義個(gè)log文件路徑载迄,崩潰報(bào)告則會(huì)將其log文件的內(nèi)容以字符串的形式作為報(bào)告的信息進(jìn)行收集。

11.17 媒體格式收集器(MediaCodecCollector)

收集設(shè)備支持的媒體格式抡蛙,如視頻格式护昧,音頻格式等。

收集這些信息是使用反射 android.media.MediaCodecInfo 類兩個(gè)內(nèi)部類:

  • MediaCodecInfo$CodecCapabilities
  • MediaCodecInfo$CodecProfileLevel

11.18 崩潰線程信息收集器(ThreadCollector)

收集崩潰線程的信息粗截,如 Thread 的 id, name, priority, groupName

這里 Thread 對(duì)象是由 UncaughtExceptionHandleruncaughtException 方法一路傳進(jìn)來的惋耙,指向引發(fā)崩潰的線程。

十二. 結(jié)語

ACRA作為一個(gè)被廣泛使用的Android Crash Report系統(tǒng)慈格,又是Google官方推出怠晴,因此該框架有幾點(diǎn)優(yōu)勢(shì):

  1. 經(jīng)過大量真實(shí)應(yīng)用長(zhǎng)時(shí)間的使用,穩(wěn)定性比較高浴捆。
  2. 因?yàn)镚oogle對(duì)Android的了解蒜田,對(duì)于大量信息的收集比較全面,包括使用反射來收集的信息选泻。
  3. 框架在架構(gòu)上設(shè)計(jì)得比較好冲粤,值得學(xué)習(xí)。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末页眯,一起剝皮案震驚了整個(gè)濱河市梯捕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌窝撵,老刑警劉巖傀顾,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異碌奉,居然都是意外死亡短曾,警方通過查閱死者的電腦和手機(jī)寒砖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嫉拐,“玉大人哩都,你說我怎么就攤上這事⊥衽牵” “怎么了漠嵌?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)盖呼。 經(jīng)常有香客問我儒鹿,道長(zhǎng),這世上最難降的妖魔是什么几晤? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任挺身,我火速辦了婚禮,結(jié)果婚禮上锌仅,老公的妹妹穿的比我還像新娘章钾。我一直安慰自己,他們只是感情好热芹,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布贱傀。 她就那樣靜靜地躺著,像睡著了一般伊脓。 火紅的嫁衣襯著肌膚如雪府寒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天报腔,我揣著相機(jī)與錄音株搔,去河邊找鬼。 笑死纯蛾,一個(gè)胖子當(dāng)著我的面吹牛纤房,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播翻诉,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼炮姨,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了碰煌?” 一聲冷哼從身側(cè)響起舒岸,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎芦圾,沒想到半個(gè)月后蛾派,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年洪乍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了梭依。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡典尾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出糊探,到底是詐尸還是另有隱情钾埂,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布科平,位于F島的核電站褥紫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏瞪慧。R本人自食惡果不足惜髓考,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望弃酌。 院中可真熱鬧氨菇,春花似錦、人聲如沸妓湘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽榜贴。三九已至豌研,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間唬党,已是汗流浹背鹃共。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留驶拱,地道東北人霜浴。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蓝纲,于是被迫代替她去往敵國(guó)和親坷随。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,328評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理驻龟,服務(wù)發(fā)現(xiàn)温眉,斷路器,智...
    卡卡羅2017閱讀 134,716評(píng)論 18 139
  • 2017-10-22 23:46:32 我必須深度檢討我自己翁狐,重度拖延癥患者类溢,現(xiàn)在是周日的23:49分,明明一天時(shí)...
    流年娃娃閱讀 291評(píng)論 1 1
  • 夯實(shí)市區(qū)價(jià)格 拓展縣區(qū)網(wǎng)絡(luò) 打造宴席網(wǎng)點(diǎn) 拉動(dòng)消費(fèi)渠道
    撿書看劍閱讀 171評(píng)論 0 0
  • 下載地址:https://mac.github.com/ or https://desktop.github.co...
    一個(gè)人開到荼蘼閱讀 1,087評(píng)論 0 1