Android中处理崩溃闪退错误

来源:互联网 发布:如何实时获取股票数据 编辑:程序博客网 时间:2024/04/28 10:09

我们需要的是软件有一个全局的异常捕获器,当出现一个我们没有发现的异常时,捕获这个异常,并且将异常信息记录下来,上传到服务器公开发这分析出现异常的具体原因。不过首先我们还是来了解以下两个类:android.app.Application和java.lang.Thread.UncaughtExceptionHandler。

Application:用来管理应用程序的全局状态。在应用程序启动时Application会首先创建,然后才会根据情况(Intent)来启动相应的Activity和Service。本示例中将在自定义加强版的Application中注册未捕获异常处理器。

Thread.UncaughtExceptionHandler:线程未捕获异常处理器,用来处理未捕获异常。如果程序出现了未捕获异常,默认会弹出系统中强制关闭对话框。我们需要实现此接口,并注册为程序中默认未捕获异常处理。这样当未捕获异常发生时,就可以做一些个性化的异常处理操作。

AppException.java实现了Thread.UncaughtExceptionHandler,使我们用来处理未捕获异常的主要成员,代码如下:



[html] view plain copy
  1. import java.lang.Thread.UncaughtExceptionHandler;    
  2. import java.lang.reflect.Field;    
  3. import java.text.DateFormat;    
  4. import java.text.SimpleDateFormat;    
  5. import java.util.Date;    
  6. import java.util.HashMap;    
  7. import java.util.Map;    
  8. import android.content.Context;    
  9. import android.content.pm.PackageInfo;    
  10. import android.content.pm.PackageManager;    
  11. import android.content.pm.PackageManager.NameNotFoundException;    
  12. import android.os.Build;    
  13. import android.os.Environment;    
  14. import android.os.Looper;    
  15. import android.util.Log;    
  16. import android.widget.Toast;    
  17.     
  18. /**   
  19.  * AppException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.   
  20.  *    
  21.  * @author user   
  22.  *    
  23.  */    
  24. public class AppException implements UncaughtExceptionHandler {    
  25.         
  26.     public static final String TAG = "AppException";    
  27.         
  28.     //系统默认的UncaughtException处理类     
  29.     private Thread.UncaughtExceptionHandler mDefaultHandler;    
  30.     //CrashHandler实例    
  31.     private static AppException INSTANCE = new AppException();    
  32.     //程序的Context对象    
  33.     private Context mContext;    
  34.     //用来存储设备信息和异常信息    
  35.     private Map<String, String> infos = new HashMap<String, String>();    
  36.     
  37.     //用于格式化日期,作为日志文件名的一部分    
  38.     private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");    
  39.     
  40.     /** 保证只有一个AppEeception实例 */    
  41.     private AppException() {    
  42.     }    
  43.     
  44.     /** 获取AppException实例 ,单例模式 */    
  45.     public static AppException getInstance() {    
  46.         return INSTANCE;    
  47.     }    
  48.     
  49.     /**   
  50.      * 初始化   
  51.      *    
  52.      * @param context   
  53.      */    
  54.     public void init(Context context) {    
  55.         mContext = context;    
  56.         //获取系统默认的UncaughtException处理器    
  57.         mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();    
  58.         //设置该CrashHandler为程序的默认处理器    
  59.         Thread.setDefaultUncaughtExceptionHandler(this);    
  60.     }    
  61.     
  62.     /**   
  63.      * 当UncaughtException发生时会转入该函数来处理   
  64.      */    
  65.     @Override    
  66.     public void uncaughtException(Thread thread, Throwable ex) {    
  67.         if (!handleException(ex) && mDefaultHandler != null) {    
  68.             //如果用户没有处理则让系统默认的异常处理器来处理    
  69.             mDefaultHandler.uncaughtException(thread, ex);    
  70.         } else {    
  71.             try {    
  72.                 Thread.sleep(3000);    
  73.             } catch (InterruptedException e) {    
  74.                 Log.e(TAG, "error : ", e);    
  75.             }    
  76.             //退出程序    
  77.             android.os.Process.killProcess(android.os.Process.myPid());    
  78.             System.exit(1);    
  79.         }    
  80.     }    
  81.     
  82.     /**   
  83.      * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.   
  84.      *    
  85.      * @param ex   
  86.      * @return true:如果处理了该异常信息;否则返回false.   
  87.      */    
  88.     private boolean handleException(Throwable ex) {    
  89.         if (ex == null) {    
  90.             return false;    
  91.         }    
  92.         //使用Toast来显示异常信息    
  93.         new Thread() {    
  94.             @Override    
  95.             public void run() {    
  96.                 Looper.prepare();    
  97.                 Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG).show();    
  98.                 Looper.loop();    
  99.             }    
  100.         }.start();    
  101.         //收集设备参数信息     
  102.         collectDeviceInfo(mContext);    
  103.         //保存日志文件     
  104.         saveCrashInfo2File(ex);    
  105.         return true;    
  106.     }    
  107.         
  108.     /**   
  109.      * 收集设备参数信息   
  110.      * @param ctx   
  111.      */    
  112.     public void collectDeviceInfo(Context ctx) {    
  113.         try {    
  114.             PackageManager pm = ctx.getPackageManager();    
  115.             PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);    
  116.             if (pi != null) {    
  117.                 String versionName = pi.versionName == null ? "null" : pi.versionName;    
  118.                 String versionCode = pi.versionCode + "";    
  119.                 infos.put("versionName", versionName);    
  120.                 infos.put("versionCode", versionCode);    
  121.             }    
  122.         } catch (NameNotFoundException e) {    
  123.             Log.e(TAG, "an error occured when collect package info", e);    
  124.         }    
  125.         Field[] fields = Build.class.getDeclaredFields();    
  126.         for (Field field : fields) {    
  127.             try {    
  128.                 field.setAccessible(true);    
  129.                 infos.put(field.getName(), field.get(null).toString());    
  130.                 Log.d(TAG, field.getName() + " : " + field.get(null));    
  131.             } catch (Exception e) {    
  132.                 Log.e(TAG, "an error occured when collect crash info", e);    
  133.             }    
  134.         }    
  135.     }    
  136.     
  137.     /**   
  138.      * 保存错误信息到文件中   
  139.      *    
  140.      * @param ex   
  141.      * @return  返回文件名称,便于将文件传送到服务器   
  142.      */    
  143.     private String saveCrashInfo2File(Throwable ex) {    
  144.             
  145.         StringBuffer sb = new StringBuffer();    
  146.         for (Map.Entry<String, String> entry : infos.entrySet()) {    
  147.             String key = entry.getKey();    
  148.             String value = entry.getValue();    
  149.             sb.append(key + "=" + value + "\n");    
  150.         }    
  151.             
  152.         Writer writer = new StringWriter();    
  153.         PrintWriter printWriter = new PrintWriter(writer);    
  154.         ex.printStackTrace(printWriter);    
  155.         Throwable cause = ex.getCause();    
  156.         while (cause != null) {    
  157.             cause.printStackTrace(printWriter);    
  158.             cause = cause.getCause();    
  159.         }    
  160.         printWriter.close();    
  161.         String result = writer.toString();    
  162.         sb.append(result);    
  163.         try {    
  164.             long timestamp = System.currentTimeMillis();    
  165.             String time = formatter.format(new Date());    
  166.             String fileName = "log-" + time + "-" + timestamp + ".log";    
  167.             if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {    
  168.                 String path = android.os.Environment.getExternalStorageDirectory().getAbsolutePath() + "/PdaChina/";    
  169.                 File dir = new File(path);    
  170.                 if (!dir.exists()) {    
  171.                     dir.mkdirs();    
  172.                 }    
  173.                 FileOutputStream fos = new FileOutputStream(path + fileName);    
  174.                 fos.write(sb.toString().getBytes());    
  175.                 fos.close();    
  176.             }    
  177.             return fileName;    
  178.         } catch (Exception e) {    
  179.             Log.e(TAG, "an error occured while writing file...", e);    
  180.         }    
  181.         return null;    
  182.     }    
  183. }  

完成这个AppException后,我们需要在一个Application环境中让其运行,为此,我们继承android.app.Application,添加自己的代码,AppContext.java代码如下:


[html] view plain copy
  1. package cn.com.ista.pdachina.app;  
  2.   
  3. import android.app.Application;  
  4. import android.content.Context;  
  5. /**  
  6.  * 全局获取上下文类:用于保存和调用全局应用配置及访问网络数据  
  7.  * @author guopeng  
  8.  * @version 1.0  
  9.  * @created 2015-10-26  
  10.  */  
  11. public class AppContext extends Application {  
  12.   
  13.     private static Context instance;  
  14.   
  15.     @Override  
  16.     public void onCreate()   
  17.     {  
  18.         instance = getApplicationContext();  
  19.           
  20.         AppException appException = AppException.getInstance();  
  21.         appException.init(instance);  
  22.     }  
  23.       
  24.     public static Context getContext()  
  25.     {  
  26.         return instance;  
  27.     }  
  28.       
  29. }  

[html] view plain copy
  1. <!-- SD卡读写权限 -->  
  2. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
  3. <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>   

[html] view plain copy
  1. 为了让我们的AppContext取代android.app.Application的地位,在我们的代码中生效,我们需要修改AndroidManifest.xml:  
  2.   
  3. <application android:name=".CrashApplication" ...>    
  4. </application>  
0 0