Android程式崩潰處理
阿新 • • 發佈:2019-01-01
由於Android手機型號,廠商等很多,我們不能保證測試時候沒問題的程式,在各種手機上都沒問題。
當出現問題,程式崩潰時,我們可以設定對應的監聽,將對應的報錯資訊記錄下來,上傳至伺服器。
一、原理
在Thread類中,有一個介面 UncaughtExceptionHandler
/** * Implemented by objects that want to handle cases where a thread is being * terminated by an uncaught exception. Upon such termination, the handler * is notified of the terminating thread and causal exception. If there is * no explicit handler set then the thread's group is the default handler. */ public static interface UncaughtExceptionHandler { /** * The thread is being terminated by an uncaught exception. Further * exceptions thrown in this method are prevent the remainder of the * method from executing, but are otherwise ignored. * * @param thread the thread that has an uncaught exception * @param ex the exception that was thrown */ void uncaughtException(Thread thread, Throwable ex); }
同時有一個實現該介面的成員變數 defaultUncaughtHandler
/**
* Holds the default handler for uncaught exceptions, in case there is one.
*/
private static UncaughtExceptionHandler defaultUncaughtHandler;
當程式出現未處理的異常(崩潰)時,會呼叫該成員變數的 uncaughtException
方法 ,因此,我們可以在此方法中進行記錄異常資訊,上傳伺服器等操作
二、使用方法
1.寫一個 UncaughtExceptionHandler 的實現類 CrashHandler
package com.czlaite.utils; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.lang.Thread.UncaughtExceptionHandler; import java.text.SimpleDateFormat; import java.util.Date; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Build; import android.os.Environment; import android.util.Log; import android.os.Process; public class CrashHandler implements UncaughtExceptionHandler{ private static final String TAG = "CrashHandler"; private static final boolean DEBUG = true; //路徑,檔名字首,字尾 private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/CrashTest/log/"; private static final String FILE_NAME_PERFIX = "crash"; private static final String FILE_NAME_SUFFIX = ".trace"; //系統預設的異常處理器 private UncaughtExceptionHandler mDefaultCrashHandler; private Context mContext; //單例模式 private static CrashHandler sInstance = new CrashHandler(); private CrashHandler() { } public static CrashHandler getInstance() { return sInstance; } public void init(Context context) { mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); mContext = context.getApplicationContext(); } /** * 這個是最關鍵的函式,當程式中有未被捕獲的異常,系統將會自動呼叫#uncaughtException方法 * thread為出現未捕獲異常的執行緒,ex為未捕獲的異常,有了這個ex,我們就可以得到異常資訊。 */ @Override public void uncaughtException(Thread thread, Throwable ex) { try { //匯出異常資訊到SD卡中 dumpExceptionToSDCard(ex); //這裡可以通過網路上傳異常資訊到伺服器,便於開發人員分析日誌從而解決bug uploadExceptionToServer(); } catch (IOException e) { e.printStackTrace(); } ex.printStackTrace(); //如果系統提供了預設的異常處理器,則交給系統去結束我們的程式,否則就由我們自己結束自己 if (mDefaultCrashHandler != null) { mDefaultCrashHandler.uncaughtException(thread, ex); } else { Process.killProcess(Process.myPid()); } } /** * 將異常資訊存到SD卡中 * @param ex * @throws IOException */ private void dumpExceptionToSDCard(Throwable ex) throws IOException { //如果SD卡不存在或無法使用,則無法把異常資訊寫入SD卡 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { if (DEBUG) { Log.w(TAG, "sdcard unmounted,skip dump exception"); return; } } File dir = new File(PATH); if (!dir.exists()) { dir.mkdirs(); } long current = System.currentTimeMillis(); String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current)); File file = new File(PATH + FILE_NAME_PERFIX + time + FILE_NAME_SUFFIX); try { PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file))); pw.println(time); dumpPhoneInfo(pw); pw.println(); ex.printStackTrace(pw); pw.close(); } catch (Exception e) { Log.e(TAG, "dump crash info failed"); } } /** * 記錄手機對應的 資訊 * @param pw * @throws NameNotFoundException */ private void dumpPhoneInfo(PrintWriter pw) throws NameNotFoundException { PackageManager pm = mContext.getPackageManager(); PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES); pw.print("App Version: "); pw.print(pi.versionName); pw.print('_'); pw.println(pi.versionCode); //android版本號 pw.print("OS Version: "); pw.print(Build.VERSION.RELEASE); pw.print("_"); pw.println(Build.VERSION.SDK_INT); //手機制造商 pw.print("Vendor: "); pw.println(Build.MANUFACTURER); //手機型號 pw.print("Model: "); pw.println(Build.MODEL); //cpu架構 pw.print("CPU ABI: "); pw.println(Build.CPU_ABI); } private void uploadExceptionToServer() { //TODO 將異常資訊上傳至伺服器 } }
2. 在Application初始化的時候,為執行緒設定CrashHandler
package com.czlaite.application;
import com.czlaite.utils.CrashHandler;
import android.app.Application;
import android.content.Context;
public class MyApplication extends Application{
public static Context context;
public static Context getContext(){
return context;
}
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
//為應用設定異常處理器
CrashHandler crashHandler = CrashHandler.getInstance();
crashHandler.init(context);
}
}
最後,別忘了在AndroidManifest.xml中,註冊MyApplication。順便給大家推薦一位大神,任玉剛 。http://my.csdn.net/singwhatiwanna