Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 異常崩潰日志,捕捉並保存到本地

Android 異常崩潰日志,捕捉並保存到本地

編輯:關於Android編程

前幾天因為在省公安廳做一個通訊類之類的應用;碰到個問題,就是download人員信息將信息保存到本地數據庫完成的時候,菊花轉還沒有dismission掉程序就崩潰了;當然這種問題是可以排查和猜測的,當時我就猜測是progressBar的問題,

軟件

其實bug很接近,跟progressBar也有關系;就是在Fragment中獲取getActivity是為空的問題;也是醉了;就是上面這個圖片,當同步到100%時候就崩潰了;
最關鍵的還不是說bug問題主要問題是他們公安廳的手機是雙系統的,總而言之一句話就是不能調式手機(開發者模式)安裝只能通過把內存卡拔出來之後在將打包好的apk復制進去,然後插入手機,安裝,查看原因;反正就是坑爹,剛開始我以為找到了問題,可是沒有效果,就是看不見錯誤日志,所以我上網查了查資料,對比別人寫的異常崩潰日志,我自己又改良了一下;

我這裡通過分點的方式在這裡講解代碼主要邏輯代碼都是在重寫的uncaughtException方法中實現的,只是方法寫的比較多,認真看是可以看明白的;

##### 首先創建異常類實現系統默認的接口 ##### 提供抽象的方法供子類操作 ##### 捕捉到異常手機異常信息和設備信息保存到本地

1,在創建異常類之後要決定使用當前類來處理異常;CrashAppLog.java

     public void init(Context context) {

       try {
           if (context == null)
               throw new NullPointerException("Application 的Context不能為空");

           this.mContext = context;

           /**
            * 獲取系統默認的異常處理類
            */
           mUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();

           /**
            * 在獲取系統異常類之前子類可以先做一些初始化的操作
            */
           initParams(this);
           /**
            * 使用當前的類為異常處理類
            */
           Thread.setDefaultUncaughtExceptionHandler(this);
       }catch (Exception e){
           Log.e(TAG, "init - "+e.getMessage());
       }

    }

2,異常類在實現接口Thread.UncaughtExceptionHandler的時候會重寫uncaughtException方法,這個方法就是在程序崩潰或異常的時候執行的;

 /**
     * 此類是當應用出現異常的時候執行該方法
     * @param thread
     * @param throwable
     */
    @Override
    public void uncaughtException(Thread thread, Throwable throwable) {

        try {

            /**
             * hanlderException此方法是是否處理異常日志,如果處理那麼就返回tru              * e,如果處理遇到問題那麼就返回false,交由系統默認處理
             **/
            if (!hanlderException(throwable) && mUncaughtExceptionHandler != null) {

                /**
                 * 如果此異常不處理則由系統自己處理
                 */
                this.mUncaughtExceptionHandler.uncaughtException(thread, throwable);

            }else{

                /**
                 * 可以延遲一秒鐘在退出
                 */
//                Thread.sleep(1000);
                //如果開發者自己處理一場那麼就自己處理,這裡我處理的是退出進程
                android.os.Process.killProcess(android.os.Process.myPid());
                System.exit(1);

            }
        }catch (Exception e) {
            Log.e(TAG, "uncaughtException - "+e.getMessage());
        }
    }
3,收集設備信息和異常日志信息;
 /**
     * 用戶處理異常日志
     * @param throwable
     * @return
     */
    private boolean hanlderException(Throwable throwable) {

        try {

            if (throwable == null)
                return false;

            new Thread(new Runnable() {
                @Override
                public void run() {

                    Looper.prepare();
                    Toast.makeText(mContext, "程序崩潰", Toast.LENGTH_SHORT).show();
                    Looper.loop();
                }
            }).start();

            /**
             * 收集應用信息
             */
            collectCrashLogInfo(mContext);
            /**
             * 將日志寫入文件
             */
            writerCrashLogToFile(throwable);

            /**
             * 限制日子志文件的數量
             */

            limitAppLogCount(LIMIT_LOG_COUNT);

        } catch (Exception e) {
            Log.e(TAG, "hanlderException - " + e.getMessage());
        }
        return false;
    }
4,收集應用信息
/**
     * 獲取應用信息
     * @param mContext
     */
    private void collectCrashLogInfo(Context mContext) {

        try {
            if (mContext == null)
                return ;

            PackageManager packageManager = mContext.getPackageManager();

            if (packageManager != null) {

                PackageInfo packageInfo = packageManager.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);

                if (packageInfo != null) {

                    String versionName = packageInfo.versionName;
                    String versionCode = ""+packageInfo.versionCode;
                    String packName = packageInfo.packageName;

                    crashAppLog.put("versionName",versionName);
                    crashAppLog.put("versionCode",versionCode);
                    crashAppLog.put("packName",packName);

                }
            }


            /**
             * 獲取手機型號,系統版本,以及SDK版本
             */
            crashAppLog.put("手機型號:", android.os.Build.MODEL);
            crashAppLog.put("系統版本", ""+android.os.Build.VERSION.SDK);
            crashAppLog.put("Android版本", android.os.Build.VERSION.RELEASE);

            Field[] fields = Build.class.getFields();

            if (fields != null && fields.length > 0) {

                for (Field field:fields) {

                    if (field != null) {

                        field.setAccessible(true);

                        crashAppLog.put(field.getName(), field.get(null).toString());
                    }
                }
            }

        }catch (Exception e) {
            Log.e(TAG, "collectDeviceInfo - "+e.getMessage());
        }
    }
5,將日志寫入文件
  /**
     * 寫入文件中
     * @param ex
     */
    private void writerCrashLogToFile(Throwable ex) {

        try {

            StringBuffer buffer = new StringBuffer();

            if (crashAppLog != null && crashAppLog.size() >0) {

                for (Map.Entry entry:crashAppLog.entrySet()) {

                    buffer.append(entry.getKey()+":"+entry.getValue()+"\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.flush();
            printWriter.close();

            String result = writer.toString();

            buffer.append("Exception:+\n");

            buffer.append(result);

            writerToFile(buffer.toString());

        }catch (Exception e) {
            Log.e(TAG, "writerCrashLogToFile - "+e.getMessage());
        }
    }


    //將文件寫入

     private void writerToFile(String s) {

        try {
            /**
             * 創建日志文件名稱
             */
            String curtTimer = ""+System.currentTimeMillis();
            if (formate == null) {

                formate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
            }
            String timer = formate.format(new Date());

            String fileName = "crash-"+timer+"-"+curtTimer+".log";
            /**
             * 創建文件夾
             */
            File folder = new File(CAHCE_CRASH_LOG);

            if (!folder.exists())
                folder.mkdirs();

            /**
             * 創建日志文件
             */
            File file = new File(folder.getAbsolutePath()+File.separator+fileName);

            if (!file.exists())
                file.createNewFile();

            FileWriter fileWriter = new FileWriter(file);
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

            bufferedWriter.write(s);
            bufferedWriter.flush();
            bufferedWriter.close();

            sendCrashLogToServer(folder, file);

        }catch (Exception e) {
            Log.e(TAG, "writerToFile - "+e.getMessage());
        }
    }
6,限制日子志文件的數量
/**
     * 最大文件數量
     * @param limitLogCount
     */
    private void limitAppLogCount(int limitLogCount) {

        try {

            File file = new File(CAHCE_CRASH_LOG);

            if (file != null && file.isDirectory()) {

                File[] files = file.listFiles(new CrashLogFliter());

                if(files != null && files.length >0) {

                    Arrays.sort(files, comparator);

                    if (files.length > LIMIT_LOG_COUNT) {

                        for (int i = 0 ; i < files.length - LIMIT_LOG_COUNT ;i++) {

                            files[i].delete();
                        }
                    }

                }
            }

        }catch (Exception e) {
            Log.e(TAG, "limitAppLogCount - "+e.getMessage());
        }
    }

//這裡限制文件的數量我使用的文件類型過濾和排序
 /**
     * 日志文件按日志大小排序
     */
    private Comparator comparator = new Comparator() {
        @Override
        public int compare(File l, File r) {

            if (l.lastModified() > r.lastModified())
                return 1;
            if (l.lastModified() < r.lastModified())
                return -1;

            return 0;
        }
    };

    /**
     * 過濾.log的文件
     */
    public class CrashLogFliter implements FileFilter {

        @Override
        public boolean accept(File file) {

            if (file.getName().endsWith(".log"))
                return true;
            return false;
        }
    }

主要的代碼就是這麼多以上是關鍵代碼,
###### 從init方法初始化 ###### 到捕捉UncatchException方法處理handlerExcception方法 ###### 再到收集手機應用信息collectCrashLogInfo和writerCrashLogToFile錯誤異常日志,最後通過writerToFile寫入文件
主要的步驟就這麼多,其實當我們如果想要設置緩存目錄的路徑或者是設置日志文件最大存儲數量的話,我們可以把這個類寫成抽象類,實現以及方法以供子類調用;
 /**
     * 默認放在內存卡的root路徑
     */
    private String CAHCE_CRASH_LOG = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator;

    /**
     * 抽象方法,
     * 在該類初始化的時候使用
     */
    public abstract void initParams(CrashAppLog crashAppLog);

    /**
     * 發送一場日志文件到服務器
     * @param folder 文件路徑
     * @param file 文件
     */
    public abstract void sendCrashLogToServer(File folder, File file);

 /**
     * 允許最大日志文件的數量
     */
    private int LIMIT_LOG_COUNT = 10;
    //這兩個變量是可以通過父類調用的,比如可以進行對緩存目錄更改和文件數量更改

     public int getLIMIT_LOG_COUNT() {
        return LIMIT_LOG_COUNT;
    }

    public void setLIMIT_LOG_COUNT(int LIMIT_LOG_COUNT) {
        this.LIMIT_LOG_COUNT = LIMIT_LOG_COUNT;
    }

    public String getCAHCE_CRASH_LOG() {
        return CAHCE_CRASH_LOG;
    }

    public void setCAHCE_CRASH_LOG(String CAHCE_CRASH_LOG) {
        this.CAHCE_CRASH_LOG = CAHCE_CRASH_LOG;
    }
    //initParams這個抽象方法拋出父類進行一些操作;
    //sendCrashLogToServer(File folder, File file)這個是在異常日志寫入文件之後進行用開發者進行發送給服務器


所以我們可以另外創建一個類繼承於CrashAppLog實現CrashApphandler

package com.base.crash;

import android.os.Environment;
import android.util.Log;

import java.io.File;

/**
 * Created by 東帥 on 2016/9/6.
 */
public class CrashApphandler extends CrashAppLog {

    public static CrashApphandler mCrashApphandler = null;

//實現單例

    private CrashApphandler(){};
    public static CrashApphandler getInstance() {

        if (mCrashApphandler == null)
            mCrashApphandler = new CrashApphandler();

        return mCrashApphandler;

    }

    @Override
    public void initParams(CrashAppLog crashAppLog) {

        if (crashAppLog != null){

//動態的改變緩存目錄和緩存文件數量
            crashAppLog.setCAHCE_CRASH_LOG(Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"crashLog");
            crashAppLog.setLIMIT_LOG_COUNT(5);
        }
    }

    @Override
    public void sendCrashLogToServer(File folder, File file) {
        //發送服務端
        Log.e("*********", "文件夾:"+folder.getAbsolutePath()+" - "+file.getAbsolutePath()+"");
    }
}
最後要注意的一點就是要在Application中實例化
package com.base.crash;

import android.app.Application;

/**
 * Created by 東帥 on 2016/9/6.
 */
public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        //初始化一下就行了,別忘記了
        CrashApphandler.getInstance().init(this);
    }
}


// *** 最後加上權限




項目我已經上傳到github上了,項目名稱是CrashApp,需要的可以上GitHub下載查看!!如有不足之處請指出,必定虛心學習;謝謝啦!!!!

https://github.com/gifmeryshuai/androidProject.git

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved