Android 捕捉app系统中未处理的异常

一:为什么要处理?

其实我们都知道,在开发过程中,自己的app系统或许有许多隐藏的异常,自己没有捕捉到,那么关于异常的捕捉,这是相当重要的,如果系统发生崩溃,那么至少也可以让系统挂在系统之内,不会发现什么系统直接退了,或者是卡死,这样做,能够使得用户体验效果更加,自己也可以发现用户到底出现什么异常,便于自己以后好处理这个问题,优化处理自己的系统。

二:如何解决

在Android 开发中,自身其实带有一个系统默认的异常处理接口,UncaughtExceptionHandler,该接口呢,能够很好的处理程序中发生的异常,所以,往往开发者都喜欢使用它,而且它也是一个非常简单好用的东西。

三:具体实现

(1)实现UncaughtExceptionHandler接口的类

package com.x1.tools;  
  
import java.lang.Thread.UncaughtExceptionHandler;  
  
import android.app.ActivityManager;  
import android.content.ComponentName;  
import android.content.Context;  
import android.os.Looper;  
import android.util.Log;  
  
import com.x1.dao.SubmitConfigBugs;  
import com.x1.ui.R;  
  
/** 
 * 未捕获异常捕捉类 
 *  
 * @author zengtao 2015年5月6日 
 * 
 * 
 */  
public class CrashHandlers implements UncaughtExceptionHandler {  
  
    public static final String TGA = "CrashHandlers";  
  
    // 系统默认的UncaughtException处理类  
    private Thread.UncaughtExceptionHandler mDefaultHandler;  
  
    // CrashHandler实例  
    private static CrashHandlers instance;  
    // 程序的Context对象  
    private Context mContext;  
  
    private GetPhoneInfo phone;  
  
    /** 保证只有一个CrashHandler实例 */  
    private CrashHandlers() {  
    }  
  
    /** 获取CrashHandler实例 ,单例模式 */  
    public synchronized static CrashHandlers getInstance() {  
        if (instance == null) {  
            instance = new CrashHandlers();  
        }  
        return instance;  
    }  
  
    /** 
     * 初始化 
     *  
     * @param context 
     */  
    public void init(Context context) {  
        mContext = context;  
        // 获取系统默认的UncaughtException处理器  
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();  
        // 设置该CrashHandler为程序的默认处理器  
        Thread.setDefaultUncaughtExceptionHandler(this);  
        phone = new GetPhoneInfo(context);  
    }  
  
    /** 
     * 当UncaughtException发生时会转入该函数来处理 
     */  
    @Override  
    public void uncaughtException(Thread thread, Throwable ex) {  
        if (!handleException(thread, ex) && mDefaultHandler != null) {  
            // 如果用户没有处理则让系统默认的异常处理器来处理  
            mDefaultHandler.uncaughtException(thread, ex);  
        } else {  
            try {  
                Thread.sleep(3000);  
            } catch (InterruptedException e) {  
                Log.e(TGA, e.toString());  
            }  
            // 退出程序  
            android.os.Process.killProcess(android.os.Process.myPid());  
            System.exit(1);  
        }  
    }  
  
    /** 
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 
     *  
     * @param ex 
     * @return true:如果处理了该异常信息;否则返回false. 
     */  
    private boolean handleException(Thread thread, Throwable ex) {  
        if (ex == null) {  
            return false;  
        }  
        // 使用Toast来显示异常信息  
        new Thread() {  
            @Override  
            public void run() {  
                Looper.prepare();  
                ShowToast.show(mContext, "喵,很抱歉,程序出现异常,即将退出!",  
                        R.drawable.error_icon);  
                Looper.loop();  
            }  
        }.start();  
        // 把异常信息和设备信息上传到服务器  
        subMitThreadAndDeviceInfo(mContext, thread, ex);  
        return true;  
    }  
  
    // 提交信息到服务器  
    public void subMitThreadAndDeviceInfo(Context ctx, Thread thread,  
            Throwable ex) {  
        // 当前用户的账号  
        String Account = null;  
        if (Config.getCachedAccount(ctx) != null) {  
            Account = Config.getCachedAccount(ctx);  
        } else {  
            Account = "当前无用户登录";  
        }  
        // 手机设备的信息  
        String PhoneModel = phone.PhoneModel;  
        String PhoneVersion = phone.PhoneVersion;  
        String PhoneResolution = phone.PhoneResolution;  
        String ZcmVersion = phone.ZcmVersion;  
        String AvailableRom = phone.AvailableRom;  
        // 获取当前显示在界面上的Activity的路径加类名  
        ActivityManager am = (ActivityManager) ctx  
                .getSystemService(Context.ACTIVITY_SERVICE);  
        ComponentName cn = am.getRunningTasks(1).get(0).topActivity;  
        // 异常信息加异常所在类的绝对路径  
        final String content = "thread: " + thread + " , name: "  
                + thread.getName() + ", id: " + thread.getId()  
                + ", exception: " + ex + ", " + cn.getClassName();  
  
        // 执行接口,把异常信息提交到服务器  
        new SubmitConfigBugs(0, ctx, Account, PhoneModel, PhoneVersion,  
                PhoneResolution, ZcmVersion, AvailableRom, content,  
                new SubmitConfigBugs.SuccessCallback() {  
                    @Override  
                    public void onSuccess() {  
                        Log.e(TGA, content + "
错误信息提交成功");  
                    }  
                }, new SubmitConfigBugs.FailCallback() {  
  
                    @Override  
                    public void onFail() {  
                        Log.e(TGA, content + "
错误信息提交失败");  
                    }  
                });  
    }  
  
}

(2)实体类:手机信息

package com.x1.tools;  
  
import android.content.Context;  
import android.content.pm.PackageInfo;  
import android.content.pm.PackageManager;  
import android.os.Environment;  
import android.os.StatFs;  
import android.text.TextUtils;  
import android.view.Display;  
import android.view.WindowManager;  
  
/** 
 * 获取当前手机的设备信息和当前软件的版本 
 *  
 * @author zengtao 2015年5月6日 
 * 
 * 
 */  
public class GetPhoneInfo {  
    private Context context;  
    public String PhoneModel;  
    public String PhoneVersion;  
    public String PhoneResolution;  
    public String ZcmVersion;  
    public String AvailableRom;  
  
    public GetPhoneInfo(Context context) {  
        this.context = context;  
        PhoneModel = android.os.Build.MODEL;  
        PhoneVersion = android.os.Build.VERSION.RELEASE;  
        PhoneResolution = getDisplayWAndH();  
        ZcmVersion = getAppVersionName(this.context);  
        AvailableRom = "ROM剩余存储空间: " + getAvailableInternalMemorySize() + "MB"  
                + ",内置SDCard剩余存储空间: " + getAvailableExternalMemorySize() + "MB";  
    }  
  
    // 获取当前版本号  
    private String getAppVersionName(Context context) {  
        String versionName = "";  
        try {  
            PackageManager packageManager = context.getPackageManager();  
            PackageInfo packageInfo = packageManager.getPackageInfo(  
                    "com.x1.ui", 0);  
            versionName = packageInfo.versionName;  
            if (TextUtils.isEmpty(versionName)) {  
                return "";  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return versionName;  
    }  
  
    // 获取屏幕分辨率  
    @SuppressWarnings("deprecation")  
    private String getDisplayWAndH() {  
        WindowManager wm = (WindowManager) context  
                .getSystemService(Context.WINDOW_SERVICE);  
        Display display = wm.getDefaultDisplay();  
        String string = "屏幕分辨率: " + display.getWidth() + "x"  
                + display.getHeight();  
        return string;  
    }  
  
    /** 
     *  
     * @return ROM存储路径 
     */  
    private String getInternalMemoryPath() {  
        return Environment.getDataDirectory().getPath();  
    }  
  
    /** 
     *  
     * @return 内置sd卡路径 
     */  
    private String getExternalMemoryPath() {  
        return Environment.getExternalStorageDirectory().getPath();  
    }  
  
    /** 
     *  
     * @param path 
     *            文件路径 
     * @return 文件路径的StatFs对象 
     * @throws Exception 
     *             路径为空或非法异常抛出 
     */  
    private StatFs getStatFs(String path) {  
        try {  
            return new StatFs(path);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return null;  
    }  
  
    /** 
     *  
     * @param stat 
     *            文件StatFs对象 
     * @return 剩余存储空间的MB数 
     *  
     */  
    @SuppressWarnings("deprecation")  
    private float calculateSizeInMB(StatFs stat) {  
        if (stat != null)  
            return stat.getAvailableBlocks()  
                    * (stat.getBlockSize() / (1024f * 1024f));  
        return 0.0f;  
    }  
  
    /** 
     *  
     * @return ROM剩余存储空间的MB数 
     */  
    private float getAvailableInternalMemorySize() {  
  
        String path = getInternalMemoryPath();// 获取数据目录  
        StatFs stat = getStatFs(path);  
        return calculateSizeInMB(stat);  
    }  
  
    /** 
     *  
     * @return 内置SDCard剩余存储空间MB数 
     */  
    private float getAvailableExternalMemorySize() {  
  
        String path = getExternalMemoryPath();// 获取数据目录  
        StatFs stat = getStatFs(path);  
        return calculateSizeInMB(stat);  
  
    }  
} 

  

  1. package com.x1.tools;  
  2.   
  3. import java.lang.Thread.UncaughtExceptionHandler;  
  4.   
  5. import android.app.ActivityManager;  
  6. import android.content.ComponentName;  
  7. import android.content.Context;  
  8. import android.os.Looper;  
  9. import android.util.Log;  
  10.   
  11. import com.x1.dao.SubmitConfigBugs;  
  12. import com.x1.ui.R;  
  13.   
  14. /** 
  15.  * 未捕获异常捕捉类 
  16.  *  
  17.  * @author zengtao 2015年5月6日 
  18.  * 
  19.  * 
  20.  */  
  21. public class CrashHandlers implements UncaughtExceptionHandler {  
  22.   
  23.     public static final String TGA = "CrashHandlers";  
  24.   
  25.     // 系统默认的UncaughtException处理类  
  26.     private Thread.UncaughtExceptionHandler mDefaultHandler;  
  27.   
  28.     // CrashHandler实例  
  29.     private static CrashHandlers instance;  
  30.     // 程序的Context对象  
  31.     private Context mContext;  
  32.   
  33.     private GetPhoneInfo phone;  
  34.   
  35.     /** 保证只有一个CrashHandler实例 */  
  36.     private CrashHandlers() {  
  37.     }  
  38.   
  39.     /** 获取CrashHandler实例 ,单例模式 */  
  40.     public synchronized static CrashHandlers getInstance() {  
  41.         if (instance == null) {  
  42.             instance = new CrashHandlers();  
  43.         }  
  44.         return instance;  
  45.     }  
  46.   
  47.     /** 
  48.      * 初始化 
  49.      *  
  50.      * @param context 
  51.      */  
  52.     public void init(Context context) {  
  53.         mContext = context;  
  54.         // 获取系统默认的UncaughtException处理器  
  55.         mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();  
  56.         // 设置该CrashHandler为程序的默认处理器  
  57.         Thread.setDefaultUncaughtExceptionHandler(this);  
  58.         phone = new GetPhoneInfo(context);  
  59.     }  
  60.   
  61.     /** 
  62.      * 当UncaughtException发生时会转入该函数来处理 
  63.      */  
  64.     @Override  
  65.     public void uncaughtException(Thread thread, Throwable ex) {  
  66.         if (!handleException(thread, ex) && mDefaultHandler != null) {  
  67.             // 如果用户没有处理则让系统默认的异常处理器来处理  
  68.             mDefaultHandler.uncaughtException(thread, ex);  
  69.         } else {  
  70.             try {  
  71.                 Thread.sleep(3000);  
  72.             } catch (InterruptedException e) {  
  73.                 Log.e(TGA, e.toString());  
  74.             }  
  75.             // 退出程序  
  76.             android.os.Process.killProcess(android.os.Process.myPid());  
  77.             System.exit(1);  
  78.         }  
  79.     }  
  80.   
  81.     /** 
  82.      * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 
  83.      *  
  84.      * @param ex 
  85.      * @return true:如果处理了该异常信息;否则返回false. 
  86.      */  
  87.     private boolean handleException(Thread thread, Throwable ex) {  
  88.         if (ex == null) {  
  89.             return false;  
  90.         }  
  91.         // 使用Toast来显示异常信息  
  92.         new Thread() {  
  93.             @Override  
  94.             public void run() {  
  95.                 Looper.prepare();  
  96.                 ShowToast.show(mContext, "喵,很抱歉,程序出现异常,即将退出!",  
  97.                         R.drawable.error_icon);  
  98.                 Looper.loop();  
  99.             }  
  100.         }.start();  
  101.         // 把异常信息和设备信息上传到服务器  
  102.         subMitThreadAndDeviceInfo(mContext, thread, ex);  
  103.         return true;  
  104.     }  
  105.   
  106.     // 提交信息到服务器  
  107.     public void subMitThreadAndDeviceInfo(Context ctx, Thread thread,  
  108.             Throwable ex) {  
  109.         // 当前用户的账号  
  110.         String Account = null;  
  111.         if (Config.getCachedAccount(ctx) != null) {  
  112.             Account = Config.getCachedAccount(ctx);  
  113.         } else {  
  114.             Account = "当前无用户登录";  
  115.         }  
  116.         // 手机设备的信息  
  117.         String PhoneModel = phone.PhoneModel;  
  118.         String PhoneVersion = phone.PhoneVersion;  
  119.         String PhoneResolution = phone.PhoneResolution;  
  120.         String ZcmVersion = phone.ZcmVersion;  
  121.         String AvailableRom = phone.AvailableRom;  
  122.         // 获取当前显示在界面上的Activity的路径加类名  
  123.         ActivityManager am = (ActivityManager) ctx  
  124.                 .getSystemService(Context.ACTIVITY_SERVICE);  
  125.         ComponentName cn = am.getRunningTasks(1).get(0).topActivity;  
  126.         // 异常信息加异常所在类的绝对路径  
  127.         final String content = "thread: " + thread + " , name: "  
  128.                 + thread.getName() + ", id: " + thread.getId()  
  129.                 + ", exception: " + ex + ", " + cn.getClassName();  
  130.   
  131.         // 执行接口,把异常信息提交到服务器  
  132.         new SubmitConfigBugs(0, ctx, Account, PhoneModel, PhoneVersion,  
  133.                 PhoneResolution, ZcmVersion, AvailableRom, content,  
  134.                 new SubmitConfigBugs.SuccessCallback() {  
  135.                     @Override  
  136.                     public void onSuccess() {  
  137.                         Log.e(TGA, content + " 错误信息提交成功");  
  138.                     }  
  139.                 }, new SubmitConfigBugs.FailCallback() {  
  140.   
  141.                     @Override  
  142.                     public void onFail() {  
  143.                         Log.e(TGA, content + " 错误信息提交失败");  
  144.                     }  
  145.                 });  
  146.     }  
  147.   
原文地址:https://www.cnblogs.com/foxy/p/7815753.html