android 异常收集

来源:互联网 发布:python sleep函数 编辑:程序博客网 时间:2024/05/17 19:22

当开发了一个APP,发布出去之后难免会碰到系统异常的情况,而且大多数的异常是不可重现的。经常遇到的情况就是平台迁移之后,数据与原来存在差异,比如说实际情况种

的数据和开发和测试不一样。还有就是测试覆盖面不全,市面上的手机型号那么多,而有的公司并不会为没款手机都适配,跑不同的手机出现异常在所难免。这个时候如果出现

了异常调试起来时比较麻烦的,因为出现异常的环境不一定就能重现出来。

这里主要总结一下开发中用到的异常捕获和调试方法,不同于开发中使用的logcat。思路和原理都很简单,就是当应用发生 异常的时候,可以把异常信息收集并保存起来,我这

里是把它保存到了一个固定目录下的文件中,出现异常时可以通过异常记录文件跟踪定位异常,分析出错原因。

捕获异常的方法是通过实现UncaughtExceptionHandler接口,当异常发生的时候,会回调uncaughtException()方法,在此方法中完成我们需要处理的工作,此处仅把出错信息

保存到固定目录中。

1.首先定义一个自己的异常处理类,实现UncaughtExceptionHandler接口,代码如下:

public class ExceptionHandler implements Thread.UncaughtExceptionHandler {private static ExceptionHandler instance = new ExceptionHandler();private Context context;private String infoPath = "/ErrorLog/";private Thread.UncaughtExceptionHandler defaultHandler;private Map<String, String> devInfos = new HashMap<String, String>();private DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault());public static ExceptionHandler getInstance() {return instance;}public void setCustomCrashHanler(Context ctx) {context = ctx;defaultHandler = Thread.getDefaultUncaughtExceptionHandler();Thread.setDefaultUncaughtExceptionHandler(this);}/** * @name uncaughtException(Thread thread, Throwable ex) * @description 当发生UncaughtException时会回调此函数 * @param thread 发生异常的线程 * @param ex 抛出的异常 * @return void */@Overridepublic void uncaughtException(Thread thread, Throwable ex) {boolean isDone = doException(ex);if (!isDone && defaultHandler != null) {// 如果用户没有处理则让系统默认的异常处理器来处理defaultHandler.uncaughtException(thread, ex);} else {// 如果自己处理了异常,则不会弹出错误对话框,则需要手动退出apptry {Thread.sleep(3000);} catch (InterruptedException e) {}}android.os.Process.killProcess(android.os.Process.myPid());System.exit(0);}/** * @name doException(Throwable ex) * @description 处理异常 * @param ex 抛出的异常 * @return 异常处理标志 */private boolean doException(Throwable ex) {if (ex == null) {return true;}new Thread() {              @Override              public void run() {                  Looper.prepare();                  Toast.makeText(context, "程序出现错误退出!", Toast.LENGTH_LONG).show();                  Looper.loop();              }          }.start(); collectDeviceInfo(context);saveExceptionToFile(ex);return true;}/** * @name collectDeviceInfo(Context ctx) * @description 收集必须的设备信息 * @param ctx  * @return void */public void collectDeviceInfo(Context ctx) {try {PackageManager pm = ctx.getPackageManager();PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);if (pi != null) {devInfos.put("versionName", pi.versionName);devInfos.put("versionCode", "" + pi.versionCode);devInfos.put("MODEL", "" + Build.MODEL);devInfos.put("SDK_INT", "" + Build.VERSION.SDK_INT);devInfos.put("PRODUCT", "" + Build.PRODUCT);devInfos.put("TIME", "" + getCurrentTime());}} catch (NameNotFoundException e) {}Field[] fields = Build.class.getDeclaredFields();for (Field field : fields) {try {field.setAccessible(true);devInfos.put(field.getName(), field.get(null).toString());} catch (Exception e) {}}}/** * @name saveExceptionToFile(Throwable ex) * @description 保存异常信息到文件中 * @param ex 抛出的异常 * @return void */private void saveExceptionToFile(Throwable ex) {StringBuffer sb = new StringBuffer();for (Map.Entry<String, String> entry : devInfos.entrySet()) {String key = entry.getKey();String value = entry.getValue();sb.append(key + "=" + value + "\n");}Writer writer = new StringWriter();PrintWriter printWriter = new PrintWriter(writer);ex.printStackTrace(printWriter);Throwable cause = ex.getCause();while (cause != null) {cause.printStackTrace(printWriter);cause = cause.getCause();}printWriter.close();String result = writer.toString();sb.append(result);try {String time = df.format(new Date());String fileName = time + ".txt";if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {String path = Environment.getExternalStorageDirectory() + infoPath;File dir = new File(path);if (!dir.exists()) {dir.mkdirs();}FileOutputStream fos = new FileOutputStream(path + fileName);fos.write(sb.toString().getBytes());fos.close();}} catch (Exception e) {}}/** * @name getCurrentTime() * @description 获取当前时间 * @param void * @return 当前时间 */public static String getCurrentTime() {SimpleDateFormat sdf = null;sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String CurrentTime = sdf.format(new Date());return CurrentTime;}}



2.在自定义Application初始化异常捕获对象,并设置异常出现是处理的对象:

/** * @name MyApplication * @description 自定义Application * @author cold * @date 2016.05.18 15:30 * @copyright  */public class MyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();ExceptionHandler mCustomCrashHandler = ExceptionHandler.getInstance();mCustomCrashHandler.setCustomCrashHanler(this);}}

3.在MainActivity里模拟出现空指针的异常

public class MainActivity extends Activity {private Button btnTest = null;private Button btnError = null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btnTest = (Button)findViewById(R.id.btn_test);btnTest.setOnClickListener(listener);}private OnClickListener listener = new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubbtnError.setText("error");}};}
代码中btnError并未初始化,所以会报出空指针异常。

4.当出现异常时,可以查看/storage/sdcard0/ErrorLog目录下的异常文件,根据此文件分析异常原因:

文件内容截取:

java.lang.NullPointerExceptionat com.example.exceptiontest.MainActivity$1.onClick(MainActivity.java:26)at android.view.View.performClick(View.java:4496)at android.view.View$PerformClick.run(View.java:18603)at android.os.Handler.handleCallback(Handler.java:733)at android.os.Handler.dispatchMessage(Handler.java:95)at android.os.Looper.loop(Looper.java:136)at android.app.ActivityThread.main(ActivityThread.java:5426)at java.lang.reflect.Method.invokeNative(Native Method)at java.lang.reflect.Method.invoke(Method.java:515)at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1268)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1084)at dalvik.system.NativeStart.main(Native Method)

可以看出这和我们使用logcat调试一样,只不过是多了一步文件存储的过程。

4.需要在AndroidManifest.xml中添加sd卡权限

    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

例子代码:

点击打开链接

















0 0
原创粉丝点击