Android 全局异常错误或崩溃捕捉

当出现崩溃,软件不会闪退,会出现弹出一个对话框,异常错误信息会自动保存在sd卡crash这个文件夹下。后续需要还可以发送到服务器的。看效果图。

1、实现效果图

2、全局异常捕捉类CrashHandler

package com.crashhandler.util;import java.io.File;import java.io.FileOutputStream;import java.lang.Thread.UncaughtExceptionHandler;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.Date;import com.crashhandler.activity.R;import android.annotation.SuppressLint;import android.app.AlertDialog;import android.content.Context;import android.content.DialogInterface;import android.content.Intent;import android.content.pm.PackageInfo;import android.content.pm.PackageManager.NameNotFoundException;import android.net.Uri;import android.os.Environment;import android.os.Looper;import android.util.Log;import android.view.WindowManager;/** * UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告. * */public class CrashHandler implements UncaughtExceptionHandler {// 系统默认的UncaughtException处理类private Thread.UncaughtExceptionHandler mDefaultHandler;// CrashHandler实例private static CrashHandler INSTANCE;// 程序的Context对象private Context mContext;//保证只有一个CrashHandler实例 private CrashHandler() {}//获取CrashHandler实例 ,单例模式public static CrashHandler getInstance() {if (INSTANCE == null)INSTANCE = new CrashHandler();return INSTANCE;}/** * 初始化 * * @param context */public void init(Context context) {mContext = context;// 获取系统默认的UncaughtException处理器mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();// 设置该CrashHandler为程序的默认处理器Thread.setDefaultUncaughtExceptionHandler(this);}/** * 当UncaughtException发生时会转入该重写的方法来处理 */public void uncaughtException(Thread thread, Throwable ex) {if (!handleException(ex) && mDefaultHandler != null) {// 如果自定义的没有处理则让系统默认的异常处理器来处理mDefaultHandler.uncaughtException(thread, ex);}}/** * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. * * @param ex *异常信息 * @return true 如果处理了该异常信息;否则返回false. */public boolean handleException(Throwable ex) {if (ex == null || mContext == null)return false;final String crashReport = getCrashReport(mContext, ex);new Thread() {public void run() {Looper.prepare();File file = save2File(crashReport);sendAppCrashReport(mContext, crashReport, file);Looper.loop();}}.start();return true;}@SuppressLint("SimpleDateFormat")private File save2File(String crashReport) {//用于格式化日期,作为日志文件名的一部分DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");String time = dateFormat.format(new Date());String fileName = "crash-" + time + "-" + System.currentTimeMillis() + ".txt";if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {try {//存储路径,是sd卡的crash文件夹File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "crash");if (!dir.exists())dir.mkdir();File file = new File(dir, fileName);FileOutputStream fos = new FileOutputStream(file);fos.write(crashReport.toString().getBytes());fos.close();return file;} catch (Exception e) {//sd卡存储,记得加上权限,,不然这里会抛出异常Log.i("Show","save2File error:" + e.getMessage());}}return null;}private void sendAppCrashReport(final Context context,final String crashReport, final File file) {AlertDialog.Builder builder = new AlertDialog.Builder(context).setIcon(android.R.drawable.ic_dialog_info).setTitle(R.string.app_error).setMessage(R.string.app_error_message).setPositiveButton(R.string.submit_report,new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {try {//这以下的内容,只做参考,因为没有服务器Intent intent = new Intent(Intent.ACTION_SEND);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);String[] tos = { "way.ping.li@gmail.com" };intent.putExtra(Intent.EXTRA_EMAIL, tos);intent.putExtra(Intent.EXTRA_SUBJECT,"Android客户端 – 错误报告");if (file != null) {intent.putExtra(Intent.EXTRA_STREAM,Uri.fromFile(file));intent.putExtra(Intent.EXTRA_TEXT,"请将此错误报告发送给我,以便我尽快修复此问题,谢谢合作!\n");} else {intent.putExtra(Intent.EXTRA_TEXT,"请将此错误报告发送给我,以便我尽快修复此问题,谢谢合作!\n"+ crashReport);}intent.setType("text/plain");intent.setType("message/rfc882");Intent.createChooser(intent, "Choose Email Client");context.startActivity(intent);} catch (Exception e) {Log.i("Show","error:" + e.getMessage());} finally {dialog.dismiss();// 退出android.os.Process.killProcess(android.os.Process.myPid());System.exit(1);}}}).setNegativeButton(android.R.string.cancel,new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {dialog.dismiss();// 退出android.os.Process.killProcess(android.os.Process.myPid());System.exit(1);}});AlertDialog dialog = builder.create();//需要的窗口句柄方式,没有这句会报错的dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);dialog.show();}/** * 获取APP崩溃异常报告 * * @param ex * @return */private String getCrashReport(Context context, Throwable ex) {PackageInfo pinfo = getPackageInfo(context);StringBuffer exceptionStr = new StringBuffer();exceptionStr.append("Version: " + pinfo.versionName + "("+ pinfo.versionCode + ")\n");exceptionStr.append("Android: " + android.os.Build.VERSION.RELEASE+ "(" + android.os.Build.MODEL + ")\n");exceptionStr.append("Exception: " + ex.getMessage() + "\n");StackTraceElement[] elements = ex.getStackTrace();for (int i = 0; i < elements.length; i++) {exceptionStr.append(elements[i].toString() + "\n");}return exceptionStr.toString();}/** * 获取App安装包信息 * * @return */private PackageInfo getPackageInfo(Context context) {PackageInfo info = null;try {info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);} catch (NameNotFoundException e) {e.printStackTrace(System.err);}if (info == null)info = new PackageInfo();return info;}}

3、系统级MyApplication继承Application。因为需要整个软件的全局,所以需要在这里初始化CrashHandler在爱情里,有时候简单的一句话,能胜过千言万语。

Android 全局异常错误或崩溃捕捉

相关文章:

你感兴趣的文章:

标签云: