Android應用崩潰檢測----Bugly捕獲Crash異常
在開發當中,當程式釋出出去之後,如果出現了崩潰的問題,開發者應該及時獲取在該裝置上導致崩潰的資訊,這對於下一個版本的bug修復幫助極大,所以捕獲Crash,獲取裝置引數資訊和錯誤日誌就顯得尤為重要,現在有很多第三方平臺可以實現這些功能,在我們這裡介紹的是騰訊的Bugly.
註冊APP應用
註冊成功以後在我的產品裡面找到APP,點選“設定”。
進入設定後找到App ID,在初始化的時候會用。
庫檔案匯入
Bugly支援自動整合和手動整合兩種方式,如果您使用Gradle編譯Apk,強烈推薦您使用自動接入方式配置庫檔案。
自動整合步驟
Bugly支援JCenter倉庫和Maven Central倉庫。為了實現更加靈活的配置,Bugly SDK(2.1.5及以上版本)和NDK(SO庫)目前已經分開成兩個獨立的倉庫:
SDK:com.tencent.bugly:crashreport
NDK:com.tencent.bugly:nativecrashreport
其中,整合Bugly NDK時,需要同時整合Bugly SDK。
同時整合SDK和NDK
在Module的build.gradle檔案中新增依賴和屬性配置:
android {
defaultConfig {
ndk {
// 設定支援的SO庫架構
abiFilters 'armeabi' //, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
}
}
}
dependencies {
compile 'com.tencent.bugly:crashreport:latest.release' //其中latest.release指代最新Bugly SDK版本號,也可以指定明確的版本號,例如2.1.9
compile 'com.tencent.bugly:nativecrashreport:latest.release' //其中latest.release指代最新Bugly NDK版本號,也可以指定明確的版本號,例如3.0
}
同時整合Bugly SDK和NDK的配置如下圖所示,後續更新Bugly SDK和NDK時,只需變更配置指令碼中的版本號即可。
注意:自動整合時會自動包含Bugly SO庫,建議在Module的build.gradle檔案中使用NDK的“abiFilter”配置(如上圖),設定支援的SO庫架構。
如果在新增“abiFilter”之後Android Studio出現以下提示:
NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin.
則在專案根目錄的gradle.properties檔案中新增(如下圖所示):
android.useDeprecatedNdk=true
許可權配置
- 在AndroidManifest.xml中新增許可權:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_LOGS" />
- 請避免混淆Bugly,在Proguard混淆檔案中增加以下配置:
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
這裡沒有做混淆處理,所以只給出Proguard混淆檔案位置。如圖所示:
初始化
獲取APP ID並將以下程式碼複製到專案Application類onCreate()中,Bugly會為自動檢測環境並完成配置:
//為了保證運營資料的準確性,建議不要在非同步執行緒初始化Bugly。
CrashReport.initCrashReport(getApplicationContext(), "註冊時申請的APPID", false);
第三個引數為SDK除錯模式開關,除錯模式的行為特性如下:
- 輸出詳細的Bugly SDK的Log;
- 每一條Crash都會被立即上報;
自定義日誌將會在Logcat中輸出。
建議在測試階段建議設定成true,釋出時設定為false。
此外,Bugly2.0及以上版本還支援通過“AndroidManifest.xml”來配置APP資訊。如果同時又通過程式碼中配置了APP資訊,則最終以程式碼配置的資訊為準。
在“AndroidManifest.xml”的“Application”中增加“meta-data”配置項:
<application
<!-- 配置APP ID -->
<meta-data
android:name="BUGLY_APPID"
android:value="<APP_ID>" />
<!-- 配置APP版本號 -->
<meta-data
android:name="BUGLY_APP_VERSION"
android:value="<APP_Version>" />
<!-- 配置APP渠道號 -->
<meta-data
android:name="BUGLY_APP_CHANNEL"
android:value="<APP_Channel>" />
<!-- 配置Bugly除錯模式(true或者false)-->
<meta-data
android:name="BUGLY_ENABLE_DEBUG"
android:value="<isDebug>" />
</application>
<!-- 不同於“android:versionName”,“BUGLY_APP_VERSION”配置的是Bugly平臺的APP版本號。-->
通過“AndroidManifest.xml”配置後的初始化方法如下:
CrashReport.initCrashReport(getApplicationContext());
Bugly預設從“AndroidManifest.xml”檔案中讀取“VersionName”作為版本號,自定義設定請使用參考“高階設定”。
MultiDex注意事項
MultiDex問題簡介
Android 的classLoader在載入APK的時候限制了class.dex包含的Java方法總數不能超過65535,如果超出會報異常。但是現在隨便一個複雜一點的App,輕而易舉就能超過65535。MultiDex是google為了解決這個問題推出了官方的解決方案。(百度關鍵詞:MultiDex)
如果使用了MultiDex,建議通過Gradle的“multiDexKeepFile”配置等方式把Bugly的類放到主Dex,Bugly 類放到主Dex,方式有兩種(以下兩種方式使用一種即可):
multiDexKeepFile 屬性
在build.gradle
的同級目錄下建立 multiDexKeep.txt,在 multiDexKeep.txt 中配置進需要手動插入的 類的完整路徑 即 包名+類名.class
然後在bulid.gradle裡配置
android {
defaultConfig {
multiDexEnabled true
multiDexKeepFile file('multiDexKeep.txt') // keep specific classes
}
}
multiDexKeepProguard 屬性
在build.gradle
的同級目錄下建立multiDexKeep.pro,multiDexKeep.pro中的配置與混淆檔案的配置相同 使用 -keep class xxxx 指定類名
然後在bulid.gradle裡配置
android {
defaultConfig {
multiDexEnabled true
multiDexKeepProguard file('multidexKeep.pro')
}
}
另外建議在Application類的”attachBaseContext”方法中主動載入非主dex:
public class MyApplication extends SomeOtherApplication {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(context);
Multidex.install(this);
}
}
增加上報程序控制
如果App使用了多程序且各個程序都會初始化Bugly(例如在Application類onCreate()中初始化Bugly),那麼每個程序下的Bugly都會進行資料上報,造成不必要的資源浪費。
因此,為了節省流量、記憶體等資源,建議初始化的時候對上報程序進行控制,只在主程序下上報資料:判斷是否是主程序(通過程序名是否為包名來判斷),並在初始化Bugly時增加一個上報程序的策略配置。
Context context = getApplicationContext();
// 獲取當前包名
String packageName = context.getPackageName();
// 獲取當前程序名
String processName = getProcessName(android.os.Process.myPid());
// 設定是否為上報程序
UserStrategy strategy = new UserStrategy(context);
strategy.setUploadProcess(processName == null || processName.equals(packageName));
// 初始化Bugly
CrashReport.initCrashReport(context, "註冊時申請的APPID", isDebug, strategy);
// 如果通過“AndroidManifest.xml”來配置APP資訊,初始化方法如下
// CrashReport.initCrashReport(context, strategy);
其中獲取程序名的方法“getProcessName”有多種實現方法,推薦方法如下:
/**
* 獲取程序號對應的程序名
*
* @param pid 程序號
* @return 程序名
*/
private static String getProcessName(int pid) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("/proc/" + pid + "/cmdline"));
String processName = reader.readLine();
if (!TextUtils.isEmpty(processName)) {
processName = processName.trim();
}
return processName;
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException exception) {
exception.printStackTrace();
}
}
return null;
}
測試
現在您可以製造一個Crash(建議通過“按鍵”來觸發),來體驗Bugly的能力了。在初始化Bugly的之後,呼叫Bugly測Java Crash介面。
CrashReport.testJavaCrash();
執行到這段程式碼時會發生一個Crash,Logcat的TAG=CrashReportInfo中輸出為:
現在您已經可以在“崩潰”頁面看到剛才觸發的Crash issue了(延遲一般在10s以內)。
如果專案包含了Native工程或者使用了程式碼混淆,建議配置符號表檔案,具體請參考“符號表配置”
總結
其實這篇部落格大部分還是從官方文件拷貝的,因為步驟是相同的,寫出來的東西肯定也是萬變不離其宗,其中在每一步都加了圖片參考,會有一個更直觀的展示。再一個,在multiDex處理時有些需要注意的地方,都已經註明了,如果有疑問和錯誤歡迎各位小夥伴留言批評指正。