如何全面Catch你的Crash

針對(duì)于Android開發(fā)的小伙伴來說莽红,對(duì)于Android Monitor的logcat再熟悉不過了,在這里我們可以查看到項(xiàng)目中的一些log信息磷蜀,檢測開發(fā)中出現(xiàn)的一些crash問題包雀。不過由于種種原因谢谦,有的時(shí)候Android Monitor會(huì)閃屏或者crash信息丟失进陡,比較不可控愿阐,不過倘若能將crash文件都存到手機(jī)上的我們自己創(chuàng)建的文件中豈不是更方便省事呢。

那么我們一起來研究如何創(chuàng)建一個(gè)CrashHander來解決

  • 1趾疚、第一步
    由于安全起見缨历,我們需要catch所有類型的問題,那么也就需要實(shí)現(xiàn)Thread.UncaughtExceptionHandler接口盗蟆,首先初始化 UncaughtExceptionHandler戈二, 并設(shè)置該 CrashHander為程序的默認(rèn)處理器
  public void init(Context context) {
        mContext = context;
        // 獲取系統(tǒng)默認(rèn)的UncaughtException處理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 設(shè)置該CrashHandler為程序的默認(rèn)處理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }
  • 2、 第二步
    作為程序員來說喳资,我們?cè)诠ぷ鲗W(xué)習(xí)中的慣性思維是首先catch掉異常,所以接下來腾供,我們重寫uncaughtException方法來處理當(dāng)UncaughtException發(fā)生時(shí)的異常情況(這里的mDefaultHandler是初始化時(shí)的Thread.UncaughtExceptionHandler)仆邓。
@Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (!handleException(ex) && mDefaultHandler != null) {
            // 如果用戶沒有處理則讓系統(tǒng)默認(rèn)的異常處理器來處理
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
            try {
                Thread.sleep(2500);
            } catch (InterruptedException e) {
                Logger.getLogger("error : " + e);
            }
            // 退出程序
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);
        }
    }
  • 3鲜滩、第三步
    之所以方便就是因?yàn)槲覀兛梢宰远x寫入crash的模式、crash的處理方式以及如何收集crash信息节值,發(fā)送crash報(bào)告(返回是否處理了該異常信息)徙硅。
private boolean handleException(Throwable ex) {
        if (ex == null) {
            return false;
        }
        ex.printStackTrace();
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                 //反饋給用戶發(fā)生crash了
                Toast.makeText(mContext, R.string.text_error_crash, Toast.LENGTH_SHORT).show();
                Looper.loop();
            }
        }.start();
        //配置信息,如果在debug模式下
        if (Config.DEBUG) {
            // 收集設(shè)備參數(shù)信息
            collectDeviceInfo(mContext);
            // 保存日志文件
            saveCrashInfo2File(ex);
        }
        //線上的crash的話我們可以借助友盟搞疗,將crash上報(bào)友盟
        PrintsUMengUtil.reportError(mContext, ex);
        return true;
    }
  • 4嗓蘑、第四步
    收集設(shè)備參數(shù)信息,這里包括收集versionName和versionCode到file里
    public void collectDeviceInfo(Context ctx) {
        try {
            //這里的PackageManager匿乃,PackageInfo是Android API 24中提供的
            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 (PackageManager.NameNotFoundException e) {
            Logger.getLogger(TAG, "an error occured when collect package info");
        }
        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                infos.put(field.getName(), field.get(null).toString());
            } catch (Exception e) {
                Logger.getLogger(TAG, "an error occured when collect package info");
            }
        }
    }
  • 5桩皿、第五步
    保存錯(cuò)誤信息到文件中,這里返回的是文件名稱幢炸,便于將文件傳送到服務(wù)器上泄隔。
private String saveCrashInfo2File(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).append("=").append(value).append("\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 {
            long timestamp = System.currentTimeMillis();
            String time = formatter.format(new Date());
            String fileName = "crash-" + time + "-" + timestamp + ".log";
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                String path = "/sdcard/crash_news/";
                File dir = new File(path);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(path + fileName);

                sb.append("\r\n報(bào)錯(cuò)日期:");
                sb.append(new Date(System.currentTimeMillis()).toLocaleString()).append("\r\n");
                printStackTrace(sb, ex);

                fos.write(sb.toString().getBytes());
                fos.close();
            }
            return fileName;
        } catch (Exception e) {
            Logger.getLogger(TAG, "an error occured while writing file...");
        }
        return null;
    }
    public void printStackTrace(StringBuffer sb, Throwable ex) {
        if (sb == null) sb = new StringBuffer();
        StackTraceElement[] trace = ex.getStackTrace();
        synchronized (sb) {
            sb.append(ex.toString() + "\r\n");
            for (int i = 0; i < trace.length; i++) {
                sb.append(" at " + trace[i] + "\r\n");
            }
            Throwable ourCause = ex.getCause();
            if (ourCause != null)
                printStackTraceAsCause(sb, ourCause, trace);
        }
    }
   private void printStackTraceAsCause(StringBuffer sb, Throwable ex, StackTraceElement[]
            causedTrace) {
        // assert Thread.holdsLock(s);
        // Compute number of frames in common between this and caused
        if (sb == null) sb = new StringBuffer();
        StackTraceElement[] trace = ex.getStackTrace();
        int m = trace.length - 1, n = causedTrace.length - 1;
        while (m >= 0 && n >= 0 && trace[m].equals(causedTrace[n])) {
            m--;
            n--;
        }
        int framesInCommon = trace.length - 1 - m;
        sb.append("Caused by: ");
        sb.append(ex + "\r\n");
        for (int i = 0; i <= m; i++) {
            sb.append(" at " + trace[i] + "\r\n");
        }
        if (framesInCommon != 0) {
            sb.append(" ..." + framesInCommon + " more \r\n");
        }

        // Recurse if we have a cause
        Throwable ourCause = ex.getCause();
        if (ourCause != null)
            printStackTraceAsCause(sb, ourCause, trace);
    }
  • 6、第6步
    網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)時(shí)對(duì)onResponse函數(shù)中的宛徊,對(duì)于異常code的存儲(chǔ)佛嬉。
   public void saveLogInfo2File(String s) {

        if (TextUtils.isEmpty(s)) {
            return;
        }

        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");
        }
        sb.append(s);
        try {
            long timestamp = System.currentTimeMillis();
            String time = formatter.format(new Date());
            String fileName = "log-" + time + "-" + timestamp + ".log";
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                String path = "/sdcard/crash_news/";
                File dir = new File(path);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(path + fileName);

                sb.append("\r\nLog日期:");
                sb.append(new Date(System.currentTimeMillis()).toLocaleString() + "\r\n");

                fos.write(sb.toString().getBytes());
                fos.close();
            }
        } catch (Exception e) {
            Logger.getLogger(TAG, "an error occured while writing file..." + e);
        }
    }
  • 7、第七步
    對(duì)Crash進(jìn)行初始化和調(diào)用闸天。此處是在BaseApplication中通過initCrashHandler方法進(jìn)行初始化暖呕。
 private void initCrashHandler() {
        CrashHandler crashHandler = CrashHandler.getInstance();
        crashHandler.init(getApplicationContext());
    }
  • 8、第八步
    在手機(jī)上下載并安裝ES文件瀏覽器苞氮,由于這里自定義的crash文件的存儲(chǔ)路徑為/sdcard/crash_news/缰揪,文件名定義為crash_news了,當(dāng)然了葱淳,這些都可以自行定義钝腺。


    crash_news.png

    打開crash文件就能看見捕獲的crash文件,非常方便,like this:


    crash.jpg

    雖然我們比較不愿意看到crash ,但是面對(duì)crash我們還是不能放過任何一個(gè),這樣做如此一來便可以更加高效便捷的處理問題赞厕。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末艳狐,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子皿桑,更是在濱河造成了極大的恐慌毫目,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诲侮,死亡現(xiàn)場離奇詭異镀虐,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)沟绪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門刮便,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绽慈,你說我怎么就攤上這事恨旱”蔡海” “怎么了?”我有些...
    開封第一講書人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵搜贤,是天一觀的道長谆沃。 經(jīng)常有香客問我,道長仪芒,這世上最難降的妖魔是什么唁影? 我笑而不...
    開封第一講書人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮掂名,結(jié)果婚禮上据沈,老公的妹妹穿的比我還像新娘。我一直安慰自己铆隘,他們只是感情好卓舵,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著膀钠,像睡著了一般掏湾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肿嘲,一...
    開封第一講書人閱讀 49,806評(píng)論 1 290
  • 那天融击,我揣著相機(jī)與錄音,去河邊找鬼雳窟。 笑死尊浪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的封救。 我是一名探鬼主播拇涤,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼誉结!你這毒婦竟也來了鹅士?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤惩坑,失蹤者是張志新(化名)和其女友劉穎掉盅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體以舒,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡趾痘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蔓钟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片永票。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瓦侮,到底是詐尸還是另有隱情艰赞,我是刑警寧澤佣谐,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布肚吏,位于F島的核電站,受9級(jí)特大地震影響狭魂,放射性物質(zhì)發(fā)生泄漏罚攀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一雌澄、第九天 我趴在偏房一處隱蔽的房頂上張望斋泄。 院中可真熱鬧,春花似錦镐牺、人聲如沸炫掐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽募胃。三九已至,卻和暖如春畦浓,著一層夾襖步出監(jiān)牢的瞬間痹束,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來泰國打工讶请, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留祷嘶,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓夺溢,卻偏偏與公主長得像论巍,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子风响,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 一嘉汰、Android Crash說明 程序因未捕獲的異常而突然終止, 系統(tǒng)會(huì)調(diào)用處理程序的接口UncaughtExc...
    Mur閱讀 3,031評(píng)論 0 6
  • 目錄 認(rèn)識(shí)與作用 Crash的捕獲 Crash信息的獲取 Crash日志寫入上傳 使用方式 一些注意 最后 認(rèn)識(shí)與...
    justCode_閱讀 1,444評(píng)論 1 0
  • /** * 1.全局捕獲異常類 * 2.@authorDell * 3.@date2017/9/19 17:03 ...
    天天大保建閱讀 211評(píng)論 0 0
  • public classCrashHandlerimplementsUncaughtExceptionHandle...
    紅桃小花閱讀 664評(píng)論 0 0
  • 關(guān)于作者米哈里·希斯贊特米哈伊,一直在關(guān)注人類的積極心理體驗(yàn)钞诡,他完成了大量經(jīng)典的研究工作郑现,提出并發(fā)展了心流理論,是...
    趙秀麗閱讀 436評(píng)論 0 0