Android app 全局异常统一处理
异常处理需求
Android app 出现 crash 时,会出现 “程序异常退出” 的提示并关闭,体验不好,另外主要是无法知道哪里出现的崩溃,需要知道哪里造成的异常,就需要一个全局抓取异常的处理方式,可以把异常保存到手机或者上传到指定的服务器上,这样有利于 bug 的解决。通过微信订阅号的文章发现了一个全局处理该方式的接口UncaughtExceptionHandler。
接口UncaughtExceptionHandler
类位于:java.lang.Thread.UncaughtExceptionHandler
接口源码:
public interface UncaughtExceptionHandler {
/**
* Method invoked when the given thread terminates due to the
* given uncaught exception.
* <p>Any exception thrown by this method will be ignored by the
* Java Virtual Machine.
* @param t the thread
* @param e the exception
*/
void uncaughtException(Thread t, Throwable e);
}
主要是通过实现接口的
uncaughtException(Thread t, Throwable e)方法,实现 crash 的捕获和保存到 sd 卡,然后联网的情况下再上传到服务器。
具体实现
创建一个类,实现接口
UncaughtExceptionHandlerpublic class CrashHandler implements UncaughtExceptionHandler
实现接口的方法
uncaughtException(Thread t, Throwable e),异常的处理就在这里,比如保存异常信息到 SD 卡,自定义错误弹出信息,自动退出。@Override
public void uncaughtException(Thread t, Throwable e) {
//收集错误信息,保存到 sd 卡上
errorInfo2SD();
//弹出自定义的错误提醒
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(mContext, "UnCrashException", Toast.LENGTH_SHORT).show();
Looper.loop();
}
});
//杀掉进程,退出应用
Process.killProcess(Process.myPid());
System.exit(1);
}
CrashHandler采用单例模式。public class CrashHandler implements UncaughtExceptionHandler { private static CrashHandler mInstance; private CrashHandler() { } // 单例模式-懒汉
public static CrashHandler getInstance() {
if (mInstance == null) {
synchronized (CrashHandler.class) {
if (mInstance == null) {
mInstance = new CrashHandler();
}
}
}
return mInstance;
} }
定义一个方法,用于把当前应用注册到系统的异常处理中,让系统知道由自定义的异常捕获器处理。
public void register(Context context) {
mContext = context;
Thread.setDefaultUncaughtExceptionHandler(this);
}
在 Application 中注册。
public class CrashHandlerApplication extends Application { @Override
public void onCreate() {
super.onCreate();
CrashHandler.getInstance().register(getApplicationContext());
}
}
收集错误信息并保存到 SD 卡上。
//用于存储设备信息
private Map<String, String> mInfo = new HashMap<>();
//格式化时间,作为Log文件名
private java.text.DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss"); private void errorInfo2SD(Throwable e) {
PackageManager pm = mContext.getPackageManager();
try {
PackageInfo info = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
// 获取版本信息
if (info != null) {
String versionName = TextUtils.isEmpty(info.versionName) ? "未设置版本名称" : info.versionName;
String versionCode = info.versionCode + "";
mInfo.put("versionName", versionName);
mInfo.put("versionCode", versionCode);
}
// 获取设备信息
Field[] fields = Build.class.getFields();
if (fields != null && fields.length > 0) {
for (Field field : fields) {
field.setAccessible(true);
mInfo.put(field.getName(), field.get(null).toString());
}
}
// 存储信息到 sd 卡指定目录
saveErrorInfo(e);
} catch (NameNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} private void saveErrorInfo(Throwable e) {
StringBuffer stringBuffer = new StringBuffer();
for (Map.Entry<String, String> entry : mInfo.entrySet()) {
String keyName = entry.getKey();
String value = entry.getValue();
stringBuffer.append(keyName + "=" + value + "\n");
}
stringBuffer.append("\n-----Crash Log Begin-----\n");
StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(stringWriter);
e.printStackTrace(writer);
Throwable cause = e.getCause();
while (cause != null) {
cause.printStackTrace(writer);
cause = e.getCause();
}
writer.close();
String string = stringWriter.toString();
stringBuffer.append(string);
stringBuffer.append("\n-----Crash Log End-----");
String format = dateFormat.format(new Date());
String fileName = "crash-" + format + ".log"; if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String path = mContext.getFilesDir() + File.separator + "crash";
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
FileOutputStream fou = null;
try {
fou = new FileOutputStream(new File(path, fileName));
fou.write(stringBuffer.toString().getBytes());
fou.flush();
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} finally {
try {
if (fou != null) {
fou.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
以上完成了异常的捕获并保存到 sd 卡上,等待 app 再次启动的时候,上传异常信息到服务器上。
保存到 sd 卡上的位置

SD 卡中拿到的 Log 信息
SUPPORTED_64_BIT_ABIS=[Ljava.lang.String;@58ff504
versionCode=1
BOARD=msm8939
BOOTLOADER=3.19.0.0000
TYPE=user
ID=MMB29M
TIME=1461124295000
BRAND=htc
SERIAL=HC4AZYC00984
HARDWARE=qcom
SUPPORTED_ABIS=[Ljava.lang.String;@abaed
CPU_ABI=arm64-v8a
RADIO=unknown
IS_DEBUGGABLE=false
MANUFACTURER=HTC
SUPPORTED_32_BIT_ABIS=[Ljava.lang.String;@c11fd17
TAGS=release-keys
CPU_ABI2=
UNKNOWN=unknown
USER=buildteam
FINGERPRINT=htc/htccn_chs_2/htc_a51dtul:6.0.1/MMB29M/738098.4:user/release-keys
HOST=ABM110
PRODUCT=htccn_chs_2
versionName=1.0
DISPLAY=MMB29M release-keys
MODEL=HTC D820u
DEVICE=htc_a51dtul -----Crash Log Begin-----
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.CharSequence android.widget.TextView.getText()' on a null object reference
at cc.lijingbo.crashhandler.MainActivity$1.onClick(MainActivity.java:26)
at android.view.View.performClick(View.java:5232)
at android.view.View$PerformClick.run(View.java:21289)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:168)
at android.app.ActivityThread.main(ActivityThread.java:5885)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687) -----Crash Log End-----
通过以上 log , 可以看到因为
NullPointerException造成的退出。原因是MainActivity.java的第26行造成的。工程的
MainActivity类的源码,可以看到获取tv2的时候因为没有实例化,造成的空指针public class MainActivity extends AppCompatActivity { private TextView tv1;
private TextView tv2; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv1 = (TextView) findViewById(R.id.tv1); tv1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, tv2.getText().toString(), Toast.LENGTH_SHORT).show();
}
}); } }
Android app 全局异常统一处理的更多相关文章
- android中全局异常捕捉
android中全局异常捕捉 只要写代码就会有bug,但是我们要想办法收集到客户的bug.有第三方bugly或者友盟等可以收集.但是,android原生就提供了有关收集异常的api,所以我们来学习一下 ...
- Android捕获全局异常
Android捕获全局异常 程序避免不了出现bug,导致程序崩溃,为了尽量不影响用户体验,可以全局捕获异常 效果图 异常捕获处理前 异常捕获处理后(将程序重新启动) 捕获异常的工具类 package ...
- SpringMVC全局异常统一处理
SpringMVC全局异常统一处理以及处理顺序最近在使用SpringMVC做全局异常统一处理的时候遇到的问题,就是想把ajax请求和普通的网页请求分开返回json错误信息或者跳转到错误页. 在实际做的 ...
- Springboot项目全局异常统一处理
转自https://blog.csdn.net/hao_kkkkk/article/details/80538955 最近在做项目时需要对异常进行全局统一处理,主要是一些分类入库以及记录日志等,因为项 ...
- APP全局异常捕获,并保存本地文件
public class CrashHandler implements Thread.UncaughtExceptionHandler { public static final String TA ...
- Application中捕获APP中的全局异常
package com.example.administrator.mystudent; import android.app.Application; import android.util.Log ...
- 【转】 Android自定义捕获Application全局异常
大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,开发者个人不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了 ...
- Android全局异常捕捉
// 定义自定义捕捉 package com.xiaosw.test; import java.io.File; import java.io.FileOutputStream; import jav ...
- Android应用捕获全局异常自定义处理
[2016-06-30]最新的全局异常处理DRCrashHandler已经集成在DR_support_lib库中 具体请看: https://coding.net/u/wrcold520/p/DR_s ...
随机推荐
- java 从spring容器中获取注入的bean对象
java 从spring容器中获取注入的bean对象 CreateTime--2018年6月1日10点22分 Author:Marydon 1.使用场景 控制层调用业务层时,控制层需要拿到业务层在 ...
- Selenium如何支持测试Windows application
很长一段时间大家都支持Selenium是只支持测试Web应用程序的. 纵观Selenium的成长历程,不难解释这个原因.Selenium开始的时候是作为一个Firefox中的插件出现的,而且做得是纯J ...
- redis数据淘汰策略
概述 在 redis 中,允许用户设置最大使用内存大小 server.maxmemory,在内存限定的情况下是很有用的.譬如,在一台 8G 机子上部署了 4 个 redis 服务点,每一个服务点分配 ...
- UML - EA 序列图
序列图中的 Fragment 的类型(Loop.Opt.Par和Alt) (还有: ) 序列图主要用于展示对象之间交互的顺序. 序列图将交互关系表示为一个二维图.纵向是时间轴,时间沿竖线向下延伸.横向 ...
- Newifi2(D1) 刷入pb-boot和breed的记录
今天要给一个newifi d1刷系统时发现居然还是原厂的uboot, 使用uboot刷入rom时会进行校验拦截第三方的rom. 之前有刷过这个设备的, 但是已经完全记不清怎么处理的了. 查了一下, 这 ...
- 共享内存:mmap函数实现
内存映射的应用: 以页面为单位,将一个普通文件映射到内存中,通常在须要对文件进行频繁读写时使用,这样用内存读写代替I/O读写,以获得较高的性能; 将特殊文件进行匿名内存映射,能够为关联进程提供共享内存 ...
- Ubuntu 12.10安装QQ2012
[日期:2012-11-05] 在最新的Ubuntu 12.10下安装QQ2012,请根据自己的机器类型下载后按照下面的32位或64位安装说明安装. 下载网址:http://www.longene.o ...
- MATLAB 向量
MATLAB 向量: 1.MATLAB 行向量: 创建行向量括在方括号中的元素的集合,用空格或逗号分隔的元素. 2.MATLAB 列向量: 创建列向量括在方括号中的元素的集合,使用分号来分隔的元素. ...
- Python实现鸢尾花数据集分类问题——基于skearn的LogisticRegression
Python实现鸢尾花数据集分类问题——基于skearn的LogisticRegression 一. 逻辑回归 逻辑回归(Logistic Regression)是用于处理因变量为分类变量的回归问题, ...
- Nginx+FastCGI运行原理(二)
1.4 PHP与PHP-FPM的安装及优化(2) 标签rlimit_files用于设置PHP-FPM对打开文件描述符的限制,默认值为1024.这个标签的值必须和Linux内核打开文件数关联起来,例如, ...