日志捕獲caughtExceptionHandler的封裝并新增異常重新啟動

開發(fā)中,我們時常會遇到各種各樣的崩潰問題,對于這種問題,我們的做法是try掉,讓android自帶的類UncaughtExceptionHandler捕獲日志.但是對于這種捕獲,不會顯示到手機上,對于后期的維護是一大問題.于是我們就有了自定義該類的做法,捕獲錯誤日志,讓錯誤日志生成文件的形式保存到手機和上傳到服務器.
于是我就寫了一個捕獲日志的工具類: CrashHandlerUtils.java
使用方式需要在Application中申明,初始化

      CrashHandlerUtils.getInstance().init(getApplicationContext());
  默認名稱是 crachLog.創(chuàng)建默認文件夾下
   或者
     CrashHandlerUtils.getInstance().init(getApplicationContext(),"名稱"); 

代碼如下:
UncaughtException處理類,當程序發(fā)生Uncaught異常的時候,有該類來接管程序,并錄錯誤報告.
需要在Application中注冊,為了要在程序啟動時就監(jiān)控整個程序懂盐。


package com.isoftstone.randomchoose.utils;
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.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
 * UncaughtException處理類,當程序發(fā)生Uncaught異常的時候,有該類來接管程序,并記錄錯誤報告.
 * 需要在Application中注冊嘱朽,為了要在程序啟動時就監(jiān)控整個程序。
 */
public class CrashHandlerUtils implements UncaughtExceptionHandler {
    public static final String TAG = "CrashHandler";
    //系統(tǒng)默認的UncaughtException處理類
    private UncaughtExceptionHandler mDefaultHandler;
    //CrashHandler實例
    private static CrashHandlerUtils instance;
    //程序的Context對象
    private Context mContext;
    //用來存儲設備信息和異常信息
    private Map<String, String> infos = new HashMap<String, String>();
    //用于格式化日期,作為日志文件名的一部分
    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //設置保存目錄
    private String name = "crachLog";
    /**
     * 保證只有一個CrashHandler實例
     */
    private CrashHandlerUtils() {
    }

    /**
     * 獲取CrashHandler實例 ,單例模式
     */
    public static CrashHandlerUtils getInstance() {
        if (instance == null)
            instance = new CrashHandlerUtils();
        return instance;
    }
    /**
     * 初始化
     **/
    public void init(Context context) {     
        mContext = context;
        //獲取系統(tǒng)默認的UncaughtException處理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        //設置該CrashHandler為程序的默認處理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }
    /**
     * 初始化
     **/
    public void init(Context context,String dec) {
        init(context);   
        this.name = dec;
    }

    /**
     * 當UncaughtException發(fā)生時會轉入該函數(shù)來處理
     **/
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (!handleException(ex) && mDefaultHandler != null) {
            //如果用戶沒有處理則讓系統(tǒng)默認的異常處理器來處理
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                Log.e(TAG, "error : ", e);
            }
            //退出程序
            // 關閉并重啟應用
            System.gc();
            _restart();
            android.os.Process.killProcess(android.os.Process.myPid());
           // android.os.Process.killProcess(android.os.Process.myPid());
           // System.exit(1);//0表示正常退出,非0表示非正常退出
        }
    }
/** 重啟應用 */
    public void _restart() {
        Intent intent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
    }
    /**
     * 自定義錯誤處理,收集錯誤信息 發(fā)送錯誤報告等操作均在此完成.
     *
     * @param ex
     * @return true:如果處理了該異常信息;否則返回false.
     */
    private boolean handleException(Throwable ex) {
        if (ex == null) {
            return false;
        }
        //收集設備參數(shù)信息
        collectDeviceInfo(mContext);
        //使用Toast來顯示異常信息
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(mContext, "很抱歉,程序出現(xiàn)異常,即將退出.", Toast.LENGTH_SHORT).show();
                Looper.loop();
            }
        }.start();
        //保存日志文件
        saveCatchInfo2File(ex);
        return true;
    }

    /**
     * 收集設備參數(shù)信息
     *
     * @param ctx
     */
    public void collectDeviceInfo(Context ctx) {
        try {
            PackageManager pm = ctx.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
            if (pi != null) {
                String versionName = pi.versionName == null ? "null" : pi.versionName;
                String versionCode = pi.versionCode + "";
                infos.put("versionName", versionName);
                infos.put("versionCode", versionCode);
            }
        } catch (NameNotFoundException e) {
            Log.e(TAG, "an error occured when collect package info", e);
        }
        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                infos.put(field.getName(), field.get(null).toString());
                Log.d(TAG, field.getName() + " : " + field.get(null));
            } catch (Exception e) {
                Log.e(TAG, "an error occured when collect crash info", e);
            }
        }
    }

    /**
     * 獲取存儲路徑
     **/
    private String getFilePath() {
        String file_dir = "";
        // SD卡是否存在
        boolean isSDCardExist = Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
        // Environment.getExternalStorageDirectory()相當于File file=new File("/sdcard")
        //boolean isRootDirExist = Environment.getExternalStorageDirectory().exists();
        if (isSDCardExist) {
            file_dir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/"+name+"/";
        } else {
            // MyApplication.getInstance().getFilesDir()返回的路勁為/data/data/PACKAGE_NAME/files踏堡,其中的包就是我們建立的主Activity所在的包
            // getRootDirectory是/system碌燕,即系統(tǒng)文件夾,要root了才看得到
            file_dir = Environment.getRootDirectory().getAbsolutePath() + "/"+name+"/";
        }
        return file_dir;
    }
    /**
     * 保存錯誤信息到文件中
     *
     * @param ex
     * @return 返回文件名稱, 便于將文件傳送到服務器
     */
    private String saveCatchInfo2File(Throwable ex) {
        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, String> entry : infos.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key + "=" + value + "\n");
        }
        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);
        Throwable cause = ex.getCause();
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();
        String result = writer.toString();
        sb.append(result);
        try {
            String time = formatter.format(new Date());
            String fileName = "crash-" + time + ".txt";
            //獲取外部存儲的路徑倔幼,如果直接在該路徑下創(chuàng)建的文件夾或文件盖腿,是沒有與應用關聯(lián)的
            // 即使應用卸載,遺留下的文件夾或文件仍然存在
            //String file_dir = getFilePath();
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                //  getExternalFilesDir("文件夾名"),則是創(chuàng)建應用私有文件夾翩腐,在Android/data/包名/files/文件夾名
                File file = new File(mContext.getExternalFilesDir("crashlog"), fileName);
                Log.e(TAG, "崩潰日志:" + file.getAbsolutePath());
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }
                if (!file.exists()) {
                    FileOutputStream fos = new FileOutputStream(file);
                    fos.write(sb.toString().getBytes());
                    fos.close();
                }
            }
            return fileName;
        } catch (Exception e) {
            Log.e(TAG, "an error occured while writing file...", e);
        }
        return null;
    }
}

常用工具類地址,在github上utils里面
戳我跳轉 ------ https://github.com/Simonhy
歡迎評論和贊賞,喜歡的話收藏一下.對于上傳到服務器的操作,推薦一篇
https://juejin.im/post/59342ddd0ce46300571d95b5
寫的比較詳細,
歡迎轉載!!!
歡迎轉載!!!
歡迎轉載!!!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末鸟款,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子茂卦,更是在濱河造成了極大的恐慌何什,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件等龙,死亡現(xiàn)場離奇詭異处渣,居然都是意外死亡,警方通過查閱死者的電腦和手機蛛砰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門罐栈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人泥畅,你說我怎么就攤上這事荠诬。” “怎么了位仁?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵柑贞,是天一觀的道長。 經常有香客問我聂抢,道長凌外,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任涛浙,我火速辦了婚禮康辑,結果婚禮上,老公的妹妹穿的比我還像新娘轿亮。我一直安慰自己疮薇,他們只是感情好,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布我注。 她就那樣靜靜地躺著按咒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪但骨。 梳的紋絲不亂的頭發(fā)上励七,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音奔缠,去河邊找鬼掠抬。 笑死,一個胖子當著我的面吹牛校哎,可吹牛的內容都是我干的两波。 我是一名探鬼主播瞳步,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼腰奋!你這毒婦竟也來了单起?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤劣坊,失蹤者是張志新(化名)和其女友劉穎嘀倒,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體局冰,經...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡测蘑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了锐想。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帮寻。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡乍狐,死狀恐怖赠摇,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情浅蚪,我是刑警寧澤藕帜,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站惜傲,受9級特大地震影響洽故,放射性物質發(fā)生泄漏。R本人自食惡果不足惜校摩,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一靶橱、第九天 我趴在偏房一處隱蔽的房頂上張望屑那。 院中可真熱鬧,春花似錦荒适、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至邪财,卻和暖如春陕壹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背树埠。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工糠馆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人怎憋。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓榨惠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子赠橙,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理耽装,服務發(fā)現(xiàn),斷路器期揪,智...
    卡卡羅2017閱讀 134,707評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,304評論 25 707
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,859評論 6 342
  • 夲圣閱讀 254評論 4 4
  • 小丫丫自己努力的穿著褲子掉奄,最后還是把兩條腿伸進一個褲筒里~~~ 我笑道:"你是小笨笨?還是小聰聰凤薛?" ...
    玉露言馨閱讀 193評論 0 1