美文网首页
捕获异常信息CrashHandler

捕获异常信息CrashHandler

作者: 勤劳的蚂蚁 | 来源:发表于2020-04-07 16:14 被阅读0次

    注意事项:

    1. 建好的文件有时候看不到,需要重启手机或者是调用 MediaScannerConnection.scanFile 方法。scanFile 方法在测试三星机型上有时候没有找到文件夹,需要重新启动手机,当其有文件夹就可以找到该文件了。
    2. 输出日期时间 需要HH,如果为 hh,则下午时间和早上时间区分不开
    3. 通过下面方法将异常捕获在 public void uncaughtException(Thread thread, Throwable ex) 中处理
      //设置该CrashHandler为程序的默认处理器
      Thread.setDefaultUncaughtExceptionHandler(this);
    4. 考虑文件拼接,printwrite 不直接输入文件内容,其会覆盖之前内容,输出编码格式为utf-8,需要OutputStreamWriter 转码。
    5. addUserInfo 可以自己定义需要传递的用户信息。
    6. 服务器上传 可以考虑与后台配合完成
    public class CrashHandler implements Thread.UncaughtExceptionHandler {
    
        /** 文件包名称*/
        private static final String TAG = "TxxCrash";
        /** 文件包名称*/
        private static final String SP_NAME = "TxxCrash";
        /** 存储位置文件夹*/
        private String logdir;
        /** 文件名称*/
        protected static final String FILE_NAME = "txx_crash_log";
        /** 文件后缀*/
        private static final String FORMAT = ".txt";
        /** 系统默认的UncaughtException处理类*/
        private Thread.UncaughtExceptionHandler mDefaultHandler;
        /** 程序的Context对象*/
        private Context mContext;
        /** 用来存储设备信息和异常信息*/
        private Map<String, String> infos = new HashMap<String, String>();
    
        /** 根据时间,定期上传日志*/
        public final static int TIME = 1;
        /** 根据日志大小,上传日志*/
        public final static int MEMORY = 2;
        /** 根据,时间,日志大小,上传日志*/
        public final static int TIME_AND_MEMORY = 3;
        //type
        private int TYPE = 0;
        //first init
        private boolean isFirstInit;
        //first init string
        private final static String IS_FIRST_INIT = "is_first_init";
        //first init time
        private final static String FIRST_INIT_TIME = "first_init_time";
        //时间差
        private int TIME_SIZE;
        //设置的时间间隔
        private int DAYS = 7;
    
    
        /**
         * 获取CrashHandler实例 ,单例模式
         */
        private CrashHandler() {
        }
        public static CrashHandler getInstance() {
            return Bulid.single;
        }
        private static class Bulid {
            private static final CrashHandler single = new CrashHandler ();
        }
        /**
         * 初始化
         *
         * @param context
         * @param type    上传模式
         */
        public void init(Context context, int type) {
            mContext = context.getApplicationContext ();
            //获取系统默认的UncaughtException处理器
            mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
            //设置该CrashHandler为程序的默认处理器
            Thread.setDefaultUncaughtExceptionHandler(this);
            //设置文件保存路径
            logdir = Environment.getExternalStorageDirectory().getAbsolutePath()
                    + File.separator + TAG;
            //设置样式
            this.TYPE = type;
            //时间存储
            saveOrGetTimeType();
        }
    
        /**
         * 初始化
         *
         * @param context 默认按照日志大小,控制日志上传
         */
        public void init(Context context) {
          this.init (context,3);
        }
    
        /**
         * 获取保存时间信息
         */
        private void saveOrGetTimeType() {
            SharedPreferences crash = mContext.getSharedPreferences(SP_NAME,MODE_PRIVATE );
            isFirstInit = crash.getBoolean(IS_FIRST_INIT, true);
            if (isFirstInit) {
                SharedPreferences.Editor edit = crash.edit();
                edit.putBoolean(IS_FIRST_INIT, false);
                edit.putLong(FIRST_INIT_TIME, getTimeToLong());
                edit.commit();
            } else {
                long firstTime = crash.getLong(FIRST_INIT_TIME, 0);
                long currentTime = getTimeToLong();
                TIME_SIZE = (int) ((currentTime - firstTime) / 86400);
            }
        }
    
        /**
         * 获取时间
         */
        private long getTimeToLong() {
            try {
                long time = Calendar.getInstance().getTimeInMillis();
                return time;
            } catch (Exception e) {
                return 0;
            }
        }
    
        /**
         * 当UncaughtException发生时会转入该函数来处理
         */
        @Override
        public void uncaughtException(Thread thread, Throwable ex) {
            if (!handleException(ex) && mDefaultHandler != null) {
                //如果用户没有处理则让系统默认的异常处理器来处理
                mDefaultHandler.uncaughtException(thread, ex);
            } else {
                /** 已经保存数据,不做退出处理*/
    //            try {
    //                Thread.sleep(3000);
    //            } catch (InterruptedException e) {
    //                Log.i(TAG, "uncaughtException: " + e.toString());
    //            }
    //            //退出程序
    //            android.os.Process.killProcess(android.os.Process.myPid());
    //            System.exit(1);
            }
        }
    
    
        /**
         * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
         *
         * @param ex
         * @return true:如果处理了该异常信息;否则返回false.
         */
        private boolean handleException(Throwable ex) {
            Log.e (TAG,"-----------------------------------app 出错了  start----------------------------------------");
            //如果没有SD卡,直接返回
            if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
               Log.e (TAG,"没有外置存储挂载");
                return false;
            }
    
            if (ex == null) {
                Log.e (TAG,"ex + null");
                return false;
            }else {
                Log.e (TAG,"ex "+ex.toString ());
            }
           //收集设备参数信息
            collectDeviceInfo(mContext);
            //保存日志文件
            saveCrashInfoToFile(ex);
            Log.e (TAG,"-----------------------------------app 出错了  end----------------------------------------");
            return true;
        }
    
        /**
         * 并不应该每次崩溃都进行日志上传
         *
         * @param file
         */
        private void svaeCrashInfoToServer(File file,StringBuffer stringBuilder) {
            Log.e (TAG,"日志已经很大了,应该上传服务器");
    
         //上传服务器后 清空数据
            WriteContentTypt(file,stringBuilder.toString (),true);
    
    
    //        new Thread() {
    //            @Override
    //            public void run() {
    //                Looper.prepare();
    //                Toast.makeText(mContext, "日志已经很大了,应该上传服务器", Toast.LENGTH_LONG).show();
    //                Looper.loop();
    //            }
    //        }.start();
        }
    
        /**
         * 收集设备参数信息
         *
         * @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("App Version", versionName);
                    infos.put("App versionCode", versionCode);
                }
    
                //android版本号
                infos.put("OS Version: ",Build.VERSION.RELEASE+"___"+Build.VERSION.SDK_INT);
                //手机制造商
                infos.put(" 产品品牌--Vendor:: ",Build.MANUFACTURER+"_____"+Build.BRAND);
                //手机型号
                infos.put(" Model:: ",Build.MODEL);
                infos.put(" 设备序列号: ",Build.SERIAL);
                //cpu架构
                infos.put(" CPU ABI: ",Build.CPU_ABI);
                infos.put("产品型号 : ",Build.PRODUCT);
            } catch (NameNotFoundException e) {
                Log.i(TAG, "collectDeviceInfo error NameNotFoundException: " + e.toString());
            }
            Log.e (TAG,"设备信息 :"+infos.toString ());
        }
    
        /**
         * 保存错误信息到文件中,应该清楚过长时间的错误信息
         *
         * @param ex
         * @return 返回文件名称, 便于将文件传送到服务器
         */
        private String saveCrashInfoToFile(Throwable ex) {
            // 先获取文件
            File file = getFilePath(logdir, FILE_NAME + FORMAT);
            if(file==null){
                Log.e (TAG,"File 文件 创建失败");
               return "";
            }
    
            final StringBuffer sb = new StringBuffer();
            Date date = Calendar.getInstance().getTime();
            //获取24 小时制,如果写为hh 获取的下午3点 也是 3点
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String times = sdf.format(date);
            sb.append("---------------------txx  start--------------------------"+"\n");
            //可以追加用户信息
            sb.append(addUserInfo());
            sb.append("crash at time: " + times+"\n");
            for (Map.Entry<String, String> entry : infos.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                sb.append(key + "=" + value + "\n");
            }
    
            try {
                Log.e("错误日志文件路径",""+file.getAbsolutePath());
    //            Writer writer = new BufferedWriter (new FileWriter (file));
                Writer writer = new StringWriter ();
                PrintWriter printWriter = new PrintWriter(writer,true);
    //            printWriter.println (sb.toString ());
                ex.printStackTrace(printWriter);
                Throwable cause = ex.getCause();
                /** 原因不存在或者未知*/
                while (cause != null) {
                    cause.printStackTrace(printWriter);
                    cause = cause.getCause();
                }
                printWriter.println ("--------------------end---------------------------");
                String result = writer.toString();
                sb.append(result);
                printWriter.close();
                switch (TYPE) {
                    case 1:
                        //写入本地,清空文本
                        WriteContentTypt(file,sb.toString (),true);
                    case 2:
                        if (checkIsTimeToPush()) {
                            //保存日志到服务器
                            svaeCrashInfoToServer(file,sb);
                        }
                    case 3:
                        //检查日志是否过大
                        if (checkFileIsToBig(file)) {
                            //写入本地,清空文本
    //                        WriteContentTypt(file,sb.toString (),false);
                            //保存日志到服务器
                            svaeCrashInfoToServer(file,sb);
    
                        } else {
                            //写入本地,追加文本
                            WriteContentTypt(file,sb.toString (),false);
                        }
                        break;
                    default:
                        break;
                }
            }catch (Exception e){
                Log.e (TAG,e.getMessage ().toString ());
            }finally {
    
                //放开以上注释,请删除此行
                MediaScannerConnection.scanFile(mContext.getApplicationContext (), new String[]{ logdir }, null, new MediaScannerConnection.OnScanCompletedListener() {
                    @Override
                    public void onScanCompleted(final String path, final Uri uri) {
                        Log.i(TAG, "MediaScannerConnection----- onScanCompleted ");
                    }
                });
            }
    
    
            return null;
        }
    
        /**
         * 按照时间周期上传日志
         */
        private boolean checkIsTimeToPush() {
            if (TIME_SIZE >= DAYS) {
                return true;
            } else {
                return false;
            }
        }
    
        private String addUserInfo() {
         
              return "UserInfo: 无用户信息"+"\n";
           
        }
    
        private boolean checkFileIsToBig(File file) {
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(file);
                int fileSize = fis.available();
                if (fileSize > 1024 * 1024 * 5) {
                    return true;
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return false;
        }
    
        /**
         * 创建文件
         *
         * @param filePath 文件路径
         * @param fileName 文件名称
         * @return
         */
        public  File getFilePath(String filePath,
                                       String fileName) {
            File file = new File(filePath, fileName);
            if (file.exists()) {
                return file;
            } else {
                file = null;
                makeRootDirectory(filePath);//创建文件夹
                try {
                    file = new File(filePath + File.separator + fileName);
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.e (TAG,"getFilePathy Exception :"+e.toString ());
                }
                return file;
            }
        }
    
        /**
         * 创建根目录
         *
         * @param filePath
         */
        public  void makeRootDirectory(String filePath) {
            File file = null;
            try {
                file = new File(filePath);
                if (!file.exists()) {
                    file.mkdir();
                }
            } catch (Exception e) {
                Log.e (TAG,"makeRootDirectory Exception :"+e.toString ());
            }
        }
    
        /**
         * 写入本地文件
         *
         * @param file    文件
         * @param sb      内容
         * @param isClean 是否清空,true:清空,false:保留
         */
        public void WriteContentTypt(File file, String sb, boolean isClean) {
            try {
    //            FileOutputStream fos = new FileOutputStream(file, !isClean);
    //            fos.flush();
    //            for (char c : sb.toString().toCharArray()) {
    //                fos.write(c);
    //            }
    //            fos.flush ();
    //            fos.close();
                OutputStreamWriter oStreamWriter = new OutputStreamWriter(new FileOutputStream(file,!isClean), "utf-8");
                oStreamWriter.append(sb);
                oStreamWriter.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                Log.i(TAG, "WriteContentTypt  FileNotFoundException" );
            } catch (IOException e) {
                e.printStackTrace();
                Log.i(TAG, "WriteContentTypt  IOException" );
            }
            Log.i(TAG, "WriteContentTypt: " + sb.toString());
        }
    
    
    
        private void zip(String src, String dest) throws IOException { //压缩文件夹,为上传做准备。节省流量。
            ZipOutputStream out = null;
            File outFile = new File(dest);
            File fileOrDirectory = new File(src);
            out = new ZipOutputStream(new FileOutputStream(outFile));
            if (fileOrDirectory.isFile()) {
                zipFileOrDirectory(out, fileOrDirectory, "");
            }else {
                File[] entries = fileOrDirectory.listFiles();
                for (int i = 0; i < entries.length; i++) {
                    zipFileOrDirectory(out, entries[i], "");
                }
            }
            if(null != out){
                out.close();
            }
        }
    
        private  void zipFileOrDirectory(ZipOutputStream out, File fileOrDirectory, String curPath) throws IOException {
            FileInputStream in = null;
            if (!fileOrDirectory.isDirectory()){
                byte[] buffer = new byte[4096];
                int bytes_read;
                in = new FileInputStream(fileOrDirectory);
                ZipEntry entry = new ZipEntry (curPath + fileOrDirectory.getName());
                out.putNextEntry(entry);
                while ((bytes_read = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytes_read);
                }
                out.closeEntry();
            }else{
                File[] entries = fileOrDirectory.listFiles();
                for (int i = 0; i < entries.length; i++) {
                    zipFileOrDirectory(out, entries[i], curPath + fileOrDirectory.getName() + "/");
                }
            }
            if (null != in){
                in.close();
            }
        }
        public  void unZip(File srcFile,File desFile) throws IOException {
            GZIPInputStream zis= null;
            FileOutputStream fos = null;
            try {
                //创建压缩输入流,传入源文件
                zis = new GZIPInputStream(new FileInputStream(srcFile));
                //创建文件输出流,传入目标文件
                fos = new FileOutputStream(desFile);
                byte[] buffer= new byte[1024];
                int len= -1;
                //利用IO流写入写出的形式将压缩源文件解压到目标文件中
                while ((len= (zis.read(buffer)))!= -1) {
                    fos.write(buffer,0, len);
                }
            }finally{
                close(zis);
                close(fos);
            }
        }
    
        public  void close(Closeable... closeIO) {
            for(Closeable clo:closeIO) {
                if(clo!=null) {
                    try {
                        clo.close();
                    } catch (IOException e) {
                        e.printStackTrace ();
                        Log.e (TAG,"关闭发生异常"+e);
                    }
                }
            }
        }
    }
    
    

    相关文章

      网友评论

          本文标题:捕获异常信息CrashHandler

          本文链接:https://www.haomeiwen.com/subject/muynphtx.html