android 捕捉异常并上传至服务器的简单实现
在项目中,我们的应用经常会遇到崩溃的情况,如果你的项目已经发送到了应用市场上,那么应用发生的崩溃开发人员是开不到的,所以我们要想办法将异常信息传到服务器上,便于开发人员查看并作出修改。Google考虑到这一点,也提供了Thread.UncaughtExceptionHandler接口来实现这一问题。
创建Crash异常捕获很简单,主要的步骤有:
1.创建BaseApplication继承Application并实现Thread.UncaughtExceptionHandler
2.通过Thread.setDefaultUncaughtExceptionHandler(this)设置默认的异常捕获
3.最后在manifests中注册创建的BaseApplication
一、异常捕捉的简单实用
publicclassBaseApplicationextendsApplicationimplementsThread.UncaughtExceptionHandler{
@Override
publicvoidonCreate(){
super.onCreate();
//设置异常捕获
CrashHandlercatchHandler=CrashHandler.getInstance();
catchHandler.init(this);
}
}
二、CrashHandler(主要是实现uncaughtException方法)
publicclassCrashHandlerimplementsUncaughtExceptionHandler{
publicstaticfinalStringTAG="CrashHandler";
//系统默认的UncaughtException处理类
privateThread.UncaughtExceptionHandlermDefaultHandler;
//CrashHandler实例
privatestaticCrashHandlerinstance;
//程序的Context对象
privateContextmContext;
//用来存储设备信息和异常信息
privateMapinfos=newHashMap();
//用于格式化日期,作为日志文件名的一部分
privateDateFormatformatter=newSimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
MyActivityLifecycleCallbacksmMyActivityLifecycleCallbacks=newMyActivityLifecycleCallbacks();
/**保证只有一个CrashHandler实例*/
privateCrashHandler(){
}
/**获取CrashHandler实例,单例模式*/
publicstaticCrashHandlergetInstance(){
if(instance==null)
instance=newCrashHandler();
returninstance;
}
/**
*初始化
*/
publicvoidinit(SspApplicationcontext){
mContext=context;
context.registerActivityLifecycleCallbacks(mMyActivityLifecycleCallbacks);
//获取系统默认的UncaughtException处理器
mDefaultHandler=Thread.getDefaultUncaughtExceptionHandler();
//设置该CrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}
/**
*当UncaughtException发生时会转入该函数来处理
*/
@Override
publicvoiduncaughtException(Threadthread,Throwableex){
if(!handleException(ex)&&mDefaultHandler!=null){
//如果用户没有处理则让系统默认的异常处理器来处理
mDefaultHandler.uncaughtException(thread,ex);
}else{
//try{
//Thread.sleep(3000);
//}catch(InterruptedExceptione){
//Log.e(TAG,"error:",e);
//}
//注意Thread.sleep(3000)和SystemClock.sleep(3000)的区别
SystemClock.sleep(3000);
//退出程序
Log.i("=====killProcess======","=====killProcess======");
mMyActivityLifecycleCallbacks.removeAllActivities();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
}
/**
*自定义错误处理,收集错误信息发送错误报告等操作均在此完成.
*
*@paramex
*@returntrue:如果处理了该异常信息;否则返回false.
*/
privatebooleanhandleException(Throwableex){
Log.i("=====handleException======","=====handleException======");
if(ex==null){
returnfalse;
}
//收集设备参数信息
collectDeviceInfo(mContext);
//使用Toast来显示异常信息
newThread(){
@Override
publicvoidrun(){
Looper.prepare();
Toast.makeText(mContext,"哎呀,出问题了,我要暂时离开了",Toast.LENGTH_SHORT).show();
Looper.loop();
}
}.start();
//保存日志文件
saveCatchInfo2File(ex);
returntrue;
}
/**
*收集设备参数信息
*
*@paramctx
*/
publicvoidcollectDeviceInfo(Contextctx){
try{
PackageManagerpm=ctx.getPackageManager();
PackageInfopi=pm.getPackageInfo(ctx.getPackageName(),PackageManager.GET_ACTIVITIES);
if(pi!=null){
StringversionName=pi.versionName==null?"null":pi.versionName;
StringversionCode=pi.versionCode+"";
infos.put("versionName",versionName);
infos.put("versionCode",versionCode);
}
}catch(NameNotFoundExceptione){
Log.e(TAG,"anerroroccuredwhencollectpackageinfo",e);
}
Field[]fields=Build.class.getDeclaredFields();
for(Fieldfield:fields){
try{
field.setAccessible(true);
infos.put(field.getName(),field.get(null).toString());
Log.d(TAG,field.getName()+":"+field.get(null));
}catch(Exceptione){
Log.e(TAG,"anerroroccuredwhencollectcrashinfo",e);
}
}
}
/**
*保存错误信息到文件中
*
*@paramex
*@return返回文件名称,便于将文件传送到服务器
*/
privateStringsaveCatchInfo2File(Throwableex){
StringBuffersb=newStringBuffer();
for(Map.Entryentry:infos.entrySet()){
Stringkey=entry.getKey();
Stringvalue=entry.getValue();
sb.append(key+"="+value+"\n");
}
Writerwriter=newStringWriter();
PrintWriterprintWriter=newPrintWriter(writer);
ex.printStackTrace(printWriter);
Throwablecause=ex.getCause();
while(cause!=null){
cause.printStackTrace(printWriter);
cause=cause.getCause();
}
printWriter.close();
Stringresult=writer.toString();
sb.append(result);
try{
longtimestamp=System.currentTimeMillis();
Stringtime=formatter.format(newDate());
SharedPreferencesuserInfo=mContext.getSharedPreferences(
Constants.USER_SETTING_INFOS,0);
StringloginName=userInfo.getString(Constants.USERNAME,"");
StringfileName="crash-"+time+"-"+loginName+".log";
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
Stringpath="/mnt/sdcard/crash/";
Filedir=newFile(path);
if(!dir.exists()){
dir.mkdirs();
}
FileOutputStreamfos=newFileOutputStream(path+fileName);
fos.write(sb.toString().getBytes());
//发送给开发人员
sendCrashLog2PM(path+fileName);
fos.close();
}
returnfileName;
}catch(Exceptione){
Log.e(TAG,"anerroroccuredwhilewritingfile...",e);
}
returnnull;
}
/**
*将捕获的导致崩溃的错误信息发送给开发人员
*
*目前只将log日志保存在sdcard和输出到LogCat中,并未发送给后台。
*/
privatevoidsendCrashLog2PM(finalStringfileName){
if(!newFile(fileName).exists()){
Toast.makeText(mContext,"日志文件不存在!",Toast.LENGTH_SHORT).show();
return;
}else{
newThread(newRunnable(){
@Override
publicvoidrun(){
Looper.prepare();
ArrayListpicList=newArrayList();
picList.add(fileName);
SendEventPicsFile=newSendEventPic(mContext);
UUIDGeneratorgenerator=newUUIDGenerator();
StringlinkId=generator.generate().toString();
SharedPreferencesuserInfo=mContext.getSharedPreferences(
Constants.USER_SETTING_INFOS,0);
StringloginName=userInfo.getString(Constants.USERNAME,"");
StringuserId=userInfo.getString(Constants.USER_USERID_INFOS,"");
booleanisproblempic=sFile.isSendSuccess(picList,linkId,
"crash_tng","crash_tng",loginName,userId);
Looper.loop();
}
}).start();
}
FileInputStreamfis=null;
BufferedReaderreader=null;
Strings=null;
try{
fis=newFileInputStream(fileName);
reader=newBufferedReader(newInputStreamReader(fis,"GBK"));
while(true){
s=reader.readLine();
if(s==null)
break;
//由于目前尚未确定以何种方式发送,所以先打出log日志。
Log.i("info",s.toString());
}
}catch(FileNotFoundExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}finally{//关闭流
try{
reader.close();
fis.close();
}catch(IOExceptione){
e.printStackTrace();
}
}
}
}
三、开发过程中遇到的坑
如果在activity创建的时候崩溃的话,系统有时候(目前不确定什么情况下会重启)会重启当前的activity,造成第二次的崩溃,如此循环……
所以,在应用崩溃时要完全退出应用。
publicclassMyActivityLifecycleCallbacksimplementsActivityLifecycleCallbacks{
privateListactivities=newLinkedList<>();
publicstaticintsAnimationId=0;
@Override
publicvoidonActivityCreated(Activityactivity,BundlesavedInstanceState){
addActivity(activity);
}
@Override
publicvoidonActivityStarted(Activityactivity){
}
@Override
publicvoidonActivityResumed(Activityactivity){
}
@Override
publicvoidonActivityPaused(Activityactivity){
}
@Override
publicvoidonActivityStopped(Activityactivity){
}
@Override
publicvoidonActivitySaveInstanceState(Activityactivity,BundleoutState){
}
@Override
publicvoidonActivityDestroyed(Activityactivity){
removeActivity(activity);
}
/**
*添加Activity
*/
publicvoidaddActivity(Activityactivity){
if(activities==null){
activities=newLinkedList<>();
}
if(!activities.contains(activity)){
activities.add(activity);//把当前Activity添加到集合中
}
}
/**
*移除Activity
*/
publicvoidremoveActivity(Activityactivity){
if(activities.contains(activity)){
activities.remove(activity);
}
if(activities.size()==0){
activities=null;
}
}
/**
*销毁所有activity
*/
publicvoidremoveAllActivities(){
for(Activityactivity:activities){
if(null!=activity){
activity.finish();
activity.overridePendingTransition(0,sAnimationId);
}
}
}
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。