Android程序崩溃重启

来源:互联网 发布:淘宝店铺生意不好 编辑:程序博客网 时间:2024/06/05 03:13

有时候,我们需要应用在崩溃的时候自动重启,并打开崩溃前的那个Activity。

这时候,我们就需要用到Thread.UncaughtExceptionHandler这个接口。


首先,我们知道,既然是要在整个Application的生命周期范围内都有效,那么我们也应该在Application中绑定它。这里有两个做法:


1.程序的Application实现这个接口:

[java] view plain copy
  1. package com.example.msi_cn.myapplication;  
  2.   
  3. import android.app.ActivityManager;  
  4. import android.app.Application;  
  5. import android.content.Intent;  
  6.   
  7. /** 
  8.  * Created by msi-cn on 2016/6/4. 
  9.  */  
  10. public class MyApplication extends Application implements Thread.UncaughtExceptionHandler {  
  11.   
  12.     @Override  
  13.     public void onCreate() {  
  14.         super.onCreate();  
  15.         Thread.setDefaultUncaughtExceptionHandler(this);  
  16.     }  
  17.   
  18.     @Override  
  19.     public void uncaughtException(Thread thread, Throwable ex) {  
  20.         Intent intent = new Intent(this, getTopActivity());  
  21.         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |  
  22.                 Intent.FLAG_ACTIVITY_NEW_TASK);  
  23.         startActivity(intent);  
  24.         android.os.Process.killProcess(android.os.Process.myPid());  
  25.     }  
  26.   
  27.     /** 
  28.      * 获取栈中最顶部的Activity,即最后发生崩溃的Activity。 
  29.      * 如果你只需要打开MainActivity等固定的Activity则无需使用此方法 
  30.      */  
  31.     public Class getTopActivity() {  
  32.         ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);  
  33.         String className = manager.getRunningTasks(1).get(0).topActivity.getClassName();  
  34.         Class cls = null;  
  35.         try {  
  36.             cls = Class.forName(className);  
  37.         } catch (ClassNotFoundException e) {  
  38.             e.printStackTrace();  
  39.         }  
  40.         return cls;  
  41.     }  
  42. }  


2.自定义类实现此接口,在MyApplication中绑定,如:

[java] view plain copy
  1. package com.example.msi_cn.myapplication;  
  2.   
  3. import android.app.Application;  
  4.   
  5. /** 
  6.  * Created by msi-cn on 2016/6/4. 
  7.  */  
  8. public class MyApplication extends Application {  
  9.   
  10.     @Override  
  11.     public void onCreate() {  
  12.         super.onCreate();  
  13.         CrashHandler.getInstance().init(this); // 一定要先初始化  
  14.         Thread.setDefaultUncaughtExceptionHandler(CrashHandler.getInstance());  
  15.     }  
  16.       
  17. }  


这里我们使用了一个自定义的CrashHandler类来处理

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


当然,这个工具类还做了很多其他的事,包括Toast提示用户程序异常,收集设备信息输出崩溃日记(SD读写权限),程序重启等。根据个人爱好定制自己的处理方式都可以。

重要的是我们可以通过继承Thread.UncaughtExceptionHandler这个接口的方式来实现程序崩溃重启

原创粉丝点击