summerain0的个人博客

  • 首页
  • 意见反馈
  • 友情链接
  • 申请友联
每个Bug都有解决之法
在修了在修了_(:з」∠)_
  1. 首页
  2. Android
  3. 正文

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

2020年9月15日 434点热度 2人点赞 1条评论
浏览量: 306
内容 隐藏目录
1) 前言
1.1) 前言
2) 效果展示
2.1) 效果展示
3) 源码
3.1) 源码
3.2) 新建MyUncaughtExceptionHandler.java
3.3) 新建UncaughtExceptionActivity.java
3.4) 新建MyApplication.java
3.5) 修改配置文件

前言

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

效果展示

源码

新建MyUncaughtExceptionHandler.java

MyUncaughtExceptionHandler.java
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
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

UncaughtExceptionActivity.java
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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

MyApplication.java
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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();
    }
}
 

修改配置文件

AndroidManifest.xml
XHTML
1
2
3
4
5
6
7
8
9
<application
    android:name=".MyApplication"
    ......>
 
    <activity
        android:label="崩溃异常"
        android:name=".UncaughtExceptionActivity" />
 
</application>
 
微海报分享
标签: Android 教程
最后更新:2020年10月17日

summerain0

这个人很懒,什么都没留下

打赏 点赞
< 上一篇
下一篇 >

文章评论

  • 匿名

    :mrgreen:

    2021年3月11日
    回复
  • 取消回复

    summerain0

    这个人很懒,什么都没留下

    最新 热点 随机
    最新 热点 随机
    还在用老办法获取版本号或者签名信息吗?不如试试PackageInfoCompat吧! Android常用工具类合集 什么?你是学生?还不快来白嫖JetBrains全家桶? Android Studio的图标库出现Nothing to show问题 GitHub在线下载加速网址集合 Android Material Design全面解析(一)- MaterialButton篇
    还在用老办法获取版本号或者签名信息吗?不如试试PackageInfoCompat吧!
    还在用老办法获取版本号或者签名信息吗?不如试试PackageInfoCompat吧! 什么?你是学生?还不快来白嫖JetBrains全家桶? Android Material Design全面解析(一)- MaterialButton篇 Android Studio的图标库出现Nothing to show问题 GitHub在线下载加速网址集合 Androidx映射文件
    标签聚合
    Material Design 工具 教程 Android
    实用工具
    • 免费API工具
    友情链接
    • AIDE教程网
    • 极坛
    • Ptcraft
    • 沐川的博客

    COPYRIGHT © 2021 summerain0的个人博客. ALL RIGHTS RESERVED.

    THEME KRATOS MADE BY VTROIS

    闽ICP备19012197号