Android 全局異常錯誤或崩潰捕捉

jopen 9年前發布 | 23K 次閱讀 Android Android開發 移動開發

當出現崩潰,軟件不會閃退,會出現彈出一個對話框,異常錯誤信息會自動保存在sd卡crash這個文件夾下。后續需要還可以發送到服務器的。看效果圖。

1、實現效果圖

2、全局異常捕捉類CrashHandler


package com.crashhandler.util;

import java.io.File; import java.io.FileOutputStream; import java.lang.Thread.UncaughtExceptionHandler; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date;

import com.crashhandler.activity.R; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.net.Uri; import android.os.Environment; import android.os.Looper; import android.util.Log; import android.view.WindowManager;

/**

  • UncaughtException處理類,當程序發生Uncaught異常的時候,由該類來接管程序,并記錄發送錯誤報告.
  • */ public class CrashHandler implements UncaughtExceptionHandler { // 系統默認的UncaughtException處理類 private Thread.UncaughtExceptionHandler mDefaultHandler; // CrashHandler實例 private static CrashHandler INSTANCE; // 程序的Context對象 private Context mContext;

    //保證只有一個CrashHandler實例 private CrashHandler() {

    }

    //獲取CrashHandler實例 ,單例模式 public static CrashHandler getInstance() {

     if (INSTANCE == null)
         INSTANCE = new CrashHandler();
     return INSTANCE;
    

    }

    /**

    • 初始化
    • @param context */ public void init(Context context) { mContext = context; // 獲取系統默認的UncaughtException處理器 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); // 設置該CrashHandler為程序的默認處理器 Thread.setDefaultUncaughtExceptionHandler(this); }

      /**

    • 當UncaughtException發生時會轉入該重寫的方法來處理 */ public void uncaughtException(Thread thread, Throwable ex) { if (!handleException(ex) && mDefaultHandler != null) {

       // 如果自定義的沒有處理則讓系統默認的異常處理器來處理
       mDefaultHandler.uncaughtException(thread, ex);
      

      } }

      /**

    • 自定義錯誤處理,收集錯誤信息 發送錯誤報告等操作均在此完成.
    • @param ex
    • 異常信息
    • @return true 如果處理了該異常信息;否則返回false. */ public boolean handleException(Throwable ex) { if (ex == null || mContext == null)

       return false;
      

      final String crashReport = getCrashReport(mContext, ex); new Thread() {

       public void run() {
           Looper.prepare();
           File file = save2File(crashReport);
           sendAppCrashReport(mContext, crashReport, file);
           Looper.loop();
       }
      
      

      }.start(); return true; }

      @SuppressLint("SimpleDateFormat") private File save2File(String crashReport) { //用于格式化日期,作為日志文件名的一部分 DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); String time = dateFormat.format(new Date()); String fileName = "crash-" + time + "-" + System.currentTimeMillis() + ".txt"; if (Environment.getExternalStorageState().equals(

           Environment.MEDIA_MOUNTED)) {
       try {
           //存儲路徑,是sd卡的crash文件夾
           File dir = new File(Environment.getExternalStorageDirectory()
                   .getAbsolutePath() + File.separator + "crash");
           if (!dir.exists())
               dir.mkdir();
           File file = new File(dir, fileName);
           FileOutputStream fos = new FileOutputStream(file);
           fos.write(crashReport.toString().getBytes());
           fos.close();
           return file;
       } catch (Exception e) {
           //sd卡存儲,記得加上權限,不然這里會拋出異常
           Log.i("Show","save2File error:" + e.getMessage());
       }
      

      } return null; }

      private void sendAppCrashReport(final Context context,

       final String crashReport, final File file) {
      

      AlertDialog.Builder builder = new AlertDialog.Builder(context) .setIcon(android.R.drawable.ic_dialog_info) .setTitle(R.string.app_error) .setMessage(R.string.app_error_message) .setPositiveButton(R.string.submit_report,

           new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int which) {
                   try {
      
                       //這以下的內容,只做參考,因為沒有服務器
                       Intent intent = new Intent(Intent.ACTION_SEND);
                       intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                       String[] tos = { "way.ping.li@gmail.com" };
                       intent.putExtra(Intent.EXTRA_EMAIL, tos);
      
                       intent.putExtra(Intent.EXTRA_SUBJECT,
                               "Android客戶端 - 錯誤報告");
                       if (file != null) {
                           intent.putExtra(Intent.EXTRA_STREAM,
                                   Uri.fromFile(file));
                           intent.putExtra(Intent.EXTRA_TEXT,
                                   "請將此錯誤報告發送給我,以便我盡快修復此問題,謝謝合作!\n");
                       } else {
                           intent.putExtra(Intent.EXTRA_TEXT,
                                   "請將此錯誤報告發送給我,以便我盡快修復此問題,謝謝合作!\n"
                                           + crashReport);
                       }
                       intent.setType("text/plain");
                       intent.setType("message/rfc882");
                       Intent.createChooser(intent, "Choose Email Client");
                       context.startActivity(intent);
      
                   } catch (Exception e) {
                       Log.i("Show","error:" + e.getMessage());
                   } finally {
                       dialog.dismiss();
                       // 退出
                       android.os.Process.killProcess(android.os.Process.myPid());
                       System.exit(1);
                   }
               }
           })
      

      .setNegativeButton(android.R.string.cancel,

           new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int which) {
                   dialog.dismiss();
                   // 退出
                   android.os.Process.killProcess(android.os.Process.myPid());
                   System.exit(1);
               }
           });
      
      

      AlertDialog dialog = builder.create(); //需要的窗口句柄方式,沒有這句會報錯的 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); dialog.show(); }

      /**

    • 獲取APP崩潰異常報告
    • @param ex
    • @return */ private String getCrashReport(Context context, Throwable ex) { PackageInfo pinfo = getPackageInfo(context); StringBuffer exceptionStr = new StringBuffer(); exceptionStr.append("Version: " + pinfo.versionName + "("

           + pinfo.versionCode + ")\n");
      

      exceptionStr.append("Android: " + android.os.Build.VERSION.RELEASE

           + "(" + android.os.Build.MODEL + ")\n");
      

      exceptionStr.append("Exception: " + ex.getMessage() + "\n"); StackTraceElement[] elements = ex.getStackTrace(); for (int i = 0; i < elements.length; i++) {

       exceptionStr.append(elements[i].toString() + "\n");
      

      } return exceptionStr.toString(); }

      /**

    • 獲取App安裝包信息
    • @return */ private PackageInfo getPackageInfo(Context context) { PackageInfo info = null; try {
       info = context.getPackageManager().getPackageInfo(
               context.getPackageName(), 0);
      
      } catch (NameNotFoundException e) {
        e.printStackTrace(System.err);
      
      } if (info == null)
       info = new PackageInfo();
      
      return info; }

}</pre>


3、系統級MyApplication繼承Application。因為需要整個軟件的全局,所以需要在這里初始化CrashHandler



package com.crashhandler.util;
import android.app.Application;
public class MyApplication extends Application{
    private static MyApplication mApplication;

public synchronized static MyApplication getInstance() {
    return mApplication;
}

@Override
public void onCreate() {
    super.onCreate();
    initData();
}

private void initData() {
    //當程序發生Uncaught異常的時候,由該類來接管程序,一定要在這里初始化
    CrashHandler.getInstance().init(this);
}

}</pre>


4、看怎么使用MainActivity



package com.crashhandler.activity;

import android.os.Bundle; import android.app.Activity; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener{

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button button = (Button)findViewById(R.id.button1);
    button.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
    case R.id.button1:
        //自已寫了一個異常信息,進行測試
        Button button = (Button)findViewById(R.id.textView1);
        break;

    default:
        break;
    }

}

}</pre>


5、記得加入一些權限

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
    <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />



    <application
        android:name="com.crashhandler.util.MyApplication"
  。。。。
    </application>


來自:http://blog.csdn.net/qq_16064871/article/details/49225693

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