0
点赞
收藏
分享

微信扫一扫

Android 重写系统Crash处理类,保存Crash信息到SD卡 和 完美退出程序的方法


我们开发Android应用的时候,当出现Crash的时候,系统弹出一个警告框,如下图一,有些手机会黑屏几秒钟然后还伴随着振动,作为我们开发人员,是很讨厌这样子的Crash,因为这意味着我们又要改bug,每个程序员都希望自己开发出来的东西bug少点,稳定点,但是没有bug的程序几乎是不可能的,作为用户,如果出现这样子的警告框,他的心情也会很不爽,也许还会破口大骂,如果用图二来提示用户是不是感觉会好一点

Android 重写系统Crash处理类,保存Crash信息到SD卡 和 完美退出程序的方法_java

Android 重写系统Crash处理类,保存Crash信息到SD卡 和 完美退出程序的方法_android_02

一句简简单单的“很抱歉,程序遭遇异常,即将退出”是不是更有人情味,人们对道歉的话是永远不会嫌腻的,哈哈!当然我们自定义Carsh处理类不仅仅是将系统警告框替换成Toast,还有更重要的原因,我们都知道市场上的Android设备和系统琳琅满目,参差不齐的,如果我们购买市场上每一种Android设备来测试,这是不现实的,况且这其中购买设备的资金也不小,所以我们重写Crash处理类,当我们的用户发生Crash的时候,我们将设备信息和错误信息保存起来,然后再上传到服务器中,这对于我们是极其有帮助的,特别是个人开发用户和小公司,因为他们的测试可能相对于大公司来说会显得不那么专业,就比如我们公司吧,自己测试,然后我们老大也要我做这个功能,我就将捕获异常和保存异常信息和大家分享下,上传到服务器功能的大家自行实现

先看下项目结构

Android 重写系统Crash处理类,保存Crash信息到SD卡 和 完美退出程序的方法_移动开发_03

1.我们新建一个CustomCrashHandler类 实现UncaughtExceptionHandler接口,重写回调方法void uncaughtException(Thread thread, Throwable ex)

 



1. <span style="font-size:12px;">package com.example.customcrash;  
2. import java.io.File;  
3. import java.io.FileOutputStream;  
4. import java.io.PrintWriter;  
5. import java.io.StringWriter;  
6. import java.lang.Thread.UncaughtExceptionHandler;  
7. import java.text.SimpleDateFormat;  
8. import java.util.Date;  
9. import java.util.HashMap;  
10. import java.util.Map;  
11. import java.util.TimeZone;  
12.   
13. import android.content.Context;  
14. import android.content.pm.PackageInfo;  
15. import android.content.pm.PackageManager;  
16. import android.content.pm.PackageManager.NameNotFoundException;  
17. import android.os.Build;  
18. import android.os.Environment;  
19. import android.os.Looper;  
20. import android.util.Log;  
21. import android.widget.Toast;  
22.   
23. /**
24.  * 自定义系统的Crash捕捉类,用Toast替换系统的对话框
25.  * 将软件版本信息,设备信息,出错信息保存在sd卡中,你可以上传到服务器中
26.  * @author xiaanming
27.  *
28.  */  
29. public class CustomCrashHandler implements UncaughtExceptionHandler {  
30. private static final String TAG = "Activity";  
31. private Context mContext;  
32. private static final String SDCARD_ROOT = Environment.getExternalStorageDirectory().toString();  
33. private static CustomCrashHandler mInstance = new CustomCrashHandler();  
34.       
35.       
36. private CustomCrashHandler(){}  
37. /**
38.      * 单例模式,保证只有一个CustomCrashHandler实例存在
39.      * @return
40.      */  
41. public static CustomCrashHandler getInstance(){  
42. return mInstance;  
43.     }  
44.   
45. /**
46.      * 异常发生时,系统回调的函数,我们在这里处理一些操作
47.      */  
48. @Override  
49. public void uncaughtException(Thread thread, Throwable ex) {  
50. //将一些信息保存到SDcard中  
51.         savaInfoToSD(mContext, ex);  
52.           
53. //提示用户程序即将退出  
54. "很抱歉,程序遭遇异常,即将退出!");  
55. try {  
56. 2000);  
57. catch (InterruptedException e) {  
58.             e.printStackTrace();  
59.         }  
60. //      android.os.Process.killProcess(android.os.Process.myPid());    
61. //        System.exit(1);  
62.           
63. //完美退出程序方法  
64.         ExitAppUtils.getInstance().exit();  
65.           
66.     }  
67.   
68.       
69. /**
70.      * 为我们的应用程序设置自定义Crash处理
71.      */  
72. public void setCustomCrashHanler(Context context){  
73.         mContext = context;  
74. this);  
75.     }  
76.       
77. /**
78.      * 显示提示信息,需要在线程中显示Toast
79.      * @param context
80.      * @param msg
81.      */  
82. private void showToast(final Context context, final String msg){  
83. new Thread(new Runnable() {  
84.               
85. @Override  
86. public void run() {  
87.                 Looper.prepare();  
88.                 Toast.makeText(context, msg, Toast.LENGTH_LONG).show();  
89.                 Looper.loop();  
90.             }  
91.         }).start();  
92.     }  
93.       
94.       
95. /**
96.      * 获取一些简单的信息,软件版本,手机版本,型号等信息存放在HashMap中
97.      * @param context
98.      * @return
99.      */  
100. private HashMap<String, String> obtainSimpleInfo(Context context){  
101. new HashMap<String, String>();  
102.         PackageManager mPackageManager = context.getPackageManager();  
103. null;  
104. try {  
105.             mPackageInfo = mPackageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);  
106. catch (NameNotFoundException e) {  
107.             e.printStackTrace();  
108.         }  
109.           
110. "versionName", mPackageInfo.versionName);  
111. "versionCode", "" + mPackageInfo.versionCode);  
112.           
113. "MODEL", "" + Build.MODEL);  
114. "SDK_INT", "" + Build.VERSION.SDK_INT);  
115. "PRODUCT", "" +  Build.PRODUCT);  
116.           
117. return map;  
118.     }  
119.       
120.       
121. /**
122.      * 获取系统未捕捉的错误信息
123.      * @param throwable
124.      * @return
125.      */  
126. private String obtainExceptionInfo(Throwable throwable) {  
127. new StringWriter();  
128. new PrintWriter(mStringWriter);  
129.         throwable.printStackTrace(mPrintWriter);  
130.         mPrintWriter.close();  
131.           
132.         Log.e(TAG, mStringWriter.toString());  
133. return mStringWriter.toString();  
134.     }  
135.       
136. /**
137.      * 保存获取的 软件信息,设备信息和出错信息保存在SDcard中
138.      * @param context
139.      * @param ex
140.      * @return
141.      */  
142. private String savaInfoToSD(Context context, Throwable ex){  
143. null;  
144. new StringBuffer();  
145.           
146. for (Map.Entry<String, String> entry : obtainSimpleInfo(context).entrySet()) {  
147.             String key = entry.getKey();  
148.             String value = entry.getValue();  
149. " = ").append(value).append("\n");  
150.         }    
151.           
152.         sb.append(obtainExceptionInfo(ex));  
153.           
154. if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){  
155. new File(SDCARD_ROOT + File.separator + "crash" + File.separator);  
156. if(! dir.exists()){  
157.                 dir.mkdir();  
158.             }  
159.               
160. try{  
161. ".log";  
162. new FileOutputStream(fileName);  
163.                 fos.write(sb.toString().getBytes());  
164.                 fos.flush();  
165.                 fos.close();  
166. catch(Exception e){  
167.                 e.printStackTrace();  
168.             }  
169.               
170.         }  
171.           
172. return fileName;  
173.           
174.     }  
175.       
176.       
177. /**
178.      * 将毫秒数转换成yyyy-MM-dd-HH-mm-ss的格式
179.      * @param milliseconds
180.      * @return
181.      */  
182. private String paserTime(long milliseconds) {  
183. "user.timezone", "Asia/Shanghai");  
184. "Asia/Shanghai");  
185.         TimeZone.setDefault(tz);  
186. new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");  
187. new Date(milliseconds));  
188.           
189. return times;  
190.     }  
191. }</span><span style="font-size: 14px;">  
192. </span>



 

上面保存信息到SD卡中需要添加权限<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

 

2.我们需要重写Application类,在onCreate()方法中为它设置Crash处理类

1. package com.example.customcrash;  
2.   
3. import android.app.Application;  
4.   
5. public class CrashApplication extends Application {  
6.   
7. @Override  
8. public void onCreate() {  
9. super.onCreate();  
10.         CustomCrashHandler mCustomCrashHandler = CustomCrashHandler.getInstance();  
11.         mCustomCrashHandler.setCustomCrashHanler(getApplicationContext());  
12.     }  
13.   
14. }

我们需要在AndroidManifest.xml指定Application为CrashApplication,

 

Android 重写系统Crash处理类,保存Crash信息到SD卡 和 完美退出程序的方法_android_04



3.接下来我们写一个MainActivity,它里面存在一个导致程序Crash的空指针异常

 



    1. package com.example.customcrash;  
    2.   
    3. import android.app.Activity;  
    4. import android.os.Bundle;  
    5. import android.widget.TextView;  
    6.   
    7. public class MainActivity extends Activity {  
    8.     TextView mTextView;  
    9.   
    10. @Override  
    11. protected void onCreate(Bundle savedInstanceState) {  
    12. super.onCreate(savedInstanceState);  
    13.         setContentView(R.layout.activity_main);  
    14.           
    15. "crash");  
    16.     }  
    17. }


    运行程序,你会发现一句很友好的“很抱歉,程序遭遇异常,即将退出”代替了冷冰冰的警告框,我们打开DDMS,在mnt/sdcard/Crash/目录下面发现了有一个文件,打开文件,我们可以看到

     

      1. versionCode = 1  
      2. PRODUCT = sdk  
      3. MODEL = sdk  
      4. versionName = 1.0  
      5. SDK_INT = 8  
      6. java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.customcrash/com.example.customcrash.MainActivity}: java.lang.NullPointerException  
      7.     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2663)  
      8.     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679)  
      9.     at android.app.ActivityThread.access$2300(ActivityThread.java:125)  
      10.     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2033)  
      11.     at android.os.Handler.dispatchMessage(Handler.java:99)  
      12.     at android.os.Looper.loop(Looper.java:123)  
      13.     at android.app.ActivityThread.main(ActivityThread.java:4627)  
      14.     at java.lang.reflect.Method.invokeNative(Native Method)  
      15.     at java.lang.reflect.Method.invoke(Method.java:521)  
      16.     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)  
      17.     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)  
      18.     at dalvik.system.NativeStart.main(Native Method)  
      19. Caused by: java.lang.NullPointerException  
      20.     at com.example.customcrash.MainActivity.onCreate(MainActivity.java:15)  
      21.     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)  
      22.     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627)  
      23.     ... 11 more


      说到这里,算是说完了,接下来我们只需要将我们的错误文件上传到服务器就行了。

       

       

      感谢4楼的朋友提出文章的bug,遭遇异常的时候程序退不出去,然后我写了一个程序退出的工具类,直接用这个工具类就能很好在任何地方退出程序,工具类如下

       

      1. <span style="font-size:12px;">package com.example.customcrash;  
      2.   
      3. import java.util.LinkedList;  
      4. import java.util.List;  
      5.   
      6. import android.app.Activity;  
      7.   
      8. /**
      9.  * android退出程序的工具类,使用单例模式
      10.  * 1.在Activity的onCreate()的方法中调用addActivity()方法添加到mActivityList
      11.  * 2.你可以在Activity的onDestroy()的方法中调用delActivity()来删除已经销毁的Activity实例
      12.  * 这样避免了mActivityList容器中有多余的实例而影响程序退出速度
      13.  * @author xiaanming
      14.  *
      15.  */  
      16. public class ExitAppUtils {  
      17. /**
      18.      * 转载Activity的容器
      19.      */  
      20. private List<Activity> mActivityList = new LinkedList<Activity>();  
      21. private static ExitAppUtils instance = new ExitAppUtils();  
      22.       
      23. /**
      24.      * 将构造函数私有化
      25.      */  
      26. private ExitAppUtils(){};  
      27.       
      28. /**
      29.      * 获取ExitAppUtils的实例,保证只有一个ExitAppUtils实例存在
      30.      * @return
      31.      */  
      32. public static ExitAppUtils getInstance(){  
      33. return instance;  
      34.     }  
      35.   
      36.       
      37. /**
      38.      * 添加Activity实例到mActivityList中,在onCreate()中调用
      39.      * @param activity
      40.      */  
      41. public void addActivity(Activity activity){  
      42.         mActivityList.add(activity);  
      43.     }  
      44.       
      45. /**
      46.      * 从容器中删除多余的Activity实例,在onDestroy()中调用
      47.      * @param activity
      48.      */  
      49. public void delActivity(Activity activity){  
      50.         mActivityList.remove(activity);  
      51.     }  
      52.       
      53.       
      54. /**
      55.      * 退出程序的方法
      56.      */  
      57. public void exit(){  
      58. for(Activity activity : mActivityList){  
      59.             activity.finish();  
      60.         }  
      61.           
      62. 0);  
      63.     }  
      64.       
      65.   
      66. }</span><span style="font-size: 14px;">  
      67. </span>

       退出工具类的使用我在代码中说的还比较清楚,相信你很容易使用,然后将CustomCrashHandler类uncaughtException(Thread thread, Throwable ex)方法中的

      注:如果你的工程有很多个Activity,你需要在每一个Activity的onCreate()和onDestroy()调用addActivity()和delActivity()方法,这样子是不是显得很多余?你可以写一个BaseActivity继承Activity,然后再BaseActivity的onCreate()和onDestroy()调用addActivity()和delActivity()方法,其他的Activity继承BaseActivity就行了

       


       


      1. android.os.Process.killProcess(android.os.Process.myPid());    
      2. 1);

      替换成

       

       

       

      1. ExitAppUtils.getInstance().exit();

      举报

      相关推荐

      0 条评论