CrashHandler全局捕捉异常

2021-05-25  本文已影响0人  奔跑的佩恩

前言

Android开发中,捕获异常是件十分重要的事。修复bug都是从捕捉bug信息开始。在上一节中,我们已经讲过了Bugly捕获异常并上报的能力。大家感兴趣的话,可参考以下文章
Bugly捕获异常并上报
Bugly捕获异常的能力确实十分出色,但是我在开发的过程中,竟然出现Bugly不上报异常的问题,当然问题排查已经在 Bugly捕获异常并上报 中做过分析。但是如果还是出现Bugly不上报异常的问题 或者 你想让程序崩溃的时候,能在假面上直观的看到异常信息怎么办?
今天就让我们来学习下Android中本地异常捕获并显示在界面的知识吧。这里我将Android本地捕获异常封装到了一个类——CrashHandler。下面就来做个详细介绍吧。

今天涉及知识:

  1. 捕获异常及界面显示异常逻辑
  2. 配置权限
  3. 捕获异常具体使用介绍
  4. Activity中使用主要示例代码
  5. 效果图及项目结构图
  6. CrashHandler及相关类源码

先来波效果图


效果图.gif

一. 捕获异常及界面显示异常逻辑

捕获异常的逻辑是,继承Thread.UncaughtExceptionHandler写一个自己的CrashHandler,然后在异常发生时,收集bug发生的时间设备信息以及错误信息,然后有必要的话,在发生Crash时,可以将错误上传到服务器,供开发者分析。最后,将这些信息通过一个全局的对话框显示在界面上,让我们可以直观的看到。

二. 配置权限

再收集异常信息时,涉及到设备信息,然后最后要弹出一个全局对话框(这里我对话框采用一个全局的Window显示)需要全局window权限,则在AndroidManifast.xml中需要以下权限:

    <uses-permission android:name="android.permission.READ_PHONE_STATE"/> 
    <!--  全局Popwindow权限  -->
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

若涉及到错误上传服务器,则还需要添加网络权限:

    <uses-permission android:name="android.permission.INTERNET" />

三. 捕获异常具体使用介绍

在你项目自定义ApplicationonCreate()中进行异常捕获类初始化:

public class AppContext extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        //初始化本地crash捕捉
        CrashHandler.getInstance().init(getApplicationContext(), true, new CrashHandler.OnCrashListener() {
            @Override
            public void uploadInfo(String info) {
                LogUtil.i("=======给服务器上传错误信息====info="+info);
            }

            @Override
            public void exitApp() {
                //关闭所有Activity,退出app
                LogUtil.i("=======关闭所有Activity,退出app=======");
            }
        });

    }

}

对于CrashHandler.getInstance().init(Context context,boolean isCatch,OnCrashListener listener)参数说明:

接着我们要手动申请全局Popwindow权限,这部分逻辑我们一般在App的启动界面处理。当涉及到异常捕获功能时,我们先申请全局Popwindow权限,若此权限申请成功,则进行其他用户权限申请(一般是调用第三方权限库),若无其他权限申请,则进入正常的app业务逻辑。若申请失败则做界面提示并退出程序。
在启动页(Activity)申请全局Popwindow权限示例代码如下:

        if (!CrashPopUtil.hasPermission(MainActivity.this)) {
            //跳转设置界面
            CrashPopUtil.goSetPop(555,MainActivity.this);
        }else {
            LogUtil.i("======有悬浮窗权限======");
            //接着申请用户权限
            //......
        }

然后在ActivityonActivityResult方法中做回调处理:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode==555){
            //悬浮窗申请
            if(CrashPopUtil.hasPermission(this)){
                LogUtil.i("======有悬浮窗权限======");
                //接着申请用户权限
                //......
            }else{
                LogUtil.i("======悬浮窗权限授权失败,请退出程序======");
            }
        }
    }

以上555是一个请求code,大家可以根据自己需要自行确认值。

在这些都搞定以后,我们可以在需要用的地方做一个bug捕捉测试。在你想制造Crash的地方调用以下方法:

        //制造一个bug
        CrashUtil.makeCrash();

四. Activity中使用主要示例代码

项目中使用主要涉及到自定义Application及申请全局Popwindow权限的界面TempActiivty。下面贴出自定义ApplicationAppContext代码:

public class AppContext extends ComContext {

    @Override
    public void onCreate() {
        super.onCreate();
        //初始化本地crash捕捉
        CrashHandler.getInstance().init(getApplicationContext(), true, new CrashHandler.OnCrashListener() {
            @Override
            public void uploadInfo(String info) {
                LogUtil.i("=======给服务器上传错误信息====info="+info);
            }

            @Override
            public void exitApp() {
                //关闭所有Activity,退出app
                LogUtil.i("=======关闭所有Activity,退出app=======");
            }
        });


        LogUtil.setDebug(true);

    }

}

接着是界面TempActivity代码:

@RequiresApi(api = Build.VERSION_CODES.N)
public class TempActivity extends AppCompatActivity implements View.OnClickListener{

    private TextView mTvTest;
    private Button mBtnTest;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_temp);

        //初始化控件
        initView();
        //初始化数据
        initData();
        //控件监听
        setListener();
    }

    /**
     * 初始化控件
     **/
    private void initView() {
        mTvTest = findViewById(R.id.mTvTest);
        mBtnTest = findViewById(R.id.mBtnTest);
    }

    private void initData() {
        if (!CrashPopUtil.hasPermission(this)) {
            //跳转设置界面
            CrashPopUtil.goSetPop(555,this);
        }else {
            LogUtil.i("======有悬浮窗权限======");
            //接着申请用户权限
            //......
        }

    }

    /**
     * 控件监听
     **/
    private void setListener() {
        //点击事件
        mBtnTest.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
         switch (v.getId()) {
             case R.id.mBtnTest:
                 LogUtil.i("======我点击了======");

                 test();
                 break;
             default:
                 break;
         }
    }

    private void test(){
        //制造一个bug
        CrashUtil.makeCrash();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode==555){
            //悬浮窗申请
            if(CrashPopUtil.hasPermission(this)){
                LogUtil.i("======有悬浮窗权限======");
                //接着申请用户权限
                //......
            }else{
                LogUtil.i("======悬浮窗权限授权失败,请退出程序======");
            }
        }
    }
}

五. 效果图及项目结构图

效果图.gif 项目结构图.png

六. CrashHandler及相关类源码

此处共涉及到三个类:

CrashUtil代码如下:

上一篇 下一篇

猜你喜欢

热点阅读