zoukankan      html  css  js  c++  java
  • android 之 Crash信息的持久化处理

    需求: 持久化运行时异常的信息

    1.CrashHandler.java

    import android.content.Context;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.os.Build;
    import android.os.Looper;
    import android.os.SystemClock;
    import android.widget.Toast;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.PrintWriter;
    import java.io.StringWriter;
    import java.text.SimpleDateFormat;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 保存运行时异常的信息到手机上
     * TODO 异常会保存3次, 应用会重启两次,这个不知道什么原因
     */
    public class CrashHandler implements Thread.UncaughtExceptionHandler {
    
        private final String FILE_NAME_FORMAT = "yyyy_MM_dd";
        private final String FILE_NAME_PREFIXES = "crash_";
        private final String FILE_NAME_EXTENSION = ".log";
        private final String CRASH_TIME_FORMAT = "[yyyy-MM-dd HH:mm:ss:sss]";
        private final String PARENT_DIRECTORY_NAME = "logs";
    
        private Context mContext;
        private Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler;
    
        /** 文件路径 */
        private String filePath;
        private String fileName;
    
        ///////////////////////////////////////////////////////////////////////////
        // 单例模式
        private static CrashHandler mInstance = new CrashHandler();
    
        private CrashHandler() {}
    
        public static CrashHandler getInstance() {
            return mInstance;
        }
        ///////////////////////////////////////////////////////////////////////////
    
        /** 在 自定义的Application 中 调用此方法即可 */
        public void init(Context context) {
            mContext = context;
            filePath = mContext.getExternalFilesDir(PARENT_DIRECTORY_NAME) + File.separator;
            fileName = FILE_NAME_PREFIXES + new SimpleDateFormat(FILE_NAME_FORMAT).format(System.currentTimeMillis()) + FILE_NAME_EXTENSION;
            defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
            Thread.setDefaultUncaughtExceptionHandler(this);
        }
    
        /** 异常发生时,系统回调的函数,我们在这里处理一些操作 */
        @Override
        public void uncaughtException(Thread thread, Throwable ex) {
            // 如果没有自定义处理方式就使用系统的方式
            if (!handleException(ex) && defaultUncaughtExceptionHandler != null) {
                defaultUncaughtExceptionHandler.uncaughtException(thread, ex);
            } else {
                // 让线程停止一会是为了显示Toast信息给用户,然后Kill程序
                SystemClock.sleep(1000);
                android.os.Process.killProcess(android.os.Process.myPid());
                System.exit(0);
            }
        }
    
        /** 自定义 crash 处理 */
        private boolean handleException(Throwable ex) {
            if (ex == null) return true;
            // 保存信息
            saveInfo(mContext, ex);
    
            // 显示提示信息,需要在线程中显示Toast
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    Looper.prepare();
                    Toast.makeText(mContext, "很抱歉,程序遭遇异常,即将退出!", Toast.LENGTH_SHORT).show();
                    Looper.loop();
                }
            }).start();
    
            return true;
        }
    
        /** 保存数据 */
        private String saveInfo(Context context, Throwable ex) {
            StringBuffer sb = new StringBuffer("
    ");
            // crash 的时间
            sb.append("crashTime = ").append(new SimpleDateFormat(CRASH_TIME_FORMAT).format(System.currentTimeMillis())).append("
    ");
            // 设备信息
            for (Map.Entry<String, String> entry : obtainSimpleInfo(context).entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                sb.append(key).append(" = ").append(value).append("
    ");
            }
            // 异常信息
            sb.append(obtainExceptionInfo(ex));
            // 数据存储
            File dir = new File(filePath);
            if (!dir.exists()) dir.mkdir();
            try {
                FileOutputStream fos = new FileOutputStream(filePath + fileName, true); // 追加的方式
                fos.write(sb.toString().getBytes());
                fos.flush();
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return fileName;
        }
    
    
        /** 获取设备信息 */
        private HashMap<String, String> obtainSimpleInfo(Context context) {
            HashMap<String, String> map = new HashMap<String, String>();
            PackageManager mPackageManager = context.getPackageManager();
            PackageInfo mPackageInfo = null;
            try {
                mPackageInfo = mPackageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
    
            map.put("versionName", mPackageInfo.versionName);
            map.put("versionCode", "" + mPackageInfo.versionCode);
    
            map.put("MODEL", "" + Build.MODEL);
            map.put("SDK_INT", "" + Build.VERSION.SDK_INT);
            map.put("PRODUCT", "" + Build.PRODUCT);
    
            return map;
        }
    
    
        /** 获取 crash 信息 */
        private String obtainExceptionInfo(Throwable throwable) {
            StringWriter mStringWriter = new StringWriter();
            PrintWriter mPrintWriter = new PrintWriter(mStringWriter);
            throwable.printStackTrace(mPrintWriter);
            mPrintWriter.close();
            return mStringWriter.toString();
        }
    
    }

    2.自定义的Application 中注册

    public class MyApplication extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            CrashHandler.getInstance().init(this);
            throw new NullPointerException("test crash " + android.os.Process.myPid());
        }
    }

    3.adb shell cat /sdcard/Android/data/<package_name>/files/logs/crash_2016_04_20.log

  • 相关阅读:
    Nginx log日志参数详解
    sea.js模块加载工具
    sea.js模块加载工具
    Airbnb React/JSX 编码规范
    4.2 react patterns(转)
    4.1 react 代码规范
    3.5 compose redux sages
    3.3 理解 Redux 中间件(转)
    3.4 redux 异步
    3.1 开始使用 redux
  • 原文地址:https://www.cnblogs.com/Westfalen/p/5414900.html
Copyright © 2011-2022 走看看