這個崩潰日志的收集個人感覺還是很不錯的喷鸽,因為你可以通過這里面發(fā)現(xiàn)一些意想不到的Crash部脚。
基本沒什么步驟:
創(chuàng)建一個crash收集類(Crash.java)
/**
* Created by DaFengA on 2017/11/18.
*/
public class Crash implements Thread.UncaughtExceptionHandler{
private Context mContext;
public Crash(Context context) {
Thread.setDefaultUncaughtExceptionHandler(this);
mContext = context;
}
/**
* @param thread 當(dāng)前線程
* @param throwable 異常信息
*/
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
}
}
2個要點译秦,一是讓Crash類實現(xiàn)UncaughtExceptionHandler远搪,再實現(xiàn)方法 uncaughtException();
二是在構(gòu)造方法中寫入 Thread.setDefaultUncaughtExceptionHandler(this) 只有走了這句劣纲,系統(tǒng)才會把crash交給你處理,而處理的場所就是uncaughtException()谁鳍,越早走越好味廊,否則在這句代碼之前的錯誤就收集不到了,所以把他放在Application中棠耕。
public class BaseApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
new Crash(this);
}
}
現(xiàn)在App中的錯誤余佛,系統(tǒng)已經(jīng)交給了你的處理類處理了。
放張圖:
這個是大部分系統(tǒng)處理Crash的情況窍荧,直接關(guān)掉app辉巡;但是MIUI不一樣,他會重啟錯誤Activity的前一個Activity蕊退,如果當(dāng)前Activity棧只有錯誤的Activity這一個實例郊楣,才會退出App(沒有Activity可以退的了)。
當(dāng)然你已經(jīng)把系統(tǒng)要處理的東西接盤了瓤荔,所以就不會有上述的差異了净蚤,說說uncaughtException()方法中寫什么
保存錯誤到本地是肯定的,給出代碼:
@Override
public void uncaughtException(Thread thread, final Throwable throwable) {
//給用戶提示
new Thread() {
@Override
public void run() {
//只有主線程是有消息隊列的输硝,要調(diào)用Looper.prepare()給子線程創(chuàng)建消息隊列今瀑,再通過Looper.loop()來使消息隊列起作用。
Looper.prepare();
Toast.makeText(mContext, "出現(xiàn)錯誤,請稍后再試!", Toast.LENGTH_SHORT).show();
Looper.loop();
}
}.start();
//檢查讀寫權(quán)限
if (PermissionsCheck.getInstance(mContext).lacksPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}) != null) {
//退出App
SystemClock.sleep(2000);
return;
}
//判斷SD卡的狀態(tài) MEDIA_MOUNTED表示正常
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
//退出App
SystemClock.sleep(2000);
return;
}
//保存錯誤信息到本地
new Thread(){
@Override
public void run() {
try {
//創(chuàng)建文件夾
File folder = new File( Environment.getExternalStorageDirectory().getPath() + "/DaFenga/");
//判斷文件是否存在
if (!folder.exists()) {
//創(chuàng)建包括父目錄的目錄
folder.mkdirs();
}
//獲取當(dāng)前時間
long current = System.currentTimeMillis();
final String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current));
//創(chuàng)建file
File file = new File(Environment.getExternalStorageDirectory().getPath() + "/DaFenga/" + "crash" + ".ttt");
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file, true)));
//保存手機信息和異常信息
PackageManager pm = mContext.getPackageManager();
PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
pw.println("異常時間:" + time);
pw.println("app版本:" + pi.versionName);
pw.println("app版本號:" + pi.versionCode);
pw.println("Android版本:" + Build.VERSION.RELEASE);
pw.println("SDK版本:" + Build.VERSION.SDK_INT);
pw.println("制造商:" + Build.MANUFACTURER);
pw.println("型號:" + Build.MODEL);
pw.println("版本號:" + Build.DISPLAY);
pw.println("唯一識別碼:" + Build.FINGERPRINT);
pw.println("異常信息:");throwable.printStackTrace(pw);
if (pw != null) {
pw.close();
}
} catch (Exception e) {
e.printStackTrace();
Log.e("Crash", "Exception: " + e.getMessage());
}
}
}.start();
//等待一段時間点把,退出APP橘荠。
SystemClock.sleep(2000);
}
給用戶提示,我用的Toast,因為代碼中的mContext是在Applaction中取得的郎逃,想用Dialog就必須要有依附的activity哥童,所以不可以。用Looper的原因注釋中已給出褒翰。
注釋很全贮懈,這里我保存的路徑是
//根目錄下的DaFenga文件夾中的crash.ttt文件
Environment.getExternalStorageDirectory().getPath() + "/DaFenga/" + "crash" + ".ttt"
把.ttt改成.txt打開看看:
這個錯誤日志的上傳,就看個人處理了优训。
還需要注意的一點朵你,上面代碼中的退出App我沒有寫,這個最穩(wěn)妥的方法是自己寫一個工具類型宙,記錄打開的activity撬呢,退出的時候,遍歷記錄的activity關(guān)掉妆兑。實測什么系統(tǒng)退出魂拦,殺進(jìn)程都會出現(xiàn)不能完全退出的情況
//系統(tǒng)退出
System.exit(0);
//殺進(jìn)程
Process.killProcess(android.os.Process.myPid());
給一個退出App的工具類:
/**
* Created by DaFengA on 2017/7/21.
*
*/
public class FinishApp extends Application {
private Map<String, Activity> activityMap = new HashMap<String, Activity>();
private static volatile FinishApp instance;
private FinishApp() {
}
// 單例模式
public static FinishApp getInstance() {
if (null == instance) {
synchronized (FinishApp.class) {
if (instance == null) {
instance = new FinishApp();
}
}
}
return instance;
}
// 將Activity添加到容器中
public void addActivity(Activity activity) {
activityMap.put(activity.getLocalClassName(), activity);
}
// 將Activity添加到容器中
public void removeActivity(Activity activity) {
activityMap.remove(activity.getLocalClassName());
}
// 當(dāng)要退出Activity時毛仪,遍歷所有Activity 并finish
public void exit() {
for (Map.Entry<String, Activity> entry : activityMap.entrySet()) {
entry.getValue().finish();
}
activityMap.clear();
System.exit(0);
}
}
麻煩的就是每個activity的onCreate中添加FinishApp.getInstance().addActivity(this);,onDestroy中移除FinishApp.getInstance().removeActivity(this);都要寫芯勘。退出調(diào)用FinishApp.getInstance().exit();
對于生活理想箱靴,應(yīng)該像宗教徒對待宗教一樣充滿虔誠與熱情!