在Java or Android 開發中,會接觸到各種Exception 先看一下分支結構:
頂級Throwable:
然後細分爲Error(無法處理) 和Exception(可以處理)的分支
接觸到最多的異常安裝頻率來說有這麼幾種:
NullPorinterException 空指針 訪問的對象或者方法等爲Null
ArrayIndexOutOfBoundsException 數組角標越界 比如size = 5 ,get(8),[8]
NumberFormatException 轉換異常 比如 數據 "123a",通過Integer.valueOf()
ParseException 解析異常
ClassCastException 類型轉換異常
ArithmeticException 算術異常 比如1/0
一旦異常發生就會發生不可期的後果,比如數據錯誤,程序崩潰,在Android中 如果異常存在,沒有捕獲可能APP直接就崩潰了 停止運行,用戶體驗極其不友好
怎麼去處理比較優雅呢?
主角登場:---> Thread.UncaughtExceptionHandler
通過源碼可以看出這是個接口 需要實現它
然後 它有個方法需要實現:
void uncaughtException(Thread t, Throwable e);
t代表線程,當前的線程 e是異常的一些信息
那麼如何在Android中進行應用呢?
首先封裝個管理類,做成單例模式,通過init 加載
private CarshHandlerMessage()
{
}
public static CarshHandlerMessage getInstance()
{
return sInstance;
}
public void init(Context context)
{
mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
mContext = context.getApplicationContext();
intent = new Intent(mContext,CarshCauseActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
這裏用的是餓漢式,不過更推薦懶漢式寫法 然後加同步
然後在實現的方法裏寫上回調和業務邏輯
@Override
public void uncaughtException(Thread t, Throwable e)
{
try
{
//保存到本地
exportExceptionToSDCard(e);
//下面也可以寫上傳的服務器的代碼
} catch (Exception e1)
{
e1.printStackTrace();
}
e.printStackTrace();
//如果系統提供了默認的異常處理器,則交給系統去結束程序,否則就自己結束自己
if (mDefaultCrashHandler != null)
{
mDefaultCrashHandler.uncaughtException(t, e);
}
}
完整代碼:
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import com.example.qualityManagement.CarshCauseActivity;
import com.example.suppliermanagement.util.GlobalVar;
import com.google.gson.Gson;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Environment;
public class CarshHandlerMessage implements Thread.UncaughtExceptionHandler
{
private static final String PATH = Environment.getExternalStorageDirectory().getAbsolutePath();
private static final String FILE_NAME_SUFFIX = ".trace";
private static CarshHandlerMessage sInstance = new CarshHandlerMessage();
private Thread.UncaughtExceptionHandler mDefaultCrashHandler;
private Context mContext;
private Object sb;
private Intent intent;
private CarshHandlerMessage()
{
}
public static CarshHandlerMessage getInstance()
{
return sInstance;
}
public void init(Context context)
{
mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
mContext = context.getApplicationContext();
intent = new Intent(mContext,CarshCauseActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
/**
* 當程序中有未被捕獲的異常,系統將會調用這個方法
*
* @param t 出現未捕獲異常的線程
* @param e 得到異常信息
*/
@Override
public void uncaughtException(Thread t, Throwable e)
{
try
{
//保存服務器
exportExceptionToSDCard(e);
} catch (Exception e1)
{
e1.printStackTrace();
}
e.printStackTrace();
//如果系統提供了默認的異常處理器,則交給系統去結束程序,否則就自己結束自己
if (mDefaultCrashHandler != null)
{
mDefaultCrashHandler.uncaughtException(t, e);
}
}
/**
* 獲取e.printStackTrace() 的具體信息,賦值給String 變量,並返回
*
* @param e
* Exception
* @return e.printStackTrace() 中 的信息
*/
public static String getStackTraceInfo(Throwable e) {
StringWriter sw = null;
PrintWriter pw = null;
try {
sw = new StringWriter();
pw = new PrintWriter(sw);
e.printStackTrace(pw);//將出錯的棧信息輸出到printWriter中
pw.flush();
sw.flush();
return sw.toString();
} catch (Exception ex) {
return "printStackTrace()轉換錯誤";
} finally {
if (sw != null) {
try {
sw.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (pw != null) {
pw.close();
}
}
}
/**
* 導出異常信息提交
*
* @param e
*/
private void exportExceptionToSDCard( final Throwable e)
{
new Thread(new Runnable() {
@Override
public void run() {
try {
DeciveInfo info = appendPhoneInfo();
StringBuilder stb = new StringBuilder();
for(int x = 0 ; x < e.getStackTrace().length;x++){
stb.append(e.getStackTrace()[x]).append("\n");
}
stb.append(e.getMessage());
stb.append(getStackTraceInfo(e));
info.setErrMessage(stb.toString());
String json = new Gson().toJson(info);
String path = url;
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.addRequestProperty("msg", json);
connection.setRequestMethod("POST");
connection.setConnectTimeout(60000);
connection.setReadTimeout(60000);
connection.connect();
if(connection.getResponseCode() == 200){
connection.getInputStream();
mContext.startActivity(intent);
}
} catch (NameNotFoundException e1) {
e1.printStackTrace();
} catch (MalformedURLException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}).start();
}
/**
* 獲取手機信息
*/
private DeciveInfo appendPhoneInfo() throws PackageManager.NameNotFoundException
{
PackageManager pm = mContext.getPackageManager();
PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
DeciveInfo info = new DeciveInfo();
info.setVersionCode(String.valueOf(pi.versionCode));
info.setVersionName(pi.versionName);
info.setAndroidVersion(String.valueOf(Build.VERSION.SDK_INT));
info.setInfo(Build.MANUFACTURER + "\n"+Build.MODEL + "\n");
return info;
}
public static class DeciveInfo {
private String versionCode;
private String versionName;
private String date;
private String errMessage;
private String androidVersion;
private String info;
private String threadName;
public String getThreadName() {
return threadName;
}
public void setThreadName(String threadName) {
this.threadName = threadName;
}
public String getVersionCode() {
return versionCode;
}
public void setVersionCode(String versionCode) {
this.versionCode = versionCode;
}
public String getVersionName() {
return versionName;
}
public void setVersionName(String versionName) {
this.versionName = versionName;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getErrMessage() {
return errMessage;
}
public void setErrMessage(String errMessage) {
this.errMessage = errMessage;
}
public String getAndroidVersion() {
return androidVersion;
}
public void setAndroidVersion(String androidVersion) {
this.androidVersion = androidVersion;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
}
注意:
android 9.0上傳信息需要加入網絡動態權限