android bug监控,Android UncaughtExceptionHandler 全局异常监控
一、全局捕获异常
为了解决这样的问题,我们需要能够及时的捕获异常,但要捕获的地方是在太多,因此,我们需要进行全局性的异常捕获,那么如何捕获全局异常呢?
答案是UncaughtExceptionHandler+Thread.setDefaultUncaughtExceptionHandler
1.UncaughtExceptionHandler是未捕获异常的处理接口,该类率先捕获异常
UncaughtExceptionHandler: 线程未捕获异常控制器是用来处理未捕获异常的。
如果程序出现了未捕获异常默认情况下则会出现强行关闭对话框
实现该接口并注册为程序中的默认未捕获异常处理
这样当未捕获异常发生时,就可以做些异常处理操作
例如:收集异常信息,发送错误报告 等。
二、代码实战
对于这个接口,我们需要进行实现
public class AppCrashHandler implements UncaughtExceptionHandler {
private Context mContext;
private Thread.UncaughtExceptionHandler mDefaultHandler;
/**防止多线程中的异常导致读写不同步问题的lock**/
private Lock lock = null;
/**本地保存文件日志**/
private final String CRASH_REPORTER_EXTENSION = ".crash";
/**日志tag**/
private final String STACK_TRACE = "logStackTrance";
/**保存文件名**/
private final String crash_pref_path ="app_crash_pref.xml";
private static final String OOM = "java.lang.OutOfMemoryError";
private static final String HPROF_FILE_PATH = Environment.getExternalStorageDirectory().getPath() + "/data_crash.hprof"
private AppCrashHandler()
{
lock = new ReentrantLock(true);
}
/**
* 获得单例对象
* @param context
* @return AppCrashHandler
*/
public static AppCrashHandler shareInstance(Context context){
AppCrashHandler crashhandler = AppCrashHandler.InstanceHolder.crashHandler;
crashhandler.initCrashHandler(context);
return crashhandler;
}
/**
* 使用初始化方法初始化,防止提前初始化或者重复初始化
* @param cxt
*/
private void initCrashHandler(Context cxt)
{
if(!hasInitilized()){
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
mContext = cxt;
}
}
public interface InstanceHolder
{
public static AppCrashHandler crashHandler = new AppCrashHandler();
}
public static boolean isOOM(Throwable throwable){
Log.d(TAG, "getName:" + throwable.getClass().getName());
if(OOM.equals(throwable.getClass().getName())){
return true;
}else{
Throwable cause = throwable.getCause();
if(cause != null){
return isOOM(cause);
}
return false;
}
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if(isOOM(throwable)){
try {
Debug.dumpHprofData(HPROF_FILE_PATH);
} catch (Exception e) {
Log.e(TAG, "couldn’t dump hprof", e);
}
}
if (!handleExceptionMessage(ex) && mDefaultHandler != null) {
// 如果用户没有处理则让系统默认的异常处理器来处理
mDefaultHandler.uncaughtException(thread, ex);
} else {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Log.e(STACK_TRACE, "Error : ", e);
}
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(10);
}
}
/**
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 开发者可以根据自己的情况来自定义异常处理逻辑
* @param ex
* @return true:如果处理了该异常信息;否则返回false
*/
private boolean handleExceptionMessage(Throwable ex)
{
if (ex == null)
{
return false;
}
// 使用Toast来显示异常信息
new Thread() {
@Override
public void run() {
// Toast 显示需要出现在一个线程的消息队列中
Looper.prepare();
Toast.makeText(mContext, "程序出错啦,即将退出", Toast.LENGTH_LONG).show();
Looper.loop();
}
}.start();
String fileName = mContext.getPackageName()+"-"+"appCrash-Exception"+ CRASH_REPORTER_EXTENSION;
String crashFileName = saveExceptionToFile(ex,fileName);
SharedPreferences.Editor editor = mContext.getSharedPreferences(crash_pref_path , Context.MODE_PRIVATE).edit();
editor.putString(STACK_TRACE, crashFileName);
editor.commit();
Log.d(STACK_TRACE, "errorLogPath="+crashFileName);
return true;
}
/**
* 是否已初始化
* @return
*/
public boolean hasInitilized()
{
return mContext!=null;
}
/**
* 保存错误信息到文件中
* @param ex
* @return
* @throws IOException
*/
private String saveExceptionToFile(Throwable ex,String fileName)
{
File saveFile = null;
PrintWriter printWriter = null;
try {
lock.tryLock();
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录
saveFile = new File(sdCardDir, fileName);
}else{
saveFile =new File(mContext.getFilesDir(),fileName);
}
if(!saveFile.exists())
{
saveFile.createNewFile();
}
printWriter = new PrintWriter(saveFile);
String result = formatException(ex);
printWriter.write(result);
printWriter.flush();
Log.e("CrashException", result);
}catch(Exception e){
e.printStackTrace();
} finally{
if(printWriter!=null)
{
printWriter.close();
}
lock.unlock();
}
return saveFile!=null?saveFile.getAbsolutePath():null;
}
/**
* 格式化异常信息
* @param e
* @return
*/
@SuppressLint("SimpleDateFormat")
private String formatException(Throwable e)
{
StringBuilder sb = new StringBuilder();
StackTraceElement[] stackTrace = e.getStackTrace();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if (stackTrace!=null)
{
String timeStramp = sdf.format(new Date(System.currentTimeMillis()));
String format = String.format("DateTime:%s\nExceptionName:%s\n\n",timeStramp,e.getLocalizedMessage());
sb.append(format);
for (int i = 0; i
{
StackTraceElement traceElement = stackTrace[i];
String fileName = traceElement.getFileName();
int lineNumber = traceElement.getLineNumber();
String methodName = traceElement.getMethodName();
String className = traceElement.getClassName();
sb.append(String.format("%s\t%s[%d].%s \n",className,fileName,lineNumber,methodName));
}
sb.append(String.format("\n%s",e.getMessage()));
Writer stringWriter = new StringWriter();
PrintWriter pw = new PrintWriter(stringWriter);
e.printStackTrace(pw);
pw.flush();
pw.close();
sb.append("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
sb.append(stringWriter.toString());
}
return sb.toString();
}
}
这里只保存了文件,一般来说,当app第二次启动时我们需要将该文件上传到网络,时间不是很充裕,这里上传暂时不贴代码了,时间充裕的话会及时补充,请保持关注吧
2.初始化,监听全局异常信息,这里需要继承Application,并替换系统默认的Application
public class BaseApplication extends Application
{
private static BaseApplication instance = null;
private AppCrashHandler appCrashHandler = null;
@Override
public void onCreate() {
synchronized (this)
{
if(instance==null)
{
instance = this;
}
appCrashHandler = AppCrashHandler.shareInstance(instance);
}
super.onCreate();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
}
修改清单文件
android:name="com.hali.luya.unitest.BaseApplication "
android:hardwareAccelerated="true"
android:icon="@drawable/app_logo"
android:logo="@drawable/app_logo"
android:label="@string/app_name"
android:configChanges="locale|keyboard|screenSize"
android:theme="@style/Theme.AppBaseTheme" >
三、腾讯Bugly
腾讯有一个bugly产品可以实现crash收集和处理,当然也可以同时使用UncaughtExceptionHandler,因为腾讯bugly虽然也实现了UncaughtExceptionHandler该回调,但腾讯bugly在捕获异常的同时也会调用你自己的UncaughtExceptionHandler。
目前腾讯的bugly不支持回调,但我申请到了腾讯的内测版支持回调。
public class BaseApplication extends Application
{
private static Application instance = null;
private AppCrashHandler appCrashHandler = null;
private final String APP_CONTEXT_TAG = "appContext";
@Override
public void onCreate() {
synchronized (this)
{
if(instance==null)
{
instance = this;
}
appCrashHandler = AppCrashHandler.shareInstance(instance);
UserStrategy strategy = new UserStrategy(instance); //App的策略Bean
strategy.setAppChannel(getPackageName()); //设置渠道
strategy.setAppVersion(getVersion()); //App的版本
strategy.setAppReportDelay(1000); //设置SDK处理延时,毫秒
strategy.setDeviceID(GlobalUtil.getInstance().getDeviceID(instance));
strategy.setCrashHandleCallback(new AppCrashHandleCallback());
CrashReport.initCrashReport(instance, "900001335", true, strategy); //自定义策略生效,必须在初始化SDK前调用
CrashReport. setUserId("BBDTEK");
}
//shutDownLog();
super.onCreate();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
/**
* 获取版本号
* @return 当前应用的版本号
*/
public String getVersion() {
try {
PackageManager manager = this.getPackageManager();
PackageInfo info = manager.getPackageInfo(this.getPackageName(), 0);
String version = info.versionName;
return this.getString(R.string.app_version) + version;
} catch (Exception e) {
e.printStackTrace();
return this.getString(R.string.app_version);
}
}
private class AppCrashHandleCallback extends CrashHandleCallback //bugly回调
{
@Override
public synchronized Map onCrashHandleStart(int crashType, String errorType, String errorMessage, String errorStack)
{
String crashTypeName = null;
switch (crashType)
{
case CrashHandleCallback.CRASHTYPE_JAVA_CATCH:
crashTypeName = "JAVA_CATCH";
break;
case CrashHandleCallback.CRASHTYPE_JAVA_CRASH:
crashTypeName = "JAVA_CRASH";
break;
case CrashHandleCallback.CRASHTYPE_NATIVE:
crashTypeName = "JAVA_NATIVE";
break;
case CrashHandleCallback.CRASHTYPE_U3D:
crashTypeName = "JAVA_U3D";
break;
default:
{
crashTypeName = "unknown";
}
}
Log.e(APP_CONTEXT_TAG, "Crash Happen Type:" + crashType + " TypeName:" + crashTypeName);
Log.e(APP_CONTEXT_TAG, "errorType:" + errorType);
Log.e(APP_CONTEXT_TAG, "errorMessage:" + errorMessage);
Log.e(APP_CONTEXT_TAG, "errorStack:" + errorStack);
Map userDatas = super.onCrashHandleStart(crashType, errorType, errorMessage, errorStack);
if (userDatas == null)
{
userDatas = new HashMap();
}
userDatas.put("DEBUG", "TRUE");
return userDatas;
}
}
/**
* 关闭重要信息的日志
*/
private void shutDownLog()
{
LogUtils.allowE = false;
LogUtils.allowI = false;
LogUtils.allowV = false;
LogUtils.allowW = false;
LogUtils.allowWtf = false;
LogUtils.allowD = false;
}
}
try doing it
android bug监控,Android UncaughtExceptionHandler 全局异常监控相关推荐
- Android UncaughtExceptionHandler 全局异常监控
2019独角兽企业重金招聘Python工程师标准>>> 一.全局捕获异常 为了解决这样的问题,我们需要能够及时的捕获异常,但要捕获的地方是在太多,因此,我们需要进行全局性的异常捕获, ...
- Asp.net Core全局异常监控和记录日志
前言 系统异常监控可以说是重中之重,系统不可能一直运行良好,开发和运维也不可能24小时盯着系统,系统抛异常后我们应当在第一时间收到异常信息.在Asp.net Core里我使用拦截器和中间件两种方式来监 ...
- 在Android中自定义捕获Application全局异常,可以替换掉系统的强制退出对话框(很有参考价值与实用价值)
在Android中自定义捕获Application全局异常,可以替换掉系统的强制退出对话框(很有参考价值与实用价值) 参考文章: (1)在Android中自定义捕获Application全局异常,可以 ...
- 小程序异常监控及错误处理
小程序异常监控收集 web端与小程序错误监控差异 在 Web 端监测的是页面完整的 url,而小程序端监测的是路由地址: 小程序页面属于app内部的页面,使用时已全部加载完毕,因此监控页面性能时不统计 ...
- 如何将数据指标异常监控和归因分析自动化
目录 一.数据指标监控与归因目的 二.监控与归因框架 三.指标监控方法与实施 3.1 指标异常监控方法 3.2 梳理核心监控指标并进行异常监控 四.异常归因方法与实施 4.1 Adtributor根因 ...
- android捕获全局异常,并对异常做出处理
2019独角兽企业重金招聘Python工程师标准>>> 在做项目时,经常会把错误利用异常抛出去,这样在开发时就可以通过手机抛的异常排查错误,很方便.但是当程序开发完毕,版本稳定,需要 ...
- Android开发之全局异常捕获
Android开发之全局异常捕获 [转载请注明出处]本文出自付小华的博客 http://blog.csdn.net/klxh2009 今晨谷歌正式发布Android 8.0,新版本的Android O ...
- Windows平台监控Android App应用
1.adb.exe 原理: adb 的运行原理是 PC 端的 adb server 与手机端的守护进程 adbd 建立连接,然后 PC 端的 adb client 通过 adb server 转发命令 ...
- Android全局异常捕捉器
Android全局异常捕捉器主要是捕获应用在运行中无法预计的异常 1.首先要写一个MyCrashHandler实现UncaughtExceptionHandler package com.exampl ...
最新文章
- 八大主流Linux桌面环境特性汇总报告
- MySQL高级 - 复制 - 集群搭建
- java和Js中的类型转换_JavaScript 类型转换
- foreach循环符合就不往下走了_柴油发电机组冷却液循环故障解决方法
- win10下安装和卸载Ubuntu双系统
- 创建Session时会把含有Session ID 的Cookie对象加到响应对象上
- 「专题训练」k-Tree(CodeForces Round #247 Div.2 C)
- 聚类算法之DBSCAN
- 2011-7-3北京李庄儿童医院看病记
- mysql++简单应用_MySQL简单应用之视图(view)
- 淘淘商城第30讲——实现商品添加功能
- python程序画吉他和弦
- 【华为OD机试真题 python】特异性双端队列 | 最小调整顺序次数【2022 Q4 | 100分】
- 快消品行业存在痛点分析
- 让我们再聊聊浏览器资源加载优化
- 接收机的噪声来源与噪声分析
- OSPF高级特性 —— 管理距离(优先级)修改 + 外部路由的metric值的修改
- 【Unity开发小技巧】FMS有限状态机详解
- 数学基础 -- 基础定义(集合、实数集、映射、函数)
- 损失函数笔记(2)--对比损失