Android自定义全局异常捕获——Activity形式

开发安卓的小伙伴都遇到过APP突然崩溃,无响应的情况.如果发生在自己手中,那么还可以通过IDE查看错误日志,但是实际都是发生在用户手中,那么这个时候产生崩溃,无响应ANR异常就很麻烦.无从下手.因此,需要全局异常捕获.也就是对未知异常,程序员没有处理的异常进行处理,记录等便于分析查找原因.

效果展示

新建MyUncaughtExceptionHandler.java

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Locale;

/**
 * @ClassName MyUncaughtExceptionHandler
 * @Description 全局捕捉异常
 * @Author summerain0
 * @Date 2020/9/11 15:31
 */
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    // 单例
    private static MyUncaughtExceptionHandler myUncaughtExceptionHandler;
    // 上下文
    private Context context;
    // 会输出到文件中
    private StringBuilder stringBuilder;
    // 系统异常处理器
    private Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler;

    public MyUncaughtExceptionHandler(Context context) {
        this.context = context;
    }

    // 获取单例
    public static synchronized MyUncaughtExceptionHandler getInstance(Context ctx) {
        if (myUncaughtExceptionHandler == null) {
            myUncaughtExceptionHandler = new MyUncaughtExceptionHandler(ctx);
        }
        return myUncaughtExceptionHandler;
    }

    // 初始化
    public void init() {
        defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    @Override
    public void uncaughtException(Thread thread, Throwable throwable) {
        if (throwable == null) {
            defaultUncaughtExceptionHandler.uncaughtException(thread, throwable);
        }

        // 创建集合对象
        stringBuilder = new StringBuilder();

        // 记录时间
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss.SSS", Locale.getDefault());
        String date = simpleDateFormat.format(new Date());
        addMessage("崩溃时间", date);

        // 记录应用版本信息
        try {
            PackageManager pm = context.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
            addMessage("版本名", pi.versionName);
            addMessage("版本号", pi.versionCode);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            addMessage("error", "记录版本信息失败!" + e.getMessage());
        }

        // 记录设备信息
        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                Object obj = field.get(null);
                if (obj != null) {
                    addMessage(field.getName(), obj);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                addMessage("error", "记录设备信息失败!" + e.getMessage());
            }
        }

        // 添加分隔符
        addMessage(null, "==============================================================");
        addMessage(null, "========================   崩溃日志   =========================");
        addMessage(null, "==============================================================");

        // 记录崩溃信息
        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        throwable.printStackTrace(printWriter);
        Throwable cause = throwable.getCause();
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();
        addMessage(null, writer.toString());

        // 生成路径,保存至/Android/data/包名,无需读写权限
        try {
            File root = context.getExternalFilesDir("log");
            String filename = date + ".log";
            File file = new File(root, filename);
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(stringBuilder.toString().getBytes());
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
            defaultUncaughtExceptionHandler.uncaughtException(thread, throwable);
        }

        // 启动崩溃异常页面
        Intent intent = new Intent(context, UncaughtExceptionActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// 请勿修改,否则无法打开页面
        intent.putExtra("error", stringBuilder.toString());
        context.startActivity(intent);
        System.exit(1);// 请勿修改,否则无法打开页面

    }

    // 添加数据
    private void addMessage(String key, Object obj) {
        // 对数组做一下处理
        if (obj instanceof String[]) {
            String[] list = (String[]) obj;
            ArrayList<String> array = new ArrayList<>(Arrays.asList(list));
            stringBuilder.append(key).append("=").append(array.toString()).append("\n");
        }
        // 其他的都直接添加
        if (key == null) {
            stringBuilder.append(obj)
                    .append("\n");
        } else {
            stringBuilder.append(key)
                    .append("=")
                    .append(obj)
                    .append("\n");
        }
    }
}

新建UncaughtExceptionActivity.java

import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

/**
 * @ClassName UncaughtExceptionActivity
 * @Description 异常页面
 * @Author summerain0
 * @Date 2020/9/12 11:02
 */
public class UncaughtExceptionActivity extends AppCompatActivity {
    public static final String TAG = "UncaughtExceptionActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 读取日志,显示在屏幕上
        String msg = getIntent().getStringExtra("error");

        ScrollView scrollView = new ScrollView(this);// 防止日志太长看不完
        scrollView.setLayoutParams(new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT, ScrollView.LayoutParams.MATCH_PARENT));
        TextView textView = new TextView(this);
        textView.setText(msg);
        scrollView.addView(textView);

        setContentView(scrollView);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(0, 0, 0, "重启").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
        menu.add(0, 1, 0, "上传").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case 0:
                Intent intent = new Intent(this, MainActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// 请勿修改,否则无法打开页面
                startActivity(intent);
                System.exit(1);// 请勿修改,否则无法打开页面

            case 1:
                // 这里自己写上传逻辑
                Toast.makeText(this, "已上传!", Toast.LENGTH_SHORT).show();
        }
        return true;
    }
}

新建MyApplication.java

import android.app.Application;

/**
 * @ClassName MyApplication
 * @Description TODO
 * @Author summerain0
 * @Date 2020/9/11 14:00
 */
public class MyApplication extends Application {
    public static final String TAG = "MyApplication";

    @Override
    public void onCreate() {
        super.onCreate();
        // 初始化异常处理器
        MyUncaughtExceptionHandler.getInstance(MyApplication.this).init();
    }
}

修改配置文件

<application
    android:name=".MyApplication"
    ......>

    <activity
        android:label="崩溃异常"
        android:name=".UncaughtExceptionActivity" />

</application>

 

© 版权声明
THE END
喜欢就支持以下吧
点赞0赞赏
分享
评论 抢沙发

请登录后发表评论