博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android_ 重写系统Crash处理类,保存Crash信息到SD卡 和 完美退出程序的方法
阅读量:4689 次
发布时间:2019-06-09

本文共 10299 字,大约阅读时间需要 34 分钟。

转载时注明地址:

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

图一图二

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

先看下项目结构

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

import java.io.File;import java.io.FileOutputStream;import java.io.PrintWriter;import java.io.StringWriter;import java.lang.Thread.UncaughtExceptionHandler;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.Map;import java.util.TimeZone;import android.content.Context;import android.content.pm.PackageInfo;import android.content.pm.PackageManager;import android.content.pm.PackageManager.NameNotFoundException;import android.os.Build;import android.os.Environment;import android.os.Looper;import android.util.Log;import android.widget.Toast;/** * 自定义系统的Crash捕捉类,用Toast替换系统的对话框 * 将软件版本信息,设备信息,出错信息保存在sd卡中,你可以上传到服务器中 * @author xiaanming * */public class CustomCrashHandler implements UncaughtExceptionHandler {    private static final String TAG = "Activity";    private Context mContext;    private static final String SDCARD_ROOT = Environment.getExternalStorageDirectory().toString();    private static CustomCrashHandler mInstance = new CustomCrashHandler();            private CustomCrashHandler(){}    /**     * 单例模式,保证只有一个CustomCrashHandler实例存在     * @return     */    public static CustomCrashHandler getInstance(){        return mInstance;    }    /**     * 异常发生时,系统回调的函数,我们在这里处理一些操作     */    @Override    public void uncaughtException(Thread thread, Throwable ex) {        //将一些信息保存到SDcard中        savaInfoToSD(mContext, ex);                //提示用户程序即将退出        showToast(mContext, "很抱歉,程序遭遇异常,即将退出!");        try {            thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }//        android.os.Process.killProcess(android.os.Process.myPid());  //        System.exit(1);                //完美退出程序方法        ExitAppUtils.getInstance().exit();            }        /**     * 为我们的应用程序设置自定义Crash处理     */    public void setCustomCrashHanler(Context context){        mContext = context;        Thread.setDefaultUncaughtExceptionHandler(this);    }        /**     * 显示提示信息,需要在线程中显示Toast     * @param context     * @param msg     */    private void showToast(final Context context, final String msg){        new Thread(new Runnable() {                        @Override            public void run() {                Looper.prepare();                Toast.makeText(context, msg, Toast.LENGTH_LONG).show();                Looper.loop();            }        }).start();    }            /**     * 获取一些简单的信息,软件版本,手机版本,型号等信息存放在HashMap中     * @param context     * @return     */    private HashMap
obtainSimpleInfo(Context context){ HashMap
map = new HashMap
(); PackageManager mPackageManager = context.getPackageManager(); PackageInfo mPackageInfo = null; try { mPackageInfo = mPackageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES); } catch (NameNotFoundException e) { e.printStackTrace(); } map.put("versionName", mPackageInfo.versionName); map.put("versionCode", "" + mPackageInfo.versionCode); map.put("MODEL", "" + Build.MODEL); map.put("SDK_INT", "" + Build.VERSION.SDK_INT); map.put("PRODUCT", "" + Build.PRODUCT); return map; } /** * 获取系统未捕捉的错误信息 * @param throwable * @return */ private String obtainExceptionInfo(Throwable throwable) { StringWriter mStringWriter = new StringWriter(); PrintWriter mPrintWriter = new PrintWriter(mStringWriter); throwable.printStackTrace(mPrintWriter); mPrintWriter.close(); Log.e(TAG, mStringWriter.toString()); return mStringWriter.toString(); } /** * 保存获取的 软件信息,设备信息和出错信息保存在SDcard中 * @param context * @param ex * @return */ private String savaInfoToSD(Context context, Throwable ex){ String fileName = null; StringBuffer sb = new StringBuffer(); for (Map.Entry
entry : obtainSimpleInfo(context).entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(key).append(" = ").append(value).append("\n"); } sb.append(obtainExceptionInfo(ex)); if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ File dir = new File(SDCARD_ROOT + File.separator + "crash" + File.separator); if(! dir.exists()){ dir.mkdir(); } try{ fileName = dir.toString() + File.separator + paserTime(System.currentTimeMillis()) + ".log"; FileOutputStream fos = new FileOutputStream(fileName); fos.write(sb.toString().getBytes()); fos.flush(); fos.close(); }catch(Exception e){ e.printStackTrace(); } } return fileName; } /** * 将毫秒数转换成yyyy-MM-dd-HH-mm-ss的格式 * @param milliseconds * @return */ private String paserTime(long milliseconds) { System.setProperty("user.timezone", "Asia/Shanghai"); TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai"); TimeZone.setDefault(tz); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); String times = format.format(new Date(milliseconds)); return times; }}

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

 

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

import android.app.Application;

public class CrashApplication extends Application {

@Override

public void onCreate() {
super.onCreate();
CustomCrashHandler mCustomCrashHandler = CustomCrashHandler.getInstance();
mCustomCrashHandler.setCustomCrashHanler(getApplicationContext());
}

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

 

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

package com.example.customcrash;import android.app.Activity;import android.os.Bundle;import android.widget.TextView;public class MainActivity extends Activity {	TextView mTextView;	@Override	protected void onCreate(Bundle savedInstanceState) {		super.onCreate(savedInstanceState);		setContentView(R.layout.activity_main);				mTextView.setText("crash");	}}

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

versionCode = 1

PRODUCT = sdk
MODEL = sdk
versionName = 1.0
SDK_INT = 8
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.customcrash/com.example.customcrash.MainActivity}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2663)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679)
at android.app.ActivityThread.access$2300(ActivityThread.java:125)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2033)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4627)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at com.example.customcrash.MainActivity.onCreate(MainActivity.java:15)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627)
... 11 more

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

 

 

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

import java.util.LinkedList;import java.util.List;import android.app.Activity;/** * android退出程序的工具类,使用单例模式 * 1.在Activity的onCreate()的方法中调用addActivity()方法添加到mActivityList * 2.你可以在Activity的onDestroy()的方法中调用delActivity()来删除已经销毁的Activity实例 * 这样避免了mActivityList容器中有多余的实例而影响程序退出速度 * @author xiaanming * */public class ExitAppUtils {    /**     * 转载Activity的容器     */    private List
mActivityList = new LinkedList
(); private static ExitAppUtils instance = new ExitAppUtils(); /** * 将构造函数私有化 */ private ExitAppUtils(){}; /** * 获取ExitAppUtils的实例,保证只有一个ExitAppUtils实例存在 * @return */ public static ExitAppUtils getInstance(){ return instance; } /** * 添加Activity实例到mActivityList中,在onCreate()中调用 * @param activity */ public void addActivity(Activity activity){ mActivityList.add(activity); } /** * 从容器中删除多余的Activity实例,在onDestroy()中调用 * @param activity */ public void delActivity(Activity activity){ mActivityList.remove(activity); } /** * 退出程序的方法 */ public void exit(){ for(Activity activity : mActivityList){ activity.finish(); } System.exit(0); } }

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

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

 

[java] 
 
  1. android.os.Process.killProcess(android.os.Process.myPid());    
  2.         System.exit(1);  

替换成

 

 

[java] 
 
  1. ExitAppUtils.getInstance().exit();  

 

 

如果觉得这篇文章对你有用,你就帮我顶一下,谢谢!

转载于:https://www.cnblogs.com/changkai244/p/4110371.html

你可能感兴趣的文章
Linux下tar,zip命令详解
查看>>
C#垃圾回收机制
查看>>
31、任务三十一——表单联动
查看>>
python之hasattr、getattr和setattr函数
查看>>
maven使用阿里镜像配置文件
查看>>
Copy code from eclipse to word, save syntax.
查看>>
arguments.callee的作用及替换方案
查看>>
23 Java学习之RandomAccessFile
查看>>
P2709 小B的询问
查看>>
润乾报表 动态控制文本的显示
查看>>
[oracle] 如何使用myBatis在数据库中插入数据并返回主键
查看>>
PHP echo 和 print 语句
查看>>
第一讲 一个简单的Qt程序分析
查看>>
Centos 6.5下的OPENJDK卸载和SUN的JDK安装、环境变量配置
查看>>
poj 1979 Red and Black(dfs)
查看>>
【.Net基础03】HttpWebRequest模拟浏览器登陆
查看>>
UML-画类图与交互图的顺序
查看>>
6月7 考试系统
查看>>
mysql 基本操作
查看>>
zTree async 动态参数处理
查看>>