作为一名android开发人员,开发好的应用推广出去的话,很难预料到会有哪些异常。
而那些未被捕获的异常如果经常出现的话,会很影响用户体验效果的。
android其实对于这些未知的异常,有一个捕获机制的。
好了,废话不多上,上一个例子。
首先是一个异常处理类:
public class ExceptionHandler implements UncaughtExceptionHandler { /** Debug Log tag */ public static final String TAG = "CrashHandler"; /** * 是否开启日志输出,在Debug状态下开启, 在Release状态下关闭以提示程序性能 * */ public static final boolean DEBUG = true; /** 系统默认的UncaughtException处理类 */ private Thread.UncaughtExceptionHandler mDefaultHandler; /** CrashHandler实例 */ private static ExceptionHandler instance; /** 程序的Context对象 */ private Context mContext; /** 使用Properties来保存设备的信息和错误堆栈信息 */ private Properties mDeviceCrashInfo = new Properties(); private static final String VERSION_NAME = "versionName"; private static final String VERSION_CODE = "versionCode"; private static final String OS_VER= "osVer";//安卓版本 private static final String Device_TYPE = "deviceType";//手机型号 private static final String STACK_TRACE = "STACK_TRACE"; /** 错误报告文件的扩展名 */ private static final String CRASH_REPORTER_EXTENSION = ".cr"; /** 保证只有一个CrashHandler实例 */ private ExceptionHandler() { } /** 获取CrashHandler实例 ,单例模式 */ public static ExceptionHandler getInstance() { if (instance == null) { instance = new ExceptionHandler(); } return instance; } /** * 初始化,注册Context对象, 获取系统默认的UncaughtException处理器, 设置该CrashHandler为程序的默认处理器 * @param ctx */ public void init(Context ctx) { mContext = ctx; mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); } /** * 当UncaughtException发生时会转入该函数来处理 */ boolean flag=true; @Override public void uncaughtException(Thread thread, Throwable ex) { if(flag==false)return; flag=false; if (!handleException(ex) && mDefaultHandler != null) { // 如果用户没有处理则让系统默认的异常处理器来处理 Log.e("TEST", "handle Throwable"); } else { // Sleep一会后结束程序 Log.e("TEST", "sleep and finish"); try { Thread.sleep(3000); } catch (InterruptedException e) { Log.e(TAG, "Error : ", e); } Log.e("TEST", "异常"); } // mDefaultHandler.uncaughtException(thread, ex); SPZApplication.getInstance().exit(); } /** * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 开发者可以根据自己的情况来自定义异常处理逻辑 * @param ex * @return true:如果处理了该异常信息;否则返回false */ private boolean handleException(Throwable ex) { if (ex == null) { return true; } final String msg = ex.getLocalizedMessage(); // 使用Toast来显示异常信息 new Thread() { @Override public void run() { Looper.prepare(); Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出。\n"+msg, Toast.LENGTH_LONG) .show(); Looper.loop(); } }.start(); // 收集设备信息 collectCrashDeviceInfo(mContext); // 保存错误报告文件 saveCrashInfoToFile(ex); // 发送错误报告到服务器 return true; } /** * 保存错误信息到文件中 * * @param ex * @return */ private void saveCrashInfoToFile(Throwable ex) { Writer info = new StringWriter(); PrintWriter printWriter = new PrintWriter(info); ex.printStackTrace(printWriter); Throwable cause = ex.getCause(); while (cause != null) { cause.printStackTrace(printWriter); cause = cause.getCause(); } String result = info.toString(); printWriter.close(); mDeviceCrashInfo.put(STACK_TRACE, result); try { long timestamp = System.currentTimeMillis(); String fileName = "crash-" + timestamp + CRASH_REPORTER_EXTENSION; File file = new File(Constant.StorageLocation_CRASH+File.separator+fileName); if(!file.getParentFile().exists()){ file.getParentFile().mkdirs(); } FileOutputStream trace = new FileOutputStream(file); mDeviceCrashInfo.store(trace, ""); trace.flush(); trace.close(); } catch (Exception e) { Log.e(TAG, "an error occured while writing report file...", e); } } /** * 收集程序崩溃的设备信息 * @param ctx */ public void collectCrashDeviceInfo(Context ctx) { PackageInfo versionInfo = Utils.getVersionInfo(ctx); mDeviceCrashInfo.put(VERSION_NAME, new String(versionInfo.versionName).trim()); mDeviceCrashInfo.put(VERSION_CODE, new String(Integer.toString(versionInfo.versionCode)).trim()); mDeviceCrashInfo.put(OS_VER, new String(Build.VERSION.RELEASE).trim()); mDeviceCrashInfo.put(Device_TYPE, new String(Build.MODEL).trim()); } }
如果想全局的接收异常,每个应用对应唯一的Application,我们就应该注册这个application的UncaughtExceptionHandler。
SPZApplication
Application里面有一个List<Activity>,是存储用来结束所有的Activity的,也用过System.exit(1);等等方式,都不理想,这些方法只能结束一个Activity
public class SPZApplication extends Application { private List<Activity> activityList = new LinkedList<Activity>(); private static SPZApplication instance; // 单例模式中获取唯一的MyApplication实例 public static SPZApplication getInstance() { return instance; } // 添加Activity到容器中 public void addActivity(Activity activity) { activityList.add(activity); } // 遍历所有Activity并finish public void exit() { for (Activity activity : activityList) { if(!activity.isFinishing()){ activity.finish(); } } } @Override public void onCreate() { super.onCreate(); instance=this; ExceptionHandler exceptionHandler = ExceptionHandler.getInstance(); // 注册crashHandler exceptionHandler.init(getApplicationContext()); // 发送以前没发送的报告(可选) // exceptionHandler.sendPreviousReportsToServer(); } }
<application android:allowBackup="true" android:icon="@drawable/showicon" android:label="@string/app_name" android:name="com.lock.activity.SPZApplication" android:theme="@style/AppTheme" > <activity android:name="com.bpo.lock.activity.CheckActivity" android:label="@string/app_name" android:screenOrientation="portrait" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>