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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值