A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

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

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

1.我们新建一个CustomCrashHandler类 实现UncaughtExceptionHandler接口,重写回调方法void uncaughtException(Thread thread, Throwable ex)
[java] view plaincopy
<span style="font-size:12px;">package com.example.customcrash;  
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<String, String> obtainSimpleInfo(Context context){  
        HashMap<String, String> map = new HashMap<String, String>();  
        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<String, String> 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;  
    }  
}</span><span style="font-size: 14px;">  
</span>  


上面保存信息到SD卡中需要添加权限<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
2.我们需要重写Application类,在onCreate()方法中为它设置Crash处理类
[java] view plaincopy
package com.example.customcrash;  
  
import android.app.Application;  
  
public class CrashApplication extends Application {  
  

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马