CustomActivityOnCrash Android program crash handling

Foreword I
believe that everyone has encountered some occasional bugs that caused the program to crash during testing. It is really difficult to reproduce, so we can only check the code over and over again and guess the code that may have problems. Now, it is often difficult to verify after modification. Next, I will introduce a "wheel" for you, which can help us jump to the specified page when the program crashes, and output the log, which can be copied to the clipboard and then saved. After reading the crash log, it is easy to analyze where the problem is.

First of all, I must thank God for sharing

https://github.com/Ereza/CustomActivityOnCrash

Well, the next step is to implement the method. The first step is to import the dependency that handles the crash

implementation 'cat.ereza:customactivityoncrash:2.3.0'//崩溃处理依赖

Then add the following code in onCreate() in your Application

@Override
public void onCreate() {
    
    
    super.onCreate();
/**
//此方法定义当应用程序在后台崩溃时是否应启动错误活动。共有三种模式:
//CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM:即使应用程序在后台运行,也会启动错误活动。
//CaocConfig.BACKGROUND_MODE_CRASH:当应用程序在后台运行时,启动默认系统错误。
//CaocConfig.BACKGROUND_MODE_SILENT:当应用程序在后台运行时,它会以静默方式崩溃。
//默认值为CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM。
*/
    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();
}

Then implement 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()");
        }
    }

I won’t introduce much about restarting the page

.restartActivity(YourCustomActivity.class) //默认程序崩溃时重启的的activity default: null (your app's launch activity)

Let me mainly introduce the page that jumps when it crashes, and if the UI and process design of this page are good, it is actually better, at least it is much better than the program crashing and flashing back.

.errorActivity(YourCustomErrorActivity.class) //默认程序崩溃时跳转的activity default: null (default error activity)

This is mine, because I just used it for testers to provide crash logs during the testing phase, so I just wrote it casually; the
first is the layout file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:ignore="MissingDefaultResource">

    <View
        android:id="@+id/V_statusBar"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="@color/colorPrimary" />


    <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:text="出错啦"
        android:textColor="#FFFFFF"
        android:textSize="17sp"
        android:textStyle="bold" />


    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#FFFFFF">

        <ImageView
            android:id="@+id/IMG_null"
            android:layout_width="248dp"
            android:layout_height="248dp"
            android:layout_centerInParent="true"
            android:layout_marginTop="100dp"
            android:src="@drawable/ic_null_page" />

        <TextView
            android:id="@+id/data_null_tip"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/IMG_null"
            android:layout_centerHorizontal="true"
            android:gravity="center"
            android:textSize="16dp"
            android:text="@string/error_occurred"
            android:textColor="#FFD0DDF8" />
        <Button
            android:id="@+id/restart_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_margin="30dp"
            android:layout_centerHorizontal="true"
            android:text="@string/close_app" />
        <Button
            android:id="@+id/more_info_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentLeft="true"
            android:layout_margin="30dp"
            android:layout_centerHorizontal="true"
            android:text="@string/error_details" />

    </RelativeLayout>
</LinearLayout>

Please add a picture description
The text, color and picture resources can be replaced by yourself;
add them to the specific activity, and there are many customized contents, so I am too lazy to repeat them one by one, just copy the key codes;

public final class DefaultErrorActivity extends BaseActivity<ActivityErrorBinding> {
    
    


    @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();
        }
    }
}

That’s it, when the program encounters a crash, it will automatically jump to the interface defined by itself and output the log;

Thanks again for sharing, you can also check the detailed introduction of the original sharer

https://github.com/Ereza/CustomActivityOnCrash

Guess you like

Origin blog.csdn.net/gqg_guan/article/details/132106134