Android处理崩溃的一些实践
對于任何程序來說,崩潰都是一件很難避免的事情,當然Android程序也不例外。在Android程序中,引起崩潰的多屬于運行時異常或者錯誤,對于這些異常我們很難做到類似Checked Exception那樣顯式捕獲,因而最終導致了程序崩潰。本文講介紹一些如何處理崩潰的實踐,比如收集崩潰的stacktrace,甚至如何避免出現程序已停止的對話框。
如何收集崩潰信息
收集崩潰信息,可以更好的修復問題,增強程序的穩定性。Android中的崩潰收集沿用了Java的收集機制,實現起來比較簡單。
1.實現UncaughtExceptionHandler
我們需要實現UncaughtExceptionHandler接口中的uncaughtException方法。該方法體中最常見的操作就是讀取崩潰的stacktrace信息,然后上報到服務器數據便于開發者分析。實現代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | public class SimpleUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { private static final String LOGTAG = "SimpleUncaughtExceptionHandler"; @Override public void uncaughtException(Thread thread, Throwable ex) { //讀取stacktrace信息 final Writer result = new StringWriter(); final PrintWriter printWriter = new PrintWriter(result); ex.printStackTrace(printWriter); String errorReport = result.toString(); Log.i(LOGTAG, "uncaughtException errorReport=" + errorReport); } } |
除此之外,還建議攜帶以下信息發送到服務器,幫助更快定位和重現問題。
- 設備唯一ID(基于IMEI或者Android ID等),方便根據用戶提供的id,查找崩潰的stacktrace
- 設備語言與區域 方便重現
- 應用的版本號
- 設備的系統版本
- 設備類型,如平板,手機,TV等
- 崩潰發生的時間等
注冊默認的異常處理
注冊默認的異常處理就是最后的一步,很簡單,通常建議放在Application的onCreate方法中進行。
| 1 2 3 4 5 6 7 8 9 10 | public class DroidApplication extends Application { private static final String LOGTAG = "DroidApplication"; @Override public void onCreate() { super.onCreate(); Log.i(LOGTAG, "onCreate"); Thread.setDefaultUncaughtExceptionHandler(new SimpleUncaughtExceptionHandler()); } } |
驗證
當我們刻意觸發一個NullPointerException時,過濾日志adb logcat | grep SimpleUncaughtExceptionHandler類似如下信息,則說明成功了。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | I/SimpleUncaughtExceptionHandler(22469): uncaughtException errorReport=java.lang.NullPointerException I/SimpleUncaughtExceptionHandler(22469): at com.droidyue.avoidforceclosedemo.MainActivity.causeNPE(MainActivity.java:22) I/SimpleUncaughtExceptionHandler(22469): at com.droidyue.avoidforceclosedemo.MainActivity.onClick(MainActivity.java:29) I/SimpleUncaughtExceptionHandler(22469): at android.view.View.performClick(View.java:4470) I/SimpleUncaughtExceptionHandler(22469): at android.view.View$PerformClick.run(View.java:18593) I/SimpleUncaughtExceptionHandler(22469): at android.os.Handler.handleCallback(Handler.java:733) I/SimpleUncaughtExceptionHandler(22469): at android.os.Handler.dispatchMessage(Handler.java:95) I/SimpleUncaughtExceptionHandler(22469): at android.os.Looper.loop(Looper.java:157) I/SimpleUncaughtExceptionHandler(22469): at android.app.ActivityThread.main(ActivityThread.java:5867) I/SimpleUncaughtExceptionHandler(22469): at java.lang.reflect.Method.invokeNative(Native Method) I/SimpleUncaughtExceptionHandler(22469): at java.lang.reflect.Method.invoke(Method.java:515) I/SimpleUncaughtExceptionHandler(22469): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858) I/SimpleUncaughtExceptionHandler(22469): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:674) I/SimpleUncaughtExceptionHandler(22469): at dalvik.system.NativeStart.main(Native Method) |
不出現應用崩潰對話框
在Android崩潰的時候,我們都會看到類似這樣的對話框
然而,實際上有些情況下是不需要展示這個對話框的,一個常用的例子,我的程序中一個不太重要的推送服務采用了單獨的進程,當這個進程崩潰時,實際上是可以允許不讓用戶感知的。
如果我們采取主進程仍彈出對話框,其他進程不彈出的策略,那么我們的問題,可以總結成如下三個
- 如何判斷進程為主進程還是其他進程,或者某個進程
- 如何在某些進程不彈出應用崩潰對話框
- 如何在主進程彈出崩潰對話框
既然問題來了,我們就開動挖掘機深挖吧。
進程判定
進行進程判定也比較容易,首先我們需要獲得進程名
| 1 2 3 4 5 6 7 8 9 10 11 12 | public static String getProcessName(Context appContext) { String currentProcessName = ""; int pid = android.os.Process.myPid(); ActivityManager manager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE); for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) { if (processInfo.pid == pid) { currentProcessName = processInfo.processName; break; } } return currentProcessName; } |
判斷主進程,則對比進程名是否和包名相同即可
| 1 | mAppContext.getPackageName().equals(processName) |
判斷為某個進程,在mainifest這樣這樣聲明
| 1 | <service android:name=".DroidService" android:process=":service"></service> |
其對應的完整進程名為com.droidyue.avoidforceclosedemo:service,我們判斷可以使用如下代碼
| 1 | "com.droidyue.avoidforceclosedemo:service".equals(processName); |
不彈框的處理
不彈框的需要做的就是不調用Android默認的異常處理,當異常出現時,收集完信息,執行進程kill即可。
| 1 | android.os.Process.killProcess(android.os.Process.myPid()); |
主進程保持彈窗的處理
想要保持彈窗也比較容易,就是調用Android默認的異常處理。
首先需要獲得Android默認的異常處理,在設置自定的異常處理之前,將Android默認處理保存起來。如下是在自定義異常處理的構造方法中獲取Android默認處理
| 1 2 3 4 | public DroidUncaughtExceptionHandler(Context context) { mAppContext = context.getApplicationContext(); mDefaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); } |
然后在異常處理方法uncaughtException中調用如下方法
| 1 | mDefaultExceptionHandler.uncaughtException(thread, ex); |
注意,如果你的應用崩潰后,不調用Android默認的異常處理,也不進行殺死進程,則進程處于不可交互,即UI點擊無響應狀態。
源碼
本示例源碼,存放在Github,地址為AvoidForceCloseDemo
https://droidyue.com/blog/2015/12/06/practise-about-crash-in-android/
總結
以上是生活随笔為你收集整理的Android处理崩溃的一些实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 滴滴开源Android插件化框架Virt
- 下一篇: Android开源框架源码鉴赏:Virt