首页 > 系统 > Android > 正文

android 捕捉异常并上传至服务器的简单实现

2019-12-12 03:04:14
字体:
来源:转载
供稿:网友

在项目中,我们的应用经常会遇到崩溃的情况,如果你的项目已经发送到了应用市场上,那么应用发生的崩溃开发人员是开不到的,所以我们要想办法将异常信息传到服务器上,便于开发人员查看并作出修改。Google考虑到这一点,也提供了Thread.UncaughtExceptionHandler接口来实现这一问题。

创建Crash异常捕获很简单,主要的步骤有:

1.创建BaseApplication继承Application并实现Thread.UncaughtExceptionHandler

2.通过Thread.setDefaultUncaughtExceptionHandler(this)设置默认的异常捕获

3.最后在manifests中注册创建的BaseApplication

一、异常捕捉的简单实用

public class BaseApplication extends Application implements Thread.UncaughtExceptionHandler {  @Override  public void onCreate() {    super.onCreate();    //设置异常捕获    CrashHandler catchHandler = CrashHandler.getInstance();    catchHandler.init(this);  }}

二、CrashHandler(主要是实现uncaughtException方法)

public class CrashHandler implements UncaughtExceptionHandler {  public static final String TAG = "CrashHandler";  // 系统默认的UncaughtException处理类  private Thread.UncaughtExceptionHandler mDefaultHandler;  // CrashHandler实例  private static CrashHandler instance;  // 程序的Context对象  private Context mContext;  // 用来存储设备信息和异常信息  private Map<String, String> infos = new HashMap<String, String>();  // 用于格式化日期,作为日志文件名的一部分  private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");  MyActivityLifecycleCallbacks mMyActivityLifecycleCallbacks = new MyActivityLifecycleCallbacks();  /** 保证只有一个CrashHandler实例 */  private CrashHandler() {  }  /** 获取CrashHandler实例 ,单例模式 */  public static CrashHandler getInstance() {    if (instance == null)      instance = new CrashHandler();    return instance;  }  /**   * 初始化   */  public void init(SspApplication context) {    mContext = context;    context.registerActivityLifecycleCallbacks(mMyActivityLifecycleCallbacks);    // 获取系统默认的UncaughtException处理器    mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();    // 设置该CrashHandler为程序的默认处理器    Thread.setDefaultUncaughtExceptionHandler(this);  }  /**   * 当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.e(TAG, "error : ", e);    //  }    // 注意Thread.sleep(3000)和 SystemClock.sleep(3000)的区别      SystemClock.sleep(3000);      // 退出程序      Log.i("=====killProcess======","=====killProcess======");      mMyActivityLifecycleCallbacks.removeAllActivities();      android.os.Process.killProcess(android.os.Process.myPid());      System.exit(0);    }  }  /**   * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.   *    * @param ex   * @return true:如果处理了该异常信息;否则返回false.   */  private boolean handleException(Throwable ex) {    Log.i("=====handleException======","=====handleException======");    if (ex == null) {      return false;    }    // 收集设备参数信息    collectDeviceInfo(mContext);    // 使用Toast来显示异常信息    new Thread() {      @Override      public void run() {        Looper.prepare();        Toast.makeText(mContext, "哎呀,出问题了,我要暂时离开了", Toast.LENGTH_SHORT).show();        Looper.loop();      }    }.start();    // 保存日志文件    saveCatchInfo2File(ex);    return true;  }  /**   * 收集设备参数信息   *    * @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("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);      }    }  }  /**   * 保存错误信息到文件中   *    * @param ex   * @return 返回文件名称,便于将文件传送到服务器   */  private String saveCatchInfo2File(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 + "=" + 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);    try {      long timestamp = System.currentTimeMillis();      String time = formatter.format(new Date());      SharedPreferences userInfo = mContext.getSharedPreferences(          Constants.USER_SETTING_INFOS, 0);      String loginName = userInfo.getString(Constants.USERNAME, "");      String fileName = "crash-" + time +"-"+loginName+ ".log";      if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {        String path = "/mnt/sdcard/crash/";        File dir = new File(path);        if (!dir.exists()) {          dir.mkdirs();        }        FileOutputStream fos = new FileOutputStream(path + fileName);        fos.write(sb.toString().getBytes());        // 发送给开发人员        sendCrashLog2PM(path + fileName);        fos.close();      }      return fileName;    } catch (Exception e) {      Log.e(TAG, "an error occured while writing file...", e);    }    return null;  }  /**   * 将捕获的导致崩溃的错误信息发送给开发人员   *    * 目前只将log日志保存在sdcard 和输出到LogCat中,并未发送给后台。   */  private void sendCrashLog2PM(final String fileName) {    if (!new File(fileName).exists()) {      Toast.makeText(mContext, "日志文件不存在!", Toast.LENGTH_SHORT).show();      return;    } else {        new Thread(new Runnable() {        @Override        public void run() {          Looper.prepare();          ArrayList<String> picList = new ArrayList<String>();          picList.add(fileName);          SendEventPic sFile = new SendEventPic(mContext);          UUIDGenerator generator = new UUIDGenerator();          String linkId = generator.generate().toString();          SharedPreferences userInfo = mContext.getSharedPreferences(              Constants.USER_SETTING_INFOS, 0);          String loginName = userInfo.getString(Constants.USERNAME, "");          String userId = userInfo.getString(Constants.USER_USERID_INFOS, "");          boolean isproblempic = sFile.isSendSuccess(picList, linkId,              "crash_tng", "crash_tng", loginName, userId);          Looper.loop();        }      }).start();    }    FileInputStream fis = null;    BufferedReader reader = null;    String s = null;    try {      fis = new FileInputStream(fileName);      reader = new BufferedReader(new InputStreamReader(fis, "GBK"));      while (true) {        s = reader.readLine();        if (s == null)          break;        // 由于目前尚未确定以何种方式发送,所以先打出log日志。        Log.i("info", s.toString());      }    } catch (FileNotFoundException e) {      e.printStackTrace();    } catch (IOException e) {      e.printStackTrace();    } finally { // 关闭流      try {        reader.close();        fis.close();      } catch (IOException e) {        e.printStackTrace();      }    }  }}

三、开发过程中遇到的坑

如果在activity创建的时候崩溃的话,系统有时候(目前不确定什么情况下会重启)会重启当前的activity,造成第二次的崩溃,如此循环……

所以,在应用崩溃时要完全退出应用。

public class MyActivityLifecycleCallbacks implements ActivityLifecycleCallbacks {  private List<Activity> activities = new LinkedList<>();  public static int sAnimationId = 0;  @Override  public void onActivityCreated(Activity activity, Bundle savedInstanceState) {    addActivity(activity);  }  @Override  public void onActivityStarted(Activity activity) {  }  @Override  public void onActivityResumed(Activity activity) {  }  @Override  public void onActivityPaused(Activity activity) {  }  @Override  public void onActivityStopped(Activity activity) {  }  @Override  public void onActivitySaveInstanceState(Activity activity, Bundle outState) {  }  @Override  public void onActivityDestroyed(Activity activity) {    removeActivity(activity);  }  /**   * 添加Activity   */  public void addActivity(Activity activity) {    if (activities == null) {      activities = new LinkedList<>();    }    if (!activities.contains(activity)) {      activities.add(activity);// 把当前Activity添加到集合中    }  }  /**   * 移除Activity   */  public void removeActivity(Activity activity) {    if (activities.contains(activity)) {      activities.remove(activity);    }    if (activities.size() == 0) {      activities = null;    }  }  /**   * 销毁所有activity   */  public void removeAllActivities() {    for (Activity activity : activities) {      if (null != activity) {        activity.finish();        activity.overridePendingTransition(0, sAnimationId);      }    }  }}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表