Android 异常日志捕捉Crash

开始的时候,一旦打包,就无法进入Debug调试,这个时候如果程序崩溃,只能通过自定义Crash来捕捉。

关键类:java.lang.Thread.UncaughtExceptionHandler

类的说明:Implemented by objects that want to handle cases where a thread is being terminated by an uncaught exception. Upon such termination, the handler is notified of the terminating thread and causal exception. If there is no explicit handler set then the thread's group is the default handler.

 大致的意思就是说,实现这个接口可以来捕获程序的异常终止,然后xxxxxxxxxxx
 

OK,下面来实现这个类,并重写它的uncaughtException 方法,然后我们看下重写的这个方法

      @Override
      public void uncaughtException(Thread thread, Throwable ex) {


      }

上面,当捕捉到异常的时候,会进入这个方法, 然后,我们就以处理这个 Throwable异常对象。

第一步:1、可以先收集设备的信息(因有些BUG可能会在一些特殊的机型上出现)  我们将收集到的信息保存在一个Map当中
          // 用来存储设备信息和异常信息
          private Map<String, String> infos = new HashMap<String, String>();
           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);
                  }
            }
      }

第二步:将异常的日志保存到本地。其中FileUtils. getErrorLog();方法是得到要写入的路径。

       private void saveCrashInfo2File(Throwable ex) {
            String path = FileUtils. getErrorLog();
            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);
            sb.append( "\n");
             final File file = new File(path);
             try {
                   if (!file.exists()) {
                        file.createNewFile();
                  }
                    //注意这里,如果是要在文本末尾追加,则为true,因为我的是在测试环境,所以我这里写成了false,就是每次写入的时候,会先清空本地保存的日志
                  FileOutputStream fos = new FileOutputStream(path, false);
                  fos.write(sb.toString().getBytes());
                  fos.close();
            } catch (Exception e) {
            }
      }

第三步:回到我们刚才的uncaughtException(Thread thread, Throwable ex)方法里面。 在这里面进行了上面的两步操作之后,然后下面,我们就要重启APP。
     注意这里,很多网友会说这个方法会执行三次!三次!,那是因为没有完全清空APP的栈,没有完全杀死已经异常了的程序。

                  new Thread() {
                   @Override
                   public void run() {
                         // Toast 显示需要出现在一个线程的消息队列中
                        Looper. prepare();
                        Toast. makeText(mContext, "很抱歉,程序异常终止!" , Toast.LENGTH_LONG).show();
                        AppManager. getAppManager().finishAllActivity();
                        Intent intent = new Intent( mContext, WelcomeActivity.class);
                        intent.addFlags(Intent. FLAG_ACTIVITY_NEW_TASK);
                         mContext.startActivity(intent);
                        Looper. loop();
                  }
            }.start();

在这里,我通过 AppManager. getAppManager().finishAllActivity(); 来清空栈,然后跳转到欢迎界面(APP入口界面)。这样就只会执行一次了!
然后在Application或者APP入口界面的Activity来将错误日志上传到服务器。

上面提到的 AppManager. getAppManager().finishAllActivity();是个什么东西呢?
这个是自定义的一个管理所有的Activity的类,所有的Activity在 setContentView 之前,添加到 activityStack中
AppManager. getAppManager().addActivity(  activity );

public class AppManager {

      private static Stack<Activity> activityStack;
      private static AppManager instance;

      private AppManager() {
      }

      /**
       * 单一实例
       */
      public static AppManager getAppManager() {
             if ( instance == null) {
                   instance = new AppManager();
            }
             return instance;
      }

      /**
       * 添加Activity到堆栈
       */
      public void addActivity(Activity activity) {
             if ( activityStack == null) {
                   activityStack = new Stack<Activity>();
            }
             activityStack.add(activity);
      }

      /**
       * 获取当前Activity(堆栈中最后一个压入的)
       */
      public Activity currentActivity() {
            Activity activity = activityStack.lastElement();
             return activity;
      }

      /**
       * 结束当前Activity(堆栈中最后一个压入的)
       */
      public void finishActivity() {
            Activity activity = activityStack.lastElement();
             if (activity != null) {
                  finishActivity(activity);
            }

      }

      /**
       * 结束指定的Activity
       */
      public void finishActivity(Activity activity) {
             if (activity != null) {
                   activityStack.remove(activity);
                  activity.finish();
                  activity = null;
            }
      }

      /**
       * 结束指定类名的Activity
       */
      public void finishActivity(Class<?> cls) {
             for (Activity activity : activityStack) {
                   if (activity.getClass().equals(cls)) {
                        finishActivity(activity);
                  }
            }
      }

      /**
       * 结束所有Activity
       */
      public void finishAllActivity() {
             for ( int i = 0, size = activityStack.size(); i < size; i++) {
                   if ( null != activityStack.get(i)) {
                         activityStack.get(i).finish();
                  }
            }
             activityStack.clear();
      }

      /**
       * 退出应用程序
       */
      public void AppExit(Context context) {
             try {
                  finishAllActivity();
                  ActivityManager activityMgr = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE );
                  activityMgr.killBackgroundProcesses(context.getPackageName());
                  System. exit(0);
            } catch (Exception e) {
            }
      }
}

OK,经过上述步骤之后, 然后还需要在Application中进行初始化
      CrashHandler. getInstance().init(getApplicationContext());
      

项目的下载地址:https://github.com/townkoim/CrashDemo



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值