1. 程式人生 > >自己定義Application的未捕獲異常處理

自己定義Application的未捕獲異常處理

ger 捕捉異常 ets 工作 而在 toa new t contex als

近期由於工作原因。進行Android應用開發時發現應用在出現類似空指針等異常時,拋出未被捕獲的異常。Android系統有默認的未捕獲異常處理器,默認行為是結束對應的線程,但並不會直接退出程序,並且在應用還有後臺Service時。服務還一直在執行,假設service在請求網絡時還會拋出一些異常信息,並且在未全然退出的應用中再次使用還會進一步導致異常,這樣對於用戶體驗來說實在不好。

因此,須要在應用無論什麽情況下出現異常後應該直接退出應用,然後重新啟動應用。網上搜了非常多資料。非常多都不能非常好解決問題,由於Android可能會啟動非常多Activity,而在結束應用進程之前應該finish全部Task中的Activity,否則會出現一些不想要的結果,當然還要結束後臺的Service。

Android中有一個Thread.UncaughtExceptionHandler接口,須要實現該接口。並實現當中的uncaughtException函數。以下直接貼上相關代碼:

public class MyCrashHandler implements Thread.UncaughtExceptionHandler {
private static final String TAG = "MyCaughtException";
// 應用
private MyApplication mApplication;
// 系統默認的未捕捉異常處理器
private Thread.UncaughtExceptionHandler mDefaultHandler;

// 自己定義的未捕捉異常處理器
private static MyCrashHandler gCaughtException;

/**
* 單例模式的保障
*/
private CEPM360CrashHandler() {

}

/**
* 單例模式
* @param application
* @return
*/
public static synchronized MyCrashHandler getInstance(Application application) {
if (gCaughtException == null) {
gCaughtException = new MyCrashHandler(application);
}
return gCaughtException;
}

/**
* 構造函數
* @param application
*/
private MyCrashHandler(Application application) {
mApplication = (MyApplication) application;
// 獲取系統默認的UncaughtExceptionHandler
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
// 設置異常處理器
Thread.setDefaultUncaughtExceptionHandler(this);
}


/**
* 未捕獲異常處理函數
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
Log.e(TAG, "Ocurrs uncaughtException!");
// 打印堆棧
ex.printStackTrace();

Intent intent = new Intent(mApplication, MyService.class);
if (!handleException(ex) && mDefaultHandler != null) {
mDefaultHandler.uncaughtException(thread, ex);
} else {
try {
mApplication.stopService(intent);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

// 退出程序後,啟動應用第一個LoginActivity
intent = new Intent(mApplication, MainActivity.class);
PendingIntent restartIntent =
PendingIntent.getActivity(mApplication,
0,
intent,
Intent.FLAG_ACTIVITY_NEW_TASK);
AlarmManager alarmManager = (AlarmManager)
mApplication.getSystemService(Context.ALARM_SERVICE);
alarmManager.set( AlarmManager.RTC,
System.currentTimeMillis() + 1000,
restartIntent);

mApplication.exit();
}

/**
* 異常處理函數
* @param ex
* @return
*/
private boolean handleException(Throwable ex) {
if (ex == null) {
return false;
}

// 重新啟動一個線程顯示異常信息
new Thread() {
@Override
public void run() {
Looper.prepare();
showExceptionToast();
Looper.loop();
}
}.start();
return true;
}

/**
* 顯示發生異常Toast,確認後重新啟動應用
*/
private void showExceptionToast() {
Toast toast = Toast.makeText(mApplication,
"非常抱歉,“CEPM360”已停止執行,即將重新啟動", Toast.LENGTH_SHORT);
// 設置居中顯示
toast.setGravity(Gravity.CENTER, 0, 0);

LinearLayout toastLayout = (LinearLayout) toast.getView();
toastLayout.setLayoutDirection(LinearLayout.HORIZONTAL);

ImageView image = new ImageView(mApplication);
image.setImageResource(R.drawable.exception_picture);
toastLayout.addView(image, 0);

toast.setView(toastLayout);
toast.show();
}
}

上面是自己定義的未捕獲異常處理器。要想讓其發揮作用,須要配合Application,默認情況下Android不須要你手動實現或實例化一個APPlication對象。但自己定義未捕獲異常處理中要finish全部的activity。但在application中沒有對應的Task相關API,就沒有辦法拿到當前應用在Task中的全部Activity,因此須要我們自己實現一個Application類,並維護一個Activity列表,當啟動一個Activity時將其增加該列表,當出現未捕獲異常時,會逐一finish掉這裏的全部Activity。

public class MyApplication extends Application {

// Activity列表,用來全然退出應用
private List<Activity> mActivities = new ArrayList<Activity>();
// 共享數據
private Map<String, Object> mAppSharedData = new HashMap<String, Object>();

@Override
public void onCreate() {
super.onCreate();
MyCrashHandler.getInstance(this);
}

/**
* 獲取應該共享數據
* @return
*/
public Map<String, Object> getAppSharedData() {
return mAppSharedData;
}

/**
* 加入一個共享Map元素
* @param map
*/
public void addSharedData(Map<String, Object> map) {
mAppSharedData.putAll(map);
}

/**
* 加入一個共享元素
* @param string
* @param object
*/
public void addSharedData(String string, Object object) {
mAppSharedData.put(string, object);
}

/**
* 獲取共享元素值
* @param string
* @return
*/
public Object getValue(String string) {
return mAppSharedData.get(string);
}

/**
* 當啟動一個activity時,加入到該列表中
* @param activity
*/
public void addActivity(Activity activity) {
mActivities.add(activity);
}

/**
* 退出應用
*/
public void exit() {
// 循環退出Activity
try {
for (Activity activity : mActivities) {
if (activity != null)
activity.finish();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 最後退出虛擬機
System.exit(0);
}
}

@Override
public void onLowMemory() {
super.onLowMemory();
System.gc();
}

}

上面的mAppSharedData映射表是用於應用的數據共享,不是本文討論重點。這裏重點指出:在MyCrashHandler 中的uncaughtException函數中完畢異常的全部處理。包含停止Service和Activity。在發送重新啟動廣播之後,退出虛擬機。這裏也能夠使用以下的代碼:

android.os.Process.killProcess(android.os.Process.myPid());

自己定義Application的未捕獲異常處理