前言
相信大家都遇到过测试人员测试的时候一些偶发性的bug导致程序崩溃,实在是难以复现,所以只能通过一遍遍的检查代码然后猜测可能出现问题的代码,非常难受,因为难以复现所以很多时候改完也难以验证,接下来就为大家介绍一个“轮子”,可以帮助我们在程序崩溃的时候跳转到指定页面,并且输出日志,可复制到粘贴板,然后保留下来,有了崩溃日志,问题出在哪儿就好分析啦。
首先肯定是先感谢大神的分享
https://github.com/Ereza/CustomActivityOnCrash
好了接下来就是实现的方法,第一步是导入处理崩溃的依赖
implementation 'cat.ereza:customactivityoncrash:2.3.0'//崩溃处理依赖
然后是在自己的Application中的onCreate()中添加以下代码
@Override
public void onCreate() {
super.onCreate();
CaocConfig.Builder.create()
.backgroundMode(CaocConfig.BACKGROUND_MODE_SILENT) //default: CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM
.enabled(false) //是否启用 default: true
.showErrorDetails(false) //default: true
.showRestartButton(false) //default: true
.logErrorOnRestart(false) //default: true
.trackActivities(true) //default: false
.minTimeBetweenCrashesMs(2000) //default: 3000
.errorDrawable(R.drawable.ic_custom_drawable) //default: bug image
.restartActivity(YourCustomActivity.class) //默认程序崩溃时重启的的activity default: null (your app's launch activity)
.errorActivity(YourCustomErrorActivity.class) //默认程序崩溃时跳转的activity default: null (default error activity)
.eventListener(new YourCustomEventListener()) //default: null
.apply();
}
然后是实现CustomEventListener
class CustomEventListener implements CustomActivityOnCrash.EventListener {
@Override
public void onLaunchErrorActivity() {
Log.i(TAG, "onLaunchErrorActivity()");
}
@Override
public void onRestartAppFromErrorActivity() {
Log.i(TAG, "onRestartAppFromErrorActivity()");
}
@Override
public void onCloseAppFromErrorActivity() {
Log.i(TAG, "onCloseAppFromErrorActivity()");
}
}
重启页面就不多介绍了
.restartActivity(YourCustomActivity.class) //默认程序崩溃时重启的的activity default: null (your app's launch activity)
主要介绍一下,崩溃时跳转的页面,然后这个页面UI和流程设计的的好的话,其实还是比较好的,最起码比程序崩溃了闪退就好多了
.errorActivity(YourCustomErrorActivity.class) //默认程序崩溃时跳转的activity default: null (default error activity)
这是我的,因为我只是用作测试阶段的时候供测试人员提供崩溃日志,所以就随便写了;
首先是布局文件
其中的文字,颜色和图片资源就自己替换一下哈;
加下来就是具体的activity,自定义的内容比较多,懒得一一复赘了,关键代码拷贝下来就可以哈;
public final class DefaultErrorActivity extends baseActivity{ @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); viewBinding.VStatusBar.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(this))); QMUIStatusBarHelper.translucent(this); QMUIStatusBarHelper.setStatusBarDarkMode(this); //This is needed to avoid a crash if the developer has not specified //an app-level theme that extends Theme.AppCompat TypedArray a = obtainStyledAttributes(R.styleable.AppCompatTheme); if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) { setTheme(R.style.Theme_AppCompat_Light_DarkActionBar); } a.recycle(); //Close/restart button logic: //If a class if set, use restart. //Else, use close and just finish the app. //It is recommended that you follow this logic if implementing a custom error activity. Button restartButton = viewBinding.restartButton; final CaocConfig config = CustomActivityOnCrash.getConfigFromIntent(getIntent()); if (config == null) { //This should never happen - Just finish the activity to avoid a recursive crash. finish(); return; } if (config.isShowRestartButton() && config.getRestartActivityClass() != null) { restartButton.setText(R.string.restart_app); restartButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { CustomActivityOnCrash.restartApplication(DefaultErrorActivity.this, config); } }); } else { restartButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { CustomActivityOnCrash.closeApplication(DefaultErrorActivity.this, config); } }); } Button moreInfoButton = viewBinding.moreInfoButton; if (config.isShowErrorDetails()) { moreInfoButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //We retrieve all the error data and show it alertDialog dialog = new alertDialog.Builder(DefaultErrorActivity.this) .setTitle(R.string.error_details) .setMessage(CustomActivityOnCrash.getAllErrorDetailsFromIntent(DefaultErrorActivity.this, getIntent())) .setPositiveButton(R.string.customactivityoncrash_error_activity_error_details_close, null) .setNeutralButton(R.string.customactivityoncrash_error_activity_error_details_copy, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { copyErrorToClipboard(); } }) .show(); TextView textView = dialog.findViewById(android.R.id.message); if (textView != null) { textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(R.dimen.error_details_text_size)); } } }); } else { moreInfoButton.setVisibility(View.GONE); } Integer defaultErrorActivityDrawableId = config.getErrorDrawable(); ImageView errorImageView = findViewById(R.id.customactivityoncrash_error_activity_image); if (defaultErrorActivityDrawableId != null) { errorImageView.setImageDrawable(ResourcesCompat.getDrawable(getResources(), defaultErrorActivityDrawableId, getTheme())); } } private void copyErrorToClipboard() { String errorInformation = CustomActivityOnCrash.getAllErrorDetailsFromIntent(DefaultErrorActivity.this, getIntent()); ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); //Are there any devices without clipboard...? if (clipboard != null) { ClipData clip = ClipData.newPlainText(getString(R.string.customactivityoncrash_error_activity_error_details_clipboard_label), errorInformation); clipboard.setPrimaryClip(clip); Toast.makeText(DefaultErrorActivity.this, R.string.customactivityoncrash_error_activity_error_details_copied, Toast.LENGTH_SHORT).show(); } } }
最后,别忘了在AndroidManifest.xml中添加自己的Application文件名
至此就可以了,程序遇到崩溃时就会自动跳转到自己定义的界面,和输出日志啦;
再次感谢分享,也可以查看原分享者的详细介绍
https://github.com/Ereza/CustomActivityOnCrash



