Android全局异常捕获并弹窗提示
来源:互联网 发布:暴雪mac可以玩的游戏 编辑:程序博客网 时间:2024/05/16 10:23
Android 难免有崩溃的时候,但是崩溃了该如何处理呢?虽然那天有位同仁说 “既然崩溃了,用户体验就差了,心里会想这是毛APP,下次也不想用了” ,所以检查BUG以防崩溃是必须的,但是也需要一个后备方案,崩溃了能友好些,我们也能收集一些崩溃的信息。
说到全局捕获异常的UncaughtExceptionHandler,就不得不说期间遇到的各种坑:
1. 初始化肯定在Application,网上说的Activity启各种不认同。但在Application启就存在不能弹AlertDialog的问题(目前不确定,不知道是自己哪里没处理好还是的确是这个问题,有时间再验证一下)
2. 崩溃不一定是单次,在多层Activity中,崩溃一个顶层的Activity可能导致下层的Activity连续崩溃,所以uncaughtException可能会捕获到多次崩溃信息(具体影响后面会说到)
先来张崩溃后的效果图:
背景是另一个APP,当前的APP已崩溃并弹出该提示
实现流程:
写个类继承于UncaughtExceptionHandler,实现方法
@Override public void uncaughtException(Thread thread, Throwable ex) { if (!handleException(ex) && mDefaultHandler != null) { // 如果用户没有处理则让系统默认的异常处理器来处 mDefaultHandler.uncaughtException(thread, ex); } else { // 跳转到崩溃提示Activity Intent intent = new Intent(mContext, CrashDialog.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); System.exit(0);// 关闭已奔溃的app进程 } }
然后转handleException方法处理异常:
private boolean handleException(Throwable ex) { if (ex == null) { return false; } // 收集错误信息 getCrashInfo(ex); return true; }
上面的代码很清楚了,如果异常被捕获到并且异常信息不会NULL,处理完则跳转到CrashDialog。为什么跳Activity用Dialog样式,而不直接弹AlertDialog,是因为的确弹不出来。
收集错误信息:
private void getCrashInfo(Throwable ex) { Writer writer = new StringWriter(); PrintWriter printWriter = new PrintWriter(writer); ex.printStackTrace(printWriter); Throwable cause = ex.getCause(); while (cause != null) { cause.printStackTrace(printWriter); cause = cause.getCause(); } printWriter.close(); String errorMessage = writer.toString(); if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { String mFilePath = Environment.getExternalStorageDirectory() + "/" + App.ERROR_FILENAME; FileTxt.WirteTxt(mFilePath, FileTxt.ReadTxt(mFilePath) + '\n' + errorMessage); } else { Log.i(App.TAG, "哦豁,说好的SD呢..."); } }
是的,我把错误信息写到了存储并在刚才的CrashDialog中读取。为什么不直接传值呢?因为刚说到的坑第2条,多次崩溃的情况下,将导致直接传值只会传最后一次崩溃信息,而最后一次崩溃信息并不是主要引发崩溃的点,收集上来的错误信息可读性不大。那为什么我不写个全局变量来存储呢?因为尝试过,不知道是机型问题(Huawei Mate7 - API 23)还是全部问题,变量压根就不记录数据,最后只有将信息依次写到存储。
主要代码
App.java
package cn.qson.androidcrash;/** * @author x024 */import android.app.Application;public class App extends Application { public final static String TAG = "x024"; public final static String ERROR_FILENAME = "x024_error.log"; @Override public void onCreate() { super.onCreate(); CrashHanlder.getInstance().init(this); }}
CrashHanlder.java
package cn.qson.androidcrash;/** * @author x024 */import java.io.PrintWriter;import java.io.StringWriter;import java.io.Writer;import java.lang.Thread.UncaughtExceptionHandler;import android.content.Context;import android.content.Intent;import android.os.Environment;import android.util.Log;/** * 收集错误报告并上传到服务器 * * @author x024 * */public class CrashHanlder implements UncaughtExceptionHandler { private Thread.UncaughtExceptionHandler mDefaultHandler; // CrashHandler实例 private static CrashHanlder INSTANCE = new CrashHanlder(); // 程序的Context对象 private Context mContext; private CrashHanlder() { } public static CrashHanlder getInstance() { return INSTANCE; } /** * 初始化 * * @param context */ public void init(Context context) { mContext = context; // 获取系统默认的UncaughtException处理 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); // 设置该CrashHandler为程序的默认处理 Thread.setDefaultUncaughtExceptionHandler(this); } /** * 异常捕获 */ @Override public void uncaughtException(Thread thread, Throwable ex) { if (!handleException(ex) && mDefaultHandler != null) { // 如果用户没有处理则让系统默认的异常处理器来处 mDefaultHandler.uncaughtException(thread, ex); } else { // 跳转到崩溃提示Activity Intent intent = new Intent(mContext, CrashDialog.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); System.exit(0);// 关闭已奔溃的app进程 } } /** * 自定义错误捕获 * * @param ex * @return true:如果处理了该异常信息;否则返回false. */ private boolean handleException(Throwable ex) { if (ex == null) { return false; } // 收集错误信息 getCrashInfo(ex); return true; } /** * 收集错误信息 * * @param ex */ private void getCrashInfo(Throwable ex) { Writer writer = new StringWriter(); PrintWriter printWriter = new PrintWriter(writer); ex.printStackTrace(printWriter); Throwable cause = ex.getCause(); while (cause != null) { cause.printStackTrace(printWriter); cause = cause.getCause(); } printWriter.close(); String errorMessage = writer.toString(); if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { String mFilePath = Environment.getExternalStorageDirectory() + "/" + App.ERROR_FILENAME; FileTxt.WirteTxt(mFilePath, FileTxt.ReadTxt(mFilePath) + '\n' + errorMessage); } else { Log.i(App.TAG, "哦豁,说好的SD呢..."); } }}
CrashDialog.java
package cn.qson.androidcrash;/** * @author x024 */import java.io.BufferedReader;import java.io.FileNotFoundException;import java.io.FileReader;import java.io.IOException;import android.app.Activity;import android.app.ActivityManager;import android.content.Context;import android.content.Intent;import android.os.Build;import android.os.Bundle;import android.os.Environment;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class CrashDialog extends Activity { private String mFilePath; private Button btnExit, btnRestart; private Boolean StorageState = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_crash); CrashDialog.this.setFinishOnTouchOutside(false); if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { mFilePath = Environment.getExternalStorageDirectory() + "/" + App.ERROR_FILENAME; StorageState = true; } else { Log.i(App.TAG, "哦豁,说好的SD呢..."); } new Thread(upLog).start(); initView(); } private void initView() { btnExit = (Button) findViewById(R.id.cash_exit); btnRestart = (Button) findViewById(R.id.cash_restart); btnExit.setOnClickListener(mOnClick); btnRestart.setOnClickListener(mOnClick); } OnClickListener mOnClick = new OnClickListener() { @Override public void onClick(View v) { switch (v.getId()) { case R.id.cash_exit: exit(); break; case R.id.cash_restart: restart(); break; default: break; } } }; // 上传错误信息 Runnable upLog = new Runnable() { @Override public void run() { try { String Mobile = Build.MODEL; String maxMemory = "" + getmem_TOLAL() / 1024 + "m"; String nowMemory = "" + getmem_UNUSED(CrashDialog.this) / 1024 + "m"; String eMessage = "未获取到错误信息"; if (StorageState) { eMessage = FileTxt.ReadTxt(mFilePath).replace("'", ""); } Log.i(App.TAG, "Mobile:" + Mobile + " | maxMemory:" + maxMemory + " |nowMemory:" + nowMemory + " |eMessage:" + eMessage); /** * 可以在这调你自己的接口上传信息 */ } catch (Exception e) { e.printStackTrace(); } } }; private void exit() { FileTxt.deleteFile(mFilePath); System.exit(0); android.os.Process.killProcess(android.os.Process.myPid()); } private void restart() { Intent intent = getBaseContext().getPackageManager() .getLaunchIntentForPackage(getBaseContext().getPackageName()); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); exit(); } @Override public void onBackPressed() { super.onBackPressed(); exit(); } // 获取可用内存 public static long getmem_UNUSED(Context mContext) { long MEM_UNUSED; ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); am.getMemoryInfo(mi); MEM_UNUSED = mi.availMem / 1024; return MEM_UNUSED; } // 获取剩余内存 public static long getmem_TOLAL() { long mTotal; String path = "/proc/meminfo"; String content = null; BufferedReader br = null; try { br = new BufferedReader(new FileReader(path), 8); String line; if ((line = br.readLine()) != null) { content = line; } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } int begin = content.indexOf(':'); int end = content.indexOf('k'); content = content.substring(begin + 1, end).trim(); mTotal = Integer.parseInt(content); return mTotal; }}
完整代码:http://download.csdn.net/detail/hx7013/9710757
0 0
- Android全局异常捕获并弹窗提示
- android 捕获全局异常
- android 捕获全局异常
- Android 捕获全局异常
- android 全局异常捕获
- android捕获全局异常
- android捕获全局异常
- android 全局异常 捕获
- Android捕获全局异常
- android 捕获全局异常
- Android全局捕获异常
- android 捕获全局异常
- Android全局异常捕获
- android 全局异常捕获
- Android 全局异常捕获
- Android 全局异常捕获
- Android全局异常捕获
- Android 捕获全局异常
- 原始 java 载入数据源 JNDI及直连
- linux 网络配置
- 链接(Linking)
- 在项目中导入环信EaseUI出现.os文件找不到的问题
- 如何使用知网
- Android全局异常捕获并弹窗提示
- 限制软件试用期的简单思路
- Java生成随机长度指定密码
- 专访|从程序员到架构师:交流和分享最能让技术人进步
- iOS下载历史版本App教程
- ios开发--Objective-C 类属性
- 高速公路ETC卡签之我见4-卡签结构说明
- 使用python处理Excel
- 谈一谈CloudBlog的系统架构