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

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发生时会转入该重写的方法来处理
	@Override
	public void uncaughtException(Thread thread, Throwable ex) {
			
	}  
}
写了记得用。这只是实现了自己的UncaughtException处理器,想用还得告诉系统:

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

@Override
public 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发生时会转入该重写的方法来处理 
	 */  
	@Override
	public 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 {

	@Override
	public 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"/>


你可能感兴趣的:(笔记92--将崩溃信息保存到本地(上))