在我们通常开发的应用程序中,不可避免的会出现crash现象,特别是当应用程序已经上线之后,这些crash异常信息我们通常是很难捕捉到的,如果我们不能对这些异常信息做及时的收集并且修复的话,势必会带来用户体验度的下降,为此,Android的Thread类中为我们提供了setDefaultUncaughtExceptionHandler方法,这个方法会为我们设置默认的异常处理器,当然这个默认的异常处理器是我们自己实现UncaughtExceptionHandler接口来实现的,这个接口里面存在一个uncaughtExceptionHandler方法,我们可以在这个方法里面进行一些写日志的操作,同时呢,将我们的异常信息发送给我们的服务端,服务端收到这些异常信息之后就可以进行BUG的修复工作了,下面是我实现的一个捕获全局异常的实例:
实现步骤是:
(1):创建一个实现了UncaughtExceptionHandler接口的类,并且实现它里面的uncaughtExceptionHandler方法;
(2):通过Thread的setDefaultUncaughtExceptionHandler方法将我们创建的UncaughtExceptionHandler对象设置为默认的异常处理器,这样的话,当异常发生的时候就会回调它里面的uncaughtExceptionHandler方法了,我们可以在uncaughtExceptionHandler方法里面进行异常信息上传服务器或者异常信息写入SD的操作;
(3):因为我们的异常是发生在整个应用程序中的,所以我们应该保证的就是我们的setDefaultUncaughtExceptionHandler的调用以及UncaughtExceptionHandler对象的创建是在应用初始化的时候完成的,因此我们需要实现自己的Application对象,具体来说就是继承Application接口,在onCreate方法里面进行这些操作就可以了;
实现我们自己的UncaughtExceptionHandler----->CrashHandler
public class CrashHandler implements UncaughtExceptionHandler{
//存储异常信息文件的路径
private static String FILE_PATH = "";// = Environment.getExternalStorageDirectory().getPath()+"/CrashFile/";
//存储异常信息文件名
private static final String FILE_NAME = "crash";
//存储异常信息文件的后缀名
private static final String FILE_SUFFIX_NAME = ".txt";
//当前的应用上下文
private Context mContext;
//创建一个单例的CrashHandler对象
private static CrashHandler instance = new CrashHandler();
private UncaughtExceptionHandler systemDefaultHandler;
private CrashHandler(){}
public static CrashHandler getInstance()
{
return instance;
}
public void init(Context context)
{
FILE_PATH = getFilePath(context);
systemDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();//首先获得系统已经默认的异常处理器
Thread.setDefaultUncaughtExceptionHandler(this);//设置当前UncaughtExceptionHandler对象为异常处理器
mContext = context.getApplicationContext();
}
/**
* 获取到存储错误日志信息文件的存储路径
* @param context
* @return
*/
public String getFilePath(Context context)
{
String filePath = "";
filePath = context.getCacheDir().getPath();
return filePath+"/CrashFile/";
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
//到处错误信息到本地SD卡文件
writeExceptionToSDCard(throwable);
if(systemDefaultHandler != null)
{
//由系统默认的异常处理器来进行处理
systemDefaultHandler.uncaughtException(thread, throwable);
}else
{
//由当前进程自己结束掉自己
Process.killProcess(Process.myPid());
}
}
/**
* 将错误信息写入到SD卡上面
* @param throwable
*/
public void writeExceptionToSDCard(Throwable throwable)
{
if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
//表示SD不存在,则直接退出就可以了
return;
}
//如果SD卡存在的话,则我们需要将异常信息写入到SD卡就可以了
File dir = new File(FILE_PATH);
if(!dir.exists())
{
//目录不存在,则创建目录
dir.mkdir();
}
//创建出来一个异常信息文件名出来
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String nowStringTime = format.format(new Date());
String realFilePath = FILE_PATH+FILE_NAME+"_"+nowStringTime+"_"+FILE_SUFFIX_NAME;//真正的异常信息文件的名字
File file = new File(realFilePath);
//将错误信息写到日志文件中
try {
PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(file)));
//将当前时间写到日志文件中
writer.println(nowStringTime);
//将手机型号信息写到日志文件中
writePhoneInfoToSDCard(writer);
writer.println();
throwable.printStackTrace(writer);
writer.close();//关闭资源
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 将手机型号信息写到SD卡上面
* @param writer
* @throws NameNotFoundException
*/
public void writePhoneInfoToSDCard(PrintWriter writer) throws NameNotFoundException
{
PackageManager manager = mContext.getPackageManager();
PackageInfo info = manager.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
//App版本号
writer.print("App_Version: ");
writer.print(info.versionName+"_");
writer.println(info.versionCode);
//Android版本号
writer.print("Android Version: ");
writer.print(Build.VERSION.RELEASE+"_");
writer.println(Build.VERSION.SDK_INT);
//手机制造商
writer.print("Manufacturer: ");
writer.println(Build.MANUFACTURER);
//手机型号
writer.print("Model: ");
writer.println(Build.MODEL);
//CPU架构
writer.print("CPU ABI: ");
writer.println(Build.CPU_ABI);
}
}
创建我们自己的Application----->MyApplication
public class MyApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
CrashHandler instance = CrashHandler.getInstance();
//这里的init方法的作用就是为应用程序设置异常处理,然后应用程序才会获取到未处理的异常
instance.init(this);
}
}
测试Activity----->MainActivity
public class MainActivity extends Activity {
public Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button) findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
/***************(1)
int result = 1/0;
****************/
/***************(2)
try {
int result = 1/0;
} catch (Exception e) {
}
****************/
/***************(3)
new Thread(new Runnable() {
@Override
public void run() {
int result = 1/0;
}
}).start();
****************/
}
});
}
}
如果我们打开(1)处的注释,从DDMS中查看异常输出日志文件信息,可以发现如下内容:
可以看到,他记录了我们除以0所带来的异常信息,但是如果我们注释掉代码(1),打开注释(2)的话,会发现不会产生错误日志文件,原因就在于,我们已经捕获了异常,只不过没有对异常进行处理而已,那么我们的UncaughtExceptionHandler就不再会捕获这个异常了;
如果我们打开注释(3)的代码,你会发现同样也会创建一个错误日志文件出来,这说明在子线程中出现的异常信息UncaughtExceptionHandler也照样可以捕获到的;