异常处理需求

Android app 出现 crash 时,会出现 “程序异常退出” 的提示并关闭,体验不好,另外主要是无法知道哪里出现的崩溃,需要知道哪里造成的异常,就需要一个全局抓取异常的处理方式,可以把异常保存到手机或者上传到指定的服务器上,这样有利于 bug 的解决。通过微信订阅号的文章发现了一个全局处理该方式的接口UncaughtExceptionHandler

接口UncaughtExceptionHandler

  • 类位于:java.lang.Thread.UncaughtExceptionHandler

  • 接口源码:

      	public interface UncaughtExceptionHandler {
    /**
    * Method invoked when the given thread terminates due to the
    * given uncaught exception.
    * <p>Any exception thrown by this method will be ignored by the
    * Java Virtual Machine.
    * @param t the thread
    * @param e the exception
    */
    void uncaughtException(Thread t, Throwable e);
    }
  • 主要是通过实现接口的 uncaughtException(Thread t, Throwable e) 方法,实现 crash 的捕获和保存到 sd 卡,然后联网的情况下再上传到服务器。

具体实现

  1. 创建一个类,实现接口UncaughtExceptionHandler

     		public class CrashHandler implements UncaughtExceptionHandler
  2. 实现接口的方法uncaughtException(Thread t, Throwable e),异常的处理就在这里,比如保存异常信息到 SD 卡,自定义错误弹出信息,自动退出。

     		@Override
    public void uncaughtException(Thread t, Throwable e) {
    //收集错误信息,保存到 sd 卡上
    errorInfo2SD();
    //弹出自定义的错误提醒
    new Thread(new Runnable() {
    @Override
    public void run() {
    Looper.prepare();
    Toast.makeText(mContext, "UnCrashException", Toast.LENGTH_SHORT).show();
    Looper.loop();
    }
    });
    //杀掉进程,退出应用
    Process.killProcess(Process.myPid());
    System.exit(1);
    }
  3. CrashHandler 采用单例模式。

     	public class CrashHandler implements UncaughtExceptionHandler {
    
     		private static CrashHandler mInstance;
    
     		private CrashHandler() {
    
     		}
    
     		// 单例模式-懒汉
    public static CrashHandler getInstance() {
    if (mInstance == null) {
    synchronized (CrashHandler.class) {
    if (mInstance == null) {
    mInstance = new CrashHandler();
    }
    }
    }
    return mInstance;
    } }
  4. 定义一个方法,用于把当前应用注册到系统的异常处理中,让系统知道由自定义的异常捕获器处理。

     public void register(Context context) {
    mContext = context;
    Thread.setDefaultUncaughtExceptionHandler(this);
    }
  5. 在 Application 中注册。

     public class CrashHandlerApplication extends Application {
    
     	@Override
    public void onCreate() {
    super.onCreate();
    CrashHandler.getInstance().register(getApplicationContext());
    }
    }
  6. 收集错误信息并保存到 SD 卡上。

     //用于存储设备信息
    private Map<String, String> mInfo = new HashMap<>();
    //格式化时间,作为Log文件名
    private java.text.DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss"); private void errorInfo2SD(Throwable e) {
    PackageManager pm = mContext.getPackageManager();
    try {
    PackageInfo info = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
    // 获取版本信息
    if (info != null) {
    String versionName = TextUtils.isEmpty(info.versionName) ? "未设置版本名称" : info.versionName;
    String versionCode = info.versionCode + "";
    mInfo.put("versionName", versionName);
    mInfo.put("versionCode", versionCode);
    }
    // 获取设备信息
    Field[] fields = Build.class.getFields();
    if (fields != null && fields.length > 0) {
    for (Field field : fields) {
    field.setAccessible(true);
    mInfo.put(field.getName(), field.get(null).toString());
    }
    }
    // 存储信息到 sd 卡指定目录
    saveErrorInfo(e);
    } catch (NameNotFoundException e) {
    e.printStackTrace();
    } catch (IllegalAccessException e) {
    e.printStackTrace();
    }
    } private void saveErrorInfo(Throwable e) {
    StringBuffer stringBuffer = new StringBuffer();
    for (Map.Entry<String, String> entry : mInfo.entrySet()) {
    String keyName = entry.getKey();
    String value = entry.getValue();
    stringBuffer.append(keyName + "=" + value + "\n");
    }
    stringBuffer.append("\n-----Crash Log Begin-----\n");
    StringWriter stringWriter = new StringWriter();
    PrintWriter writer = new PrintWriter(stringWriter);
    e.printStackTrace(writer);
    Throwable cause = e.getCause();
    while (cause != null) {
    cause.printStackTrace(writer);
    cause = e.getCause();
    }
    writer.close();
    String string = stringWriter.toString();
    stringBuffer.append(string);
    stringBuffer.append("\n-----Crash Log End-----");
    String format = dateFormat.format(new Date());
    String fileName = "crash-" + format + ".log"; if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
    String path = mContext.getFilesDir() + File.separator + "crash";
    File dir = new File(path);
    if (!dir.exists()) {
    dir.mkdirs();
    }
    FileOutputStream fou = null;
    try {
    fou = new FileOutputStream(new File(path, fileName));
    fou.write(stringBuffer.toString().getBytes());
    fou.flush();
    } catch (FileNotFoundException e1) {
    e1.printStackTrace();
    } catch (IOException e1) {
    e1.printStackTrace();
    } finally {
    try {
    if (fou != null) {
    fou.close();
    }
    } catch (IOException e1) {
    e1.printStackTrace();
    }
    }
    }
    }
  7. 以上完成了异常的捕获并保存到 sd 卡上,等待 app 再次启动的时候,上传异常信息到服务器上。

  8. 保存到 sd 卡上的位置

  9. SD 卡中拿到的 Log 信息

     SUPPORTED_64_BIT_ABIS=[Ljava.lang.String;@58ff504
    versionCode=1
    BOARD=msm8939
    BOOTLOADER=3.19.0.0000
    TYPE=user
    ID=MMB29M
    TIME=1461124295000
    BRAND=htc
    SERIAL=HC4AZYC00984
    HARDWARE=qcom
    SUPPORTED_ABIS=[Ljava.lang.String;@abaed
    CPU_ABI=arm64-v8a
    RADIO=unknown
    IS_DEBUGGABLE=false
    MANUFACTURER=HTC
    SUPPORTED_32_BIT_ABIS=[Ljava.lang.String;@c11fd17
    TAGS=release-keys
    CPU_ABI2=
    UNKNOWN=unknown
    USER=buildteam
    FINGERPRINT=htc/htccn_chs_2/htc_a51dtul:6.0.1/MMB29M/738098.4:user/release-keys
    HOST=ABM110
    PRODUCT=htccn_chs_2
    versionName=1.0
    DISPLAY=MMB29M release-keys
    MODEL=HTC D820u
    DEVICE=htc_a51dtul -----Crash Log Begin-----
    java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.CharSequence android.widget.TextView.getText()' on a null object reference
    at cc.lijingbo.crashhandler.MainActivity$1.onClick(MainActivity.java:26)
    at android.view.View.performClick(View.java:5232)
    at android.view.View$PerformClick.run(View.java:21289)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:168)
    at android.app.ActivityThread.main(ActivityThread.java:5885)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687) -----Crash Log End-----
  10. 通过以上 log , 可以看到因为 NullPointerException 造成的退出。原因是 MainActivity.java 的第 26 行造成的。

  11. 工程的 MainActivity 类的源码,可以看到获取 tv2 的时候因为没有实例化,造成的空指针

    public class MainActivity extends AppCompatActivity {
    
        private TextView tv1;
    private TextView tv2; @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    tv1 = (TextView) findViewById(R.id.tv1); tv1.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
    Toast.makeText(MainActivity.this, tv2.getText().toString(), Toast.LENGTH_SHORT).show();
    }
    }); } }
  12. 源码

Android app 全局异常统一处理的更多相关文章

  1. android中全局异常捕捉

    android中全局异常捕捉 只要写代码就会有bug,但是我们要想办法收集到客户的bug.有第三方bugly或者友盟等可以收集.但是,android原生就提供了有关收集异常的api,所以我们来学习一下 ...

  2. Android捕获全局异常

    Android捕获全局异常 程序避免不了出现bug,导致程序崩溃,为了尽量不影响用户体验,可以全局捕获异常 效果图 异常捕获处理前 异常捕获处理后(将程序重新启动) 捕获异常的工具类 package ...

  3. SpringMVC全局异常统一处理

    SpringMVC全局异常统一处理以及处理顺序最近在使用SpringMVC做全局异常统一处理的时候遇到的问题,就是想把ajax请求和普通的网页请求分开返回json错误信息或者跳转到错误页. 在实际做的 ...

  4. Springboot项目全局异常统一处理

    转自https://blog.csdn.net/hao_kkkkk/article/details/80538955 最近在做项目时需要对异常进行全局统一处理,主要是一些分类入库以及记录日志等,因为项 ...

  5. APP全局异常捕获,并保存本地文件

    public class CrashHandler implements Thread.UncaughtExceptionHandler { public static final String TA ...

  6. Application中捕获APP中的全局异常

    package com.example.administrator.mystudent; import android.app.Application; import android.util.Log ...

  7. 【转】 Android自定义捕获Application全局异常

    大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,开发者个人不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了 ...

  8. Android全局异常捕捉

    // 定义自定义捕捉 package com.xiaosw.test; import java.io.File; import java.io.FileOutputStream; import jav ...

  9. Android应用捕获全局异常自定义处理

    [2016-06-30]最新的全局异常处理DRCrashHandler已经集成在DR_support_lib库中 具体请看: https://coding.net/u/wrcold520/p/DR_s ...

随机推荐

  1. eclipse mavenWeb项目真正实现热部署(修改java代码和页面文件不用重启tomcat)

            1.前言 首先,本文创作灵感源于博客园园作者signheart,特此鸣谢!原文链接见文末推荐: 百度都搜破了,全网讲的都是如何将maven项目部署到tomcat上,对于热部署的认知,真 ...

  2. php与redis使用经验分享 (转载)

    一.安装 1.redis的下载及安装: 引用 mkdir /usr/local/redis cd /usr/local/redis wget http://redis.googlecode.com/f ...

  3. grep命令-v参数过滤以井号、分号开头的注释信息行及空白行

    grep命令-v参数(反向选择)分别去掉所有以#(井号)和;(分号)开头的注释信息行,对于剩余的空白行可以再用^$来表示并反选过滤 [root@rhel7 samba]# cat smb.conf | ...

  4. Linux中的共享链接库shared libraries

    可执行文件的静态链接和动态链接静态链接会将需要的库函数在编译时一并包含, 所以体积会比较大. 使用ldd命令查看可执行文件链接的库 $ ldd /sbin/ldconfig not a dynamic ...

  5. 【TP3.2+Oracle】数据进行分页

    1.写在前面:mysql的分页 通过limit 关键字进行处理, oracle却没有limit,而是用ROWNUM 字段来进行分页 2.参考示例,TP3.2 代码,其实原理看懂了 其他框架和原生都可以 ...

  6. 软件申请获取root权限

      申请root的工具类 //获取root权限 RootManager manager=new RootManager(); manager.upgradeRootPermission(getPack ...

  7. 屏蔽alert弹框下面一层的操作

    需求: 给alert框戴个套. 屏蔽下层页面的操作. 搞这个花里胡哨的东西. 还一baidu全都是长得一样的答案. 神魔恋. /** * Tip Message像alert一样 */ function ...

  8. ubuntu安装wkhtmltopdf

    下载安装wkhtmltox系统环境 http://wkhtmltopdf.org/downloads.html wget https://bitbucket.org/wkhtmltopdf/wkhtm ...

  9. 【Oracle】使用dbms_job包创建Oracle定时任务

           在Oracle的包里面,有一个名字叫做DBMS_JOB的包,它的作用是安排和管理作业队列.通过作业队列,可以让Oracle数据库定期执行特定的任务.当使用DBMS_JOB管理作业的时候, ...

  10. struts2 jsp ueditor 上传图片失败,获取不到值,解决方法

    struts2 ueditor 上传图片获取不到值 有点奇怪的是:涂鸦,网络的图片都可以.就是本地上传不行.(应该是struts2过滤了部分本地上传的信息,导致失败) 在进入到imageUp.jsp中 ...