Android UncaughtExceptionHandler进行全局异常捕获
2023-09-14 08:57:28 时间
在实际开发过程中,我们的APP由于各种原因,难免会有Crash现象(应用程序XXX已经停止)。这样给用户一种很不友好的感觉,那么我们如何去处理这种情况呢?答案就在实现UncaughtchExceptionHanlder,复写uncaughtException()方法。
当crash发生的时候,系统会调用UncaughtchExceptionHanlder#uncaughtException()。在uncaughtException()中我们可以选择收集错误信息,然后保存在SD卡中,在合适的时机将错误日志上传至服务器。这样开发人员在后期维护的时候,就可以有针对性的修复BUG。由于默认的异常处理器是Thread类的静态成员,所以它的作用对象是当前进程的所有线程。下面是一个常用的标准的异常处理器三步走。
一)实现自定义CrashHandler
package com.example.crashhandler; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.Field; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Build; import android.os.Environment; import android.os.Looper; import android.os.Process; import android.util.Log; import android.widget.Toast; import com.lidroid.xutils.HttpUtils; import com.lidroid.xutils.http.ResponseInfo; import com.lidroid.xutils.http.callback.RequestCallBack; import com.lidroid.xutils.http.client.HttpRequest.HttpMethod; public class CrashHandler implements UncaughtExceptionHandler { private static final String TAG = "CrashHandler"; private UncaughtExceptionHandler mDefaultHandler; private static CrashHandler crashHandler = new CrashHandler(); private Context mContext; /** 错误日志文件 */ private File logFile = new File(Environment.getExternalStorageDirectory(),"crashLog.trace"); private CrashHandler() { public static CrashHandler getInstance() { if (crashHandler == null) { synchronized (CrashHandler.class) { if (crashHandler == null) { crashHandler = new CrashHandler(); return crashHandler; public void init(Context context) { mContext = context; mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); //设置为线程默认的异常处理器 Thread.setDefaultUncaughtExceptionHandler(this); @Override public void uncaughtException(Thread thread, Throwable ex) { // 打印异常信息 ex.printStackTrace(); // 我们没有处理异常 并且默认异常处理不为空 则交给系统处理 if (!handlelException(ex) mDefaultHandler != null) { // 系统处理 mDefaultHandler.uncaughtException(thread, ex); } else { try { Thread.sleep(3 * 1000); } catch (InterruptedException e) { e.printStackTrace(); try { // 上传错误日志到服务器 upLoadErrorFileToServer(logFile); } catch (Exception e) { e.printStackTrace(); Intent intent = new Intent(mContext, SplashActivity.class); // 新开任务栈 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); // 杀死我们的进程 Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { Process.killProcess(Process.myPid()); }, 2 * 1000); private boolean handlelException(Throwable ex) { if (ex == null) { return false; // 使用Toast来显示异常信息 new Thread() { @Override public void run() { Looper.prepare(); Toast.makeText(mContext, "程序发生异常,即将重启", Toast.LENGTH_LONG) .show(); Looper.loop(); }.start(); PrintWriter pw = null; try { if (!logFile.exists()) { logFile.createNewFile(); pw = new PrintWriter(logFile); // 收集手机及错误信息 logFile = collectInfoToSDCard(pw, ex); pw.close(); } catch (Exception e) { e.printStackTrace(); return true; * 上传错误日志到服务器 * @param logFile * @throws IOException private void upLoadErrorFileToServer(File errorFile) { * 收集手机信息 * @throws NameNotFoundException private File collectInfoToSDCard(PrintWriter pw, Throwable ex) throws NameNotFoundException { PackageManager pm = mContext.getPackageManager(); PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(),PackageManager.GET_ACTIVITIES); // 错误发生时间 String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); pw.print("time : "); pw.println(time); // 版本信息 pw.print("versionCode : "); pw.println(pi.versionCode); // 应用版本号 pw.print("versionName : "); pw.println(pi.versionName); try { /** 暴力反射获取数据 */ Field[] Fields = Build.class.getDeclaredFields(); for (Field field : Fields) { field.setAccessible(true); pw.print(field.getName() + " : "); pw.println(field.get(null).toString()); } catch (Exception e) { Log.i(TAG, "an error occured when collect crash info" + e); // 打印堆栈信息 ex.printStackTrace(pw); return logFile; }
2)在MyApplication中实例化CrashHanlder
package com.example.crashhandler; import android.app.Application; public class MyApplication extends Application { private static MyApplication mInstance; public static MyApplication getInstance(){ return mInstance; @Override public void onCreate() { super.onCreate(); mInstance = this; CrashHandler crashHandler = CrashHandler.getInstance(); crashHandler.init(this);
3)应用到Manifest.xml中
?xml version="1.0" encoding="utf-8"? manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.crashhandler" android:versionCode="1" android:versionName="1.0.0" uses-sdk android:minSdkVersion="8" android:targetSdkVersion="21" / uses-permission android:name="android.permission.INTERNET"/ uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/ application android:name=".MyApplication" android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" activity android:name=".SplashActivity" android:label="@string/app_name" intent-filter action android:name="android.intent.action.MAIN" / category android:name="android.intent.category.LAUNCHER" / /intent-filter /activity activity android:name=".MainActivity" /activity /application /manifest
至此,我们的自定义异常处理器就算写完了。注释比较多,就不赘述了。有不清楚的读者请下面评论,我会一一回复。下面,测试下我们的异常处理器。代码很简单,SplshActivity---- MainActivity。在MainActivity中点击按钮抛出一个空指针异常。详情看代码
package com.example.crashhandler; import java.util.Timer; import java.util.TimerTask; import android.app.Activity; import android.content.Intent; import android.os.Bundle; public class SplashActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash); MyApplication.getInstance(); // 两秒后进入MainActivity Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { Intent intent = new Intent(SplashActivity.this, MainActivity.class); startActivity(intent); }, 2*1000);
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); crash = (Button) findViewById(R.id.crash); crash.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { throw new NullPointerException();
点击按钮之后,触发我们的全局未捕获异常处理器。手机错误信息保存至SD卡,随后新开任务栈重启SplashActivity。点我下载Demo源代码
待完善:
对SD卡是否存在进行判断 对错误日志文件轮询,新开service上传至服务器Android在Application层级维护和管理全局所有Activity的方法ActivityLifecycleCallbacks 经常看到有些项目中经常性的把所有activity继承自一个base的Activity,然后在每一次启动新activity时候添加当前activity到一个全局List那样的列表中,已达到全局管理和维护activity的目的,这种做法大概是四五年前的技术解决方案。
Android项目实战(二十三):仿QQ设置App全局字体大小 原文:Android项目实战(二十三):仿QQ设置App全局字体大小 一、项目需求: 因为产品对象用于中老年人,所以产品设计添加了APP全局字体调整大小功能。 这里仿做QQ设置字体大小的功能。 QQ实现的效果是,滚动下面的seekbar,当只有seekbar到达某一个刻度的时候,这时候上部分的效果展示部分会改变文字大小, 但是在拖动过程中字体不会改变。
相关文章
- 聚焦 Android 11: 大功告成
- android开机动画多长时间_Android开机动画及黑屏[通俗易懂]
- android 浏览器 开发,Android 浏览器的开发实例分享
- android 空间分享到朋友圈,Android开发之微信分享到好友,朋友圈
- android移动点餐系统内容和要求,基于Android云计算的移动点餐系统
- android签名命令行,Android系统签名位置及命令
- android XSS攻击
- android sdk根目录,Android SDK位置
- Android开发环境配置
- 【Android布局】在程序中设置android gravity 和 android layout Gravity属性
- 错误解决:widget.FrameLayout$LayoutParams cannot be cast to android.widget.LinearLayout$LayoutParams
- android进程间通信的方式_Android进程注入
- android线程间通信的几种方法_Android进程间和线程间通信方式
- android 系统浏览器 源码-Android 最最最简单的浏览器代码
- Android 图片获取显示照片拍摄时间
- 【错误记录】p7zip 交叉编译 Android 版本 NDK 报错 ( error: case value evaluates to -2 , which cannot be narrowed )
- 【错误记录】Android Studio 编译报错 ( Could not find com.android.tools.build:gradle:4.2.1. )
- 【Android 逆向】Android 逆向通用工具开发 ( PC 端工程分析 | 网络初始化操作 | PC 端工程核心业务逻辑 )
- 【错误记录】Android 应用安全检测漏洞修复 ( StrandHogg 漏洞 | 设置 Activity 组件 android:taskAffinity=““ )
- 【Android UI】Canvas 画布 ① ( Canvas 状态栈 | Canvas 状态栈入栈与出栈 | 获取 Canvas 状态栈容量 | Canvas 状态栈原点数据 )
- Android控制文字水平间距android:letterSpacing详解手机开发
- Android开发中遇到的问题(三)——eclipse创建android项目无法正常预览布局文件详解手机开发
- [android] 手机卫士黑名单功能(短信拦截)详解手机开发
- android 自定义控件那些事详解手机开发
- Android实现PHP连接MySQL进行数据交互(android通过php连接mysql)
- 使用Android实现连接MySQL数据库:实现快速数据交互与管理(android连接mysql数据库)
- Android源码学习之组合模式定义及应用
- android版本检测Android程序的版本检测与更新实现介绍
- android将图片转换存到数据库再从数据库读取转换成图片实现代码