笔记92--将崩溃信息保存到本地(上)

来源:互联网 发布:挖掘社交网络 pdf 编辑:程序博客网 时间:2024/05/17 03:18

from:http://blog.csdn.net/way_ping_li/article/details/7927273

一、问题

你他娘的写一个无bug的程序,但是出了bug,程序崩溃,猿猿你是不想混了吗?

二、怎么解决

需要一个全局的异常捕获器,当出现一个我们未处理的bug时,捕获它,记录它,然后想干嘛干嘛。

三、实现这个机制

需要先了解两个类:

1、Application:用来管理应用程序的全局状态。在应用程序启动时Application会首先创建,然后才根据情况(Intent)来启动相应的Activity和Service。

2、Thread.UncaughtExceptionHandler:线程未捕获异常处理器,用来处理异常。如果程序出现了未捕获异常,默认会弹出系统中强制关闭对话框。我们需要实现此接口,并注册为程序中默认未捕获异常处理。这样当未捕获异常发生时,就可做一些个性化的异常处理操作。

抽吸剥茧分析分析:

第一层:先来实现基本的捕获未处理异常

public class CrashHandler implements UncaughtExceptionHandler {<span style="white-space:pre"></span>//当UncaughtException发生时会转入该重写的方法来处理@Overridepublic void uncaughtException(Thread thread, Throwable ex) {}  }
写了记得用。这只是实现了自己的UncaughtException处理器,想用还得告诉系统:

Thread.setDefaultUncaughtExceptionHandler(this);// 设置该CrashHandler为程序的默认处理器   
第二层:异常的处理上花点心思。需求描述:自定义的异常处理器未处理,则继续让系统默认的异常处理器来处理;如果处理了,则让程序运行3秒,保证文件保存并上传到服务器。

@Overridepublic void uncaughtException(Thread thread, Throwable ex) {  if (!handleException(ex) && mDefaultHandler != null) {  // 如果自定义的没有处理则让系统默认的异常处理器来处理   mDefaultHandler.uncaughtException(thread, ex);  } else {  try {  Thread.sleep(3000);// 如果处理了,让程序继续运行3秒再退出,保证文件保存并上传到服务器   } catch (InterruptedException e) {  e.printStackTrace();  }  // 退出程序   android.os.Process.killProcess(android.os.Process.myPid());  System.exit(1);  }  } 
获取系统默认的异常处理器:

mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();// 获取系统默认的UncaughtException处理器   
/**  * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.  *   * @param ex  *            异常信息  * @return true 如果处理了该异常信息;否则返回false.  */  public boolean handleException(Throwable ex) {  if (ex == null)  return false;  new Thread() {  public void run() {  Looper.prepare();  Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出", 0).show();  Looper.loop();  }  }.start();  // 收集设备参数信息   collectDeviceInfo(mContext);  // 保存日志文件   saveCrashInfo2File(ex);  return true;  }

四、贴出所有代码:
1、自定义未捕获异常处理器
package com.example.myerrorapplication;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.PrintWriter;import java.io.StringWriter;import java.io.Writer;import java.lang.Thread.UncaughtExceptionHandler;import java.lang.reflect.Field;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.Map;import android.content.Context;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.util.Log;import android.widget.Toast;public class CrashHandler implements UncaughtExceptionHandler {private static final String TAG = "CrashHandler";  private Thread.UncaughtExceptionHandler mDefaultHandler;// 系统默认的UncaughtException处理类   private static CrashHandler INSTANCE = new CrashHandler();// CrashHandler实例   private Context mContext;// 程序的Context对象   private Map<String, String> info = new HashMap<String, String>();// 用来存储设备信息和异常信息   private SimpleDateFormat format = new SimpleDateFormat(  "yyyy-MM-dd-HH-mm-ss");// 用于格式化日期,作为日志文件名的一部分   /** 保证只有一个CrashHandler实例 */  private CrashHandler() {  }  /** 获取CrashHandler实例 ,单例模式 */  public static CrashHandler getInstance() {  return INSTANCE;  }  /**  * 初始化  *   * @param context  */  public void init(Context context) {  mContext = context;  mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();// 获取系统默认的UncaughtException处理器   Thread.setDefaultUncaughtExceptionHandler(this);// 设置该CrashHandler为程序的默认处理器   }  /**  * 当UncaughtException发生时会转入该重写的方法来处理  */  @Overridepublic void uncaughtException(Thread thread, Throwable ex) {  if (!handleException(ex) && mDefaultHandler != null) {  // 如果自定义的没有处理则让系统默认的异常处理器来处理   mDefaultHandler.uncaughtException(thread, ex);  } else {  try {  Thread.sleep(3000);// 如果处理了,让程序继续运行3秒再退出,保证文件保存并上传到服务器   } catch (InterruptedException e) {  e.printStackTrace();  }  // 退出程序   android.os.Process.killProcess(android.os.Process.myPid());  System.exit(1);  }  }  /**  * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.  *   * @param ex  *            异常信息  * @return true 如果处理了该异常信息;否则返回false.  */  public boolean handleException(Throwable ex) {  if (ex == null)  return false;  new Thread() {  public void run() {  Looper.prepare();  Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出", 0).show();  Looper.loop();  }  }.start();  // 收集设备参数信息   collectDeviceInfo(mContext);  // 保存日志文件   saveCrashInfo2File(ex);  return true;  }  /**  * 收集设备参数信息  *   * @param context  */  public void collectDeviceInfo(Context context) {  try {  PackageManager pm = context.getPackageManager();// 获得包管理器   PackageInfo pi = pm.getPackageInfo(context.getPackageName(),  PackageManager.GET_ACTIVITIES);// 得到该应用的信息,即主Activity   if (pi != null) {  String versionName = pi.versionName == null ? "null"  : pi.versionName;  String versionCode = pi.versionCode + "";  info.put("versionName", versionName);  info.put("versionCode", versionCode);  }  } catch (NameNotFoundException e) {  e.printStackTrace();  }  Field[] fields = Build.class.getDeclaredFields();// 反射机制   for (Field field : fields) {  try {  field.setAccessible(true);  info.put(field.getName(), field.get("").toString());  Log.d(TAG, field.getName() + ":" + field.get(""));  } catch (IllegalArgumentException e) {  e.printStackTrace();  } catch (IllegalAccessException e) {  e.printStackTrace();  }  }  }  private String saveCrashInfo2File(Throwable ex) {  StringBuffer sb = new StringBuffer();  for (Map.Entry<String, String> entry : info.entrySet()) {  String key = entry.getKey();  String value = entry.getValue();  sb.append(key + "=" + value + "\r\n");  }  Writer writer = new StringWriter();  PrintWriter pw = new PrintWriter(writer);  ex.printStackTrace(pw);  Throwable cause = ex.getCause();  // 循环着把所有的异常信息写入writer中   while (cause != null) {  cause.printStackTrace(pw);  cause = cause.getCause();  }  pw.close();// 记得关闭   String result = writer.toString();  sb.append(result);  // 保存文件   long timetamp = System.currentTimeMillis();  String time = format.format(new Date());  String fileName = "crash-" + time + "-" + timetamp + ".log";  if (Environment.getExternalStorageState().equals(  Environment.MEDIA_MOUNTED)) {  try {  File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +                           File.separator + "crash");  Log.i("CrashHandler", dir.toString());  if (!dir.exists())  dir.mkdir();  FileOutputStream fos = new FileOutputStream(new File(dir,  fileName));  fos.write(sb.toString().getBytes());  fos.close();  return fileName;  } catch (FileNotFoundException e) {  e.printStackTrace();  } catch (IOException e) {  e.printStackTrace();  }  }  return null;  }  }
2、告诉系统用自定义的在应用未捕获异常处理器
public class CrashApplication extends Application {@Overridepublic void onCreate() {super.onCreate();CrashHandler crashHandler=CrashHandler.getInstance();crashHandler.init(this);}}
3、告诉系统用自定义的Application
<application android:name=".CrashApplication"         android:icon="@drawable/ic_launcher"         android:label="@string/app_name"         android:theme="@style/AppTheme" >    ...  </application>  
4、清单文件中加权限
因用到了保存文件到SD卡
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>


0 0