zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

基于Android错误信息捕获发送至服务器的详解

Android服务器 详解 基于 捕获 错误信息
2023-06-13 09:15:01 时间
程序员最头疼的事情就是bug和debug。这次debug长达20天,搞的我心力交瘁。累,因为Android兼容性,不同手机会有不同的bug出来,而且很难复现,所以就上网找了下类似保存错误log到文件再上传到服务器,现把源码也共享出来。上传至服务器的代码我没加。相信大家都有现成的代码了。

先讲下原理,跟JavaEE的自定义异常捕获一样,将错误一直向上抛,然后在最上层统一处理。这里就可以获得ExceptionMessage,进行保存操作
异常捕获类如下:
复制代码代码如下:

/**
 *@authorStay
 *     在Application中统一捕获异常,保存到文件中下次再打开时上传
 */
publicclassCrashHandlerimplementsUncaughtExceptionHandler{  
   /**是否开启日志输出,在Debug状态下开启, 
    *在Release状态下关闭以提示程序性能 
    **/ 
   publicstaticfinalbooleanDEBUG=true;  
   /**系统默认的UncaughtException处理类*/ 
   privateThread.UncaughtExceptionHandlermDefaultHandler;  
   /**CrashHandler实例*/ 
   privatestaticCrashHandlerINSTANCE;  
   /**程序的Context对象*/ 
//   privateContextmContext;  
   /**保证只有一个CrashHandler实例*/ 
   privateCrashHandler(){}  
   /**获取CrashHandler实例,单例模式*/ 
   publicstaticCrashHandlergetInstance(){  
       if(INSTANCE==null){  
           INSTANCE=newCrashHandler();  
       }  
       returnINSTANCE;  
   }  

   /** 
    *初始化,注册Context对象, 
    *获取系统默认的UncaughtException处理器, 
    *设置该CrashHandler为程序的默认处理器 
    *  
    *@paramctx 
    */ 
   publicvoidinit(Contextctx){  
//       mContext=ctx;  
       mDefaultHandler=Thread.getDefaultUncaughtExceptionHandler();  
       Thread.setDefaultUncaughtExceptionHandler(this);  
   }  

   /** 
    *当UncaughtException发生时会转入该函数来处理 
    */ 
   @Override 
   publicvoiduncaughtException(Threadthread,Throwableex){  
       if(!handleException(ex)&&mDefaultHandler!=null){  
           //如果用户没有处理则让系统默认的异常处理器来处理  
           mDefaultHandler.uncaughtException(thread,ex);  
       }else{ //如果自己处理了异常,则不会弹出错误对话框,则需要手动退出app
           try{  
               Thread.sleep(3000);  
           }catch(InterruptedExceptione){  
           }  
           android.os.Process.killProcess(android.os.Process.myPid());  
           System.exit(10);  
       }  
   }  

   /** 
    *自定义错误处理,收集错误信息 
    *发送错误报告等操作均在此完成. 
    *开发者可以根据自己的情况来自定义异常处理逻辑 
    *@return 
    *true代表处理该异常,不再向上抛异常,
    *false代表不处理该异常(可以将该log信息存储起来)然后交给上层(这里就到了系统的异常处理)去处理,
    *简单来说就是true不会弹出那个错误提示框,false就会弹出
    */ 
   privatebooleanhandleException(finalThrowableex){  
       if(ex==null){  
           returnfalse;  
       }  
//       finalStringmsg=ex.getLocalizedMessage();  
       finalStackTraceElement[]stack=ex.getStackTrace();
       finalStringmessage=ex.getMessage();
       //使用Toast来显示异常信息  
       newThread(){  
           @Override 
           publicvoidrun(){  
               Looper.prepare();  
//               Toast.makeText(mContext,"程序出错啦:"+message,Toast.LENGTH_LONG).show();  
//               可以只创建一个文件,以后全部往里面append然后发送,这样就会有重复的信息,个人不推荐
               StringfileName=""+System.currentTimeMillis() +".log";  
               Filefile=newFile(Environment.getExternalStorageDirectory(),fileName);
               try{
                   FileOutputStreamfos=newFileOutputStream(file,true);
                   fos.write(message.getBytes());
                   for(inti=0;i<stack.length;i++){
                       fos.write(stack[i].toString().getBytes());
                   }
                   fos.flush();
                   fos.close();
               }catch(Exceptione){
               }
               Looper.loop();  
           }  

       }.start();  
       returnfalse;  
   }  

   //TODO使用HTTPPost发送错误报告到服务器 这里不再赘述
//   privatevoidpostReport(Filefile){  
//     在上传的时候还可以将该app的version,该手机的机型等信息一并发送的服务器,
//     Android的兼容性众所周知,所以可能错误不是每个手机都会报错,还是有针对性的去debug比较好
//   }  
}

在ApplicationonCreate时就注册ExceptionHandler,此后只要程序在抛异常后就能捕获到。
复制代码代码如下:

publicclassAppextendsApplication{
       @Override 
       publicvoidonCreate(){  
           super.onCreate();  
           CrashHandlercrashHandler=CrashHandler.getInstance();  
           //注册crashHandler  
           crashHandler.init(getApplicationContext());  
       }  
}
?publicclassLogActivityextendsActivity{
   @Override
   publicvoidonCreate(BundlesavedInstanceState){
       super.onCreate(savedInstanceState);
       setContentView(R.layout.main);
       try{//制造bug
           Filefile=newFile(Environment.getExternalStorageState(),"crash.bin");
           FileInputStreamfis=newFileInputStream(file);
           byte[]buffer=newbyte[1024];
           fis.read(buffer);
       }catch(Exceptione){
           //这里不能再向上抛异常,如果想要将log信息保存起来,则抛出runtime异常,
//         让自定义的handler来捕获,统一将文件保存起来上传
           thrownewRuntimeException(e);
       }
   }
}

注意,如果catch后不throw就默认是自己处理了,ExceptionHandler不会捕获异常了。
再分享一个Log的封装类,只要在这里设置DEBUG的值就能让控制台是否打印出log
复制代码代码如下:
publicclassDebugUtil{
   publicstaticfinalStringTAG="ICON";
   publicstaticfinalbooleanDEBUG=true;

   publicstaticvoidtoast(Contextcontext,Stringcontent){
       Toast.makeText(context,content,Toast.LENGTH_SHORT).show();
   }

   publicstaticvoiddebug(Stringtag,Stringmsg){
       if(DEBUG){
           Log.d(tag,msg);
       }
   }

   publicstaticvoiddebug(Stringmsg){
       if(DEBUG){
           Log.d(TAG,msg);
       }
   }

   publicstaticvoiderror(Stringtag,Stringerror){
       Log.e(tag,error);
   }

   publicstaticvoiderror(Stringerror){
       Log.e(TAG,error);
   }
}