Android开发之全局异常捕获完美闪退

一、Application的生命周期

在说如何完美退出APP之前,我们先来讲讲Application的生命周期.

1、onCreate,app启动的主入口,程序启动的时候调用

1. @Override  
2. public void onCreate() {  
3. super.onCreate();  
4.   
5. }

2、onTerminate()  app停止的时候执行的方法,但并不一定会调用。当虚拟机为别的应用程序腾出更大资源空间而终止当前应用程序的时候,是不会执行该方法的。

1. public void onTerminate() {  
2. super.onTerminate();  
3. 0);  
4. }

3、onLowMemory()当后台程序已经终止资源还匮乏的时候,会调用这个方法,一般的程序会在这里释放一些不必要的资源

    1. @Override  
    2. public void onLowMemory() {  
    3. super.onLowMemory();  
    4. }

    4、onConfigurationChanged(Configuration newConfig),当配置改变时调用的方法

    1. @Override  
    2. public void onConfigurationChanged(Configuration newConfig) {  
    3. super.onConfigurationChanged(newConfig);  
    4.     }

    看完application的生命周期之后,想必大腿对于APP的启动和退出有了一个大概的认识。我们常说activity,fragment,service,BroadcastReceiver的生命周期,但却很少去了解application的生命周期。其实google设计的这个application类有很大的用处。一般来说,application代表整个应用程序,所以它就是一个全局变量,所有的公共变量、需要全局传递的数值都可以通过application来达到目的。而在activiiy中或者其他地方,我们都可以通过getApplication()或者mContext.getApplicationContext()来拿到application的实例。在这里不在描述过多,网上百度可以找到很多的相关信息。好了,下面进入正题。

    二、全局异常的捕获及完美闪退出应用程序

    直接上代码,代码大部分和网站别人的全局异常捕获一样,但是,在初始化的时候,添加了application参数,在异常处理的方法也和别人的不一样,详细大家请仔细查看注释

      1. package com.batways.tnt.utils;  
      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 com.batways.tnt.AppActivityManager;  
      17. import com.batways.tnt.TntApplication;  
      18. import com.batways.tnt.activity.MainActivity;  
      19. import com.batways.tnt.activity.ProductActivity;  
      20.   
      21. import android.content.Context;  
      22. import android.content.pm.PackageInfo;  
      23. import android.content.pm.PackageManager;  
      24. import android.content.pm.PackageManager.NameNotFoundException;  
      25. import android.os.Build;  
      26. import android.os.Environment;  
      27. import android.util.Log;  
      28. import android.widget.Toast;  
      29.   
      30. /**
      31.  * @ClassName: CrashHandler
      32.  * @author victor_freedom (x_freedom_reddevil@126.com)
      33.  * @createddate 2014-12-25 下午11:41:12
      34.  * @Description: UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
      35.  */  
      36. public class CrashHandler implements UncaughtExceptionHandler {  
      37.   
      38. public static final String TAG = "CrashHandler";  
      39.   
      40. // CrashHandler 实例  
      41. private static CrashHandler INSTANCE = new CrashHandler();  
      42.   
      43. // 程序的 Context 对象  
      44. private Context mContext;  
      45. <span>    </span>// app对象  
      46. private TntApplication app;  
      47.   
      48. // 系统默认的 UncaughtException 处理类  
      49. private Thread.UncaughtExceptionHandler mDefaultHandler;  
      50.   
      51. // 用来存储设备信息和异常信息  
      52. private Map<String, String> infos = new HashMap<String, String>();  
      53.   
      54. // 用于格式化日期,作为日志文件名的一部分  
      55. private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");  
      56.   
      57. /** 保证只有一个 CrashHandler 实例 */  
      58. private CrashHandler() {  
      59.     }  
      60.   
      61. /** 获取 CrashHandler 实例 ,单例模式 */  
      62. public static CrashHandler getInstance() {  
      63. return INSTANCE;  
      64.     }  
      65.   
      66. /**
      67.      * @Title: init
      68.      * @Description: 初始化
      69.      * @param context
      70.      * @param app
      71.      *            传入的app
      72.      * @throws
      73.      */  
      74. public void init(Context context, TntApplication app) {  
      75. // 传入app对象,为完美终止app  
      76. this.app = app;  
      77.         mContext = context;  
      78. // 获取系统默认的 UncaughtException 处理器  
      79.         mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();  
      80. // 设置该 CrashHandler 为程序的默认处理器  
      81. this);  
      82.     }  
      83.   
      84. /**
      85.      * 当 UncaughtException 发生时会转入该函数来处理
      86.      */  
      87. @Override  
      88. public void uncaughtException(Thread thread, Throwable ex) {  
      89. if (!handleException(ex) && mDefaultHandler != null) {  
      90. // 如果用户没有处理则让系统默认的异常处理器来处理  
      91.             mDefaultHandler.uncaughtException(thread, ex);  
      92. else {  
      93. // 释放资源不能像常规的那样在activity的onDestroy方法里面执行,因为如果出现全局异常捕获,activity的关闭有时候是不会再走相关的生命周期函数的(onDesktroy,onStop,onPause等)。  
      94. // 这里是博主在退出app之前需要释放掉的一些资源,通过之前讲的AppActivityManager来拿到对应的实例activity释放里面的资源,然后调用AppExit退出应用程序  
      95.             ProductActivity activitys = (ProductActivity) AppActivityManager  
      96. class);  
      97.             activitys.offline();  
      98.             MainActivity activity = (MainActivity) AppActivityManager  
      99. class);  
      100.             activity.offline();  
      101. // 当执行这一句的时候,其实APP有时候并没有完美的退出(方法详情可以查看博主之前的写的activity管理的文章)  
      102. // 博主的项目里面有网络连接、有后台服务、多线程等各种。执行完这个方法之后,虽然能够闪退出去,但是,当再次进入APP的时候,是回出现ANR的,说明,这样还是没有的完美退出APP  
      103.             AppActivityManager.getAppActivityManager().AppExit(mContext);  
      104. // 之前说application的时候说过,当app退出的时候,会执行onTerminate方法,但是有时候不会主动执行。那么,博主想,如果我们强制执行这个方法,能不能让app完美的终止呢?答案是肯定的。  
      105.             app.onTerminate();  
      106.         }  
      107.     }  
      108.   
      109. /**
      110.      * 自定义错误处理,收集错误信息,发送错误报告等操作均在此完成
      111.      * 
      112.      * @param ex
      113.      * @return true:如果处理了该异常信息;否则返回 false
      114.      */  
      115. private boolean handleException(Throwable ex) {  
      116. if (ex == null) {  
      117. return false;  
      118.         }  
      119. // 收集设备参数信息  
      120.         collectDeviceInfo(mContext);  
      121. // 保存日志文件  
      122.         saveCrashInfo2File(ex);  
      123. return true;  
      124.     }  
      125.   
      126. /**
      127.      * 收集设备参数信息
      128.      * 
      129.      * @param ctx
      130.      */  
      131. public void collectDeviceInfo(Context ctx) {  
      132. try {  
      133.             PackageManager pm = ctx.getPackageManager();  
      134.             PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),  
      135.                     PackageManager.GET_ACTIVITIES);  
      136.   
      137. if (pi != null) {  
      138. null ? "null"  
      139.                         : pi.versionName;  
      140. "";  
      141. "versionName", versionName);  
      142. "versionCode", versionCode);  
      143.             }  
      144. catch (NameNotFoundException e) {  
      145. "an error occured when collect package info", e);  
      146.         }  
      147. class.getDeclaredFields();  
      148. for (Field field : fields) {  
      149. try {  
      150. true);  
      151. null).toString());  
      152. catch (Exception e) {  
      153. "an error occured when collect crash info", e);  
      154.             }  
      155.         }  
      156.     }  
      157.   
      158. /**
      159.      * 保存错误信息到文件中 *
      160.      * 
      161.      * @param ex
      162.      * @return 返回文件名称,便于将文件传送到服务器
      163.      */  
      164. private String saveCrashInfo2File(Throwable ex) {  
      165. new StringBuffer();  
      166. for (Map.Entry<String, String> entry : infos.entrySet()) {  
      167.             String key = entry.getKey();  
      168.             String value = entry.getValue();  
      169. "=" + value + "\n");  
      170.         }  
      171.   
      172. new StringWriter();  
      173. new PrintWriter(writer);  
      174.         ex.printStackTrace(printWriter);  
      175.         Throwable cause = ex.getCause();  
      176. while (cause != null) {  
      177.             cause.printStackTrace(printWriter);  
      178.             cause = cause.getCause();  
      179.         }  
      180.         printWriter.close();  
      181.   
      182.         String result = writer.toString();  
      183.         sb.append(result);  
      184. try {  
      185. long timestamp = System.currentTimeMillis();  
      186. new Date());  
      187. "error-" + time + "-" + timestamp + ".log";  
      188. if (Environment.getExternalStorageState().equals(  
      189.                     Environment.MEDIA_MOUNTED)) {  
      190.                 String path = Environment.getExternalStorageDirectory()  
      191. "/crashs";  
      192.                 Toast.makeText(mContext, path, Toast.LENGTH_LONG).show();  
      193. new File(path);  
      194. if (!dir.exists()) {  
      195.                     dir.mkdirs();  
      196.                 }  
      197. new FileOutputStream(path + fileName);  
      198.                 fos.write(sb.toString().getBytes());  
      199.                 fos.flush();  
      200.                 fos.close();  
      201.             }  
      202. return fileName;  
      203. catch (Exception e) {  
      204. "an error occured while writing file...", e);  
      205.         }  
      206.   
      207. return null;  
      208.     }  
      209.   
      210. }

      要在发现全局异常的时候捕获到,只需要在application类中的onCreate方法执行以下代码即可:

      1. CrashHandler crashHandler = CrashHandler.getInstance();  
      2. this);

      在application类中,要复写onTerminate方法:

      1. @Override  
      2. public void onTerminate() {  
      3. super.onTerminate();  
      4. 0);  
      5. }

      从此,就可以完美无残留的做到退出APP了。