CrashHandler全局捕捉异常
前言
在Android
开发中,捕获异常
是件十分重要的事。修复bug
都是从捕捉bug
信息开始。在上一节中,我们已经讲过了Bugly
捕获异常并上报的能力。大家感兴趣的话,可参考以下文章
Bugly捕获异常并上报
Bugly
捕获异常的能力确实十分出色,但是我在开发的过程中,竟然出现Bugly
不上报异常的问题,当然问题排查已经在 Bugly捕获异常并上报 中做过分析。但是如果还是出现Bugly
不上报异常的问题 或者 你想让程序崩溃的时候,能在假面上直观的看到异常信息怎么办?
今天就让我们来学习下Android
中本地异常捕获并显示在界面的知识吧。这里我将Android
本地捕获异常封装到了一个类——CrashHandler
。下面就来做个详细介绍吧。
今天涉及知识:
- 捕获异常及界面显示异常逻辑
- 配置权限
- 捕获异常具体使用介绍
- Activity中使用主要示例代码
- 效果图及项目结构图
- 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" />
三. 捕获异常具体使用介绍
在你项目自定义Application
的onCreate()
中进行异常捕获类初始化:
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)
参数说明:
- context: Application上下文实例
- isCatch:是否捕获异常。设置
true
表示使用异常捕获功能,false
表示不使用。默认情况为false
,即不使用。 - listener:异常发生时的处理监听。里面有两个方法
uploadInfo(String info)
和exitApp()
。当我们想在异常发生时给服务器上传错误信息或本地写文件的话,可以在uploadInfo(String info)
中执行你想处理的逻辑。exitApp()
是错误弹窗时的点击事件,一般我们在exitApp()
中的操作是关闭所有Activity
并退出程序。
接着我们要手动申请全局Popwindow权限
,这部分逻辑我们一般在App
的启动界面处理。当涉及到异常捕获功能时,我们先申请全局Popwindow权限
,若此权限申请成功,则进行其他用户权限申请(一般是调用第三方权限库),若无其他权限申请,则进入正常的app业务逻辑。若申请失败则做界面提示并退出程序。
在启动页(Activity
)申请全局Popwindow权限
示例代码如下:
if (!CrashPopUtil.hasPermission(MainActivity.this)) {
//跳转设置界面
CrashPopUtil.goSetPop(555,MainActivity.this);
}else {
LogUtil.i("======有悬浮窗权限======");
//接着申请用户权限
//......
}
然后在Activity
的onActivityResult
方法中做回调处理:
@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
。下面贴出自定义Application
类AppContext
代码:
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:用于获取设备信息及一些基本方法
- CrashPopUtil:处理弹窗问题
- CrashHandler:处理异常捕获问题
CrashUtil
代码如下: