將安卓app閃退信息保存在本地或服務器的工具類CrashHandler

huaakeji7 8年前發布 | 8K 次閱讀 安卓開發 Android開發 移動開發

在我剛剛實習的時候,有一個測試曾經問我,能不能把安卓app閃退時候的錯誤信息保存在本地文件夾或者上傳到服務器。他說這樣做可以把客戶反映的閃退問題、而且我們公司不能及時重現BUG的時候去使用。當時,我的回答是否定的,因為我當時并不知道原來安卓早就有UncaughtExceptionHandler可以去全局捕獲異常,直到我看了Android開發藝術探索這本書。其實寫這篇文章不是為了去展示什么,因為書上、網上都有一模一樣的繼承UncaughtExceptionHandler寫一個工具類。寫這篇文章我只是想糾正當年的錯誤并且可以作為自己項目的工具類去使用,畢竟身為程序猿都不喜歡跟“萬惡”的客戶和“死神”般的測試有過多的沖突摩擦。當他們發現app有閃退問題的時候,你只需要叫他們把app“臨終”前保存在本地的文件打開,這里有幾個好處:

1.可以輕松的擺脫“萬惡”的客戶和“死神”般的測試的糾纏,有時候客戶和測試不在身邊,遠距離就可以找到問題所在;
2.你就可以省去很多功夫去重現BUG,可以留下多點時間跟測試的妹子聊天去;
3.可以把國內各大手機商改過的安卓系統無緣無故會在某些型號的手機上出現閃退情況,
捕獲記錄下來,做好適配工作。

如果有很多app的內容要跟測試妹子深入了解的話,你可以把這篇文章省略掉~

好吧,言歸正傳,UncaughtExceptionHandler是全局捕獲異常的,為app意外中止的提供一些處理的方法。用的時候我們只需要實現UncaughtExceptionHandler這個接口,重寫一些方法,就可以做到把異常信息保存在本地或者上傳到服務器。

代碼如下:

public class CrashHandler implements Thread.UncaughtExceptionHandler {    
   private static final String TAG = "CrashHandler";    
   private static final boolean DEBUG = true;   
   private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/CrashTest/log/"; 
   private static final String FILE_NAME = "crash";  
   private static final String FILE_NAME_SUFFIX = ".txt";  
   private static CrashHandler sInstance = new CrashHandler();    
   private Thread.UncaughtExceptionHandler mDefaultCrashHandler; 
   private Context mContext;  
   private CrashHandler(){ 
   }  
   public static CrashHandler getsInstance(){   
       return sInstance; 
   } 
   public void init(Context context){  
      mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();    
      Thread.setDefaultUncaughtExceptionHandler(this);   
      mContext = context.getApplicationContext(); 
   }  
  /**  
   * 這個是最關鍵的函數,當程序中有未被捕獲的異常,系統將會自動調用uncaughtException方法  
   * thread為出現未捕獲異常的線程,ex為未捕獲的異常,有了這個ex,我們就可以得到異常信息   
   * @param thread  
   * @param ex  
   */  
   @Override 
   public void uncaughtException(Thread thread, Throwable ex) { 
       try {     
               //導出異常信息到SD卡中  
              dumpExceptionToSDCard(ex);  
             //這里可以上傳異常信息到服務器,便于開發人員分析日志從而解決bug    
             uploadExceptionToServer(); 
           }catch (IOException e){ 
               e.printStackTrace(); 
           }   
          ex.printStackTrace();  
        //如果系統提供默認的異常處理器,則交給系統去結束程序,否則就由自己結束自己
        if(mDefaultCrashHandler != null){  
          mDefaultCrashHandler.uncaughtException(thread, ex);      
        }else { 
           //自己處理  
           try {
               //延遲2秒殺進程   
               Thread.sleep(2000);              
               Toast.makeText(mContext, "程序出錯了~", Toast.LENGTH_SHORT).show();   
              } catch (InterruptedException e) {   
             Log.e(TAG, "error : ", e);   
         }    
        android.os.Process.killProcess(Process.myPid()); 
       }  
  }  
  private void dumpExceptionToSDCard(Throwable ex) throws IOException{  
      //如果SD卡不存在或無法使用,則無法把異常信息寫入SD卡        
      if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){  
          if(DEBUG){  
              Log.e(TAG, "sdcard unmounted,skip dump exception");                
              return;  
          }  
      }  
      File dir = new File(PATH);  
      if(!dir.exists()){   
         dir.mkdirs();  
      }  
      long current = System.currentTimeMillis();  
      String time = new SimpleDateFormat("yyyy-MM-dd HH:MM:SS").format(new Date(current));  
      File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);  
      try {   
          PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));  
          pw.println(time);  
          dumpPhoneInfo(pw);  
          pw.println();  
          ex.printStackTrace(pw);  
          pw.close();  
          Log.e(TAG, "dump crash info seccess");  
      }catch (Exception e){  
          Log.e(TAG,e.getMessage());  
          Log.e(TAG,"dump crash info failed");  
      }  
  }  
  private void dumpPhoneInfo(PrintWriter pw)throws PackageManager.NameNotFoundException{  
      PackageManager pm = mContext.getPackageManager();  
      PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(),                PackageManager.GET_ACTIVITIES);  
      pw.print("App Version: ");  
      pw.print(pi.versionName);  
      pw.print('_');  
      pw.println(pi.versionCode);  
      //Android版本號  
      pw.print("OS Version: ");  
      pw.print(Build.VERSION.RELEASE);  
      pw.print("_");  
      pw.print(Build.VERSION.SDK_INT);  
      //手機制造商  
      pw.print("Vendor: ");  
      pw.print(Build.MANUFACTURER);  
      //手機型號  
      pw.print("Model: ");  
      pw.println(Build.MODEL);  
      //CPU架構  
      pw.print("CPU ABI: ");  
      pw.println(Build.CPU_ABI);  
  }  
  private void uploadExceptionToServer(){  
      //將異常信息發送到服務器  
  }
}

就是這么簡單一個工具類,是不是很簡單?好吧下面我們來看看怎么使用~

CrashHandler的使用.png

只需要在application獲取CrashHandler的單例和初始化就可以做到全局捕獲異常了,跟一些第三方的框架初始化沒什么區別吧~

最后看一下效果圖:

自己拋出一個異常做測試.png

點擊這個按鈕,我們就看到app閃退了,然后我們去該保存目錄下看看有什么~

測試結果.png

我們可以看到我們還可以把安卓版本、手機型號、運營商輸出來,考慮到國內各大手機商改過的安卓系統無緣無故會在某些型號的手機上出現閃退情況,捕獲到這些異常還是很有必要的。

好吧,其實以上的代碼很大部分都是來源于Android開發藝術探索這本書的,有興趣的同學也可以去看一下,我只是想告訴更多還不知道的同學,UncaughtExceptionHandler可以把你的閃退信息保存到本地或者上傳到服務器上!

 

來自:http://www.jianshu.com/p/3f73cfbb6743

 

 本文由用戶 huaakeji7 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!