学习和使用程序捕捉异常 CrashHandler以及ACRA

来源:互联网 发布:地质中学校花聂知 编辑:程序博客网 时间:2024/06/03 19:12

今天,我来回顾一下如何友好的处理程序异常。

目前,我学习两种方法去实现友好界面处理异常

方法一:CrashHandler(老的使用办法)

方法二:ACRA(Google推荐)

1.CrashHandler如何使用?

1.1CrashHandler类

import java.io.BufferedWriter;import java.io.File;import java.io.FileWriter;import java.io.PrintWriter;import java.io.StringWriter;import java.io.Writer;import java.lang.Thread.UncaughtExceptionHandler;import java.lang.reflect.Field;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Date;import java.util.HashMap;import java.util.LinkedList;import java.util.List;import java.util.Map;import android.annotation.SuppressLint;import android.app.Activity;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.util.Log;@SuppressLint("SimpleDateFormat")public class CrashHandler implements UncaughtExceptionHandler {public static final String TAG = "CrashHandler";List<String>  filepath=new ArrayList<String>();// 系统默认的UncaughtException处理类private UncaughtExceptionHandler mDefaultHandler;// CrashHandler实例private static CrashHandler INSTANCE = new CrashHandler();// 程序的Context对象private Context mContext;// 用来存储设备信息和异常信息private Map<String, String> infos = new HashMap<String, String>();CrashLogUpload crashLogUpload = new CrashLogUpload();// 用于格式化日期,作为日志文件名的一部分private DateFormat formatter = new SimpleDateFormat("MMddHHmm");public static List<CrashLogUpload>  crashLogUploadList=new ArrayList<CrashLogUpload>(); ;private List<Activity> activityList = new LinkedList<Activity>();/** 保证只有一个CrashHandler实例 */private CrashHandler() {}/** 获取CrashHandler实例 ,单例模式 */public static CrashHandler getInstance() {return INSTANCE;}/** * 初始化 *  * @param context */public void init(Context context) {mContext = context;// 获取系统默认的UncaughtException处理器mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();// 设置该CrashHandler为程序的默认处理器Thread.setDefaultUncaughtExceptionHandler(this);}    /**     * 这个是最关键的函数,当程序中有未被捕获的异常,系统将会自动调用#uncaughtException方法     * thread为出现未捕获异常的线程,ex为未捕获的异常,有了这个ex,我们就可以得到异常信息。     */@Overridepublic void uncaughtException(Thread thread, Throwable ex) {if (!handleException(ex) && mDefaultHandler != null) {// 如果用户没有处理则让系统默认的异常处理器来处理mDefaultHandler.uncaughtException(thread, ex);} else {// 退出程序try {Thread.sleep(3000);} catch (InterruptedException e) {Log.e(TAG, "error : ", e);}exit();}}/** * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. *  * @param ex * @return true:如果处理了该异常信息;否则返回false. */private boolean handleException(Throwable ex) {if (ex == null) {return false;}// 收集设备参数信息collectDeviceInfo(mContext);// 保存日志文件saveCrashInfo2File(ex);return true;}/** * 收集设备参数信息 *  * @param ctx */public void collectDeviceInfo(Context ctx) {try {PackageManager pm = ctx.getPackageManager();PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),PackageManager.GET_ACTIVITIES);if (pi != null) {String versionName = pi.versionName == null ? "null": pi.versionName;String versionCode = pi.versionCode + "";infos.put("versionName", versionName);infos.put("versionCode", versionCode);}} catch (NameNotFoundException e) {Log.e(TAG, "an error occured when collect package info", e);}Field[] fields = Build.class.getDeclaredFields();for (Field field : fields) {try {field.setAccessible(true);infos.put(field.getName(), field.get(null).toString());Log.d(TAG, field.getName() + " : " + field.get(null));} catch (Exception e) {Log.e(TAG, "an error occured when collect crash info", e);}}}/** * 保存错误信息到文件中 *  * @param ex * @return 返回文件名称,便于将文件传送到服务器 */private String saveCrashInfo2File(Throwable ex) {StringBuffer sb = new StringBuffer();for (Map.Entry<String, String> entry : infos.entrySet()) {String key = entry.getKey();String value = entry.getValue();sb.append(key + "=" + value + "\n");}Writer writer = new StringWriter();PrintWriter printWriter = new PrintWriter(writer);ex.printStackTrace(printWriter);Throwable cause = ex.getCause();while (cause != null) {cause.printStackTrace(printWriter);cause = cause.getCause();}printWriter.close();String result = writer.toString();sb.append(result);try {String time = formatter.format(new Date());String fileName = "crash" + time +".log";if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {String path = getOutputLoggerFileFloder();File dir = new File(path);if (!dir.exists()) {dir.mkdirs();}File file = new File(path + File.separator + fileName);FileWriter filerWriter = new FileWriter(file, true);// 后面这个参数代表是不是要接上文件中原来的数据,不进行覆盖BufferedWriter bufWriter = new BufferedWriter(filerWriter);bufWriter.write(sb.toString());bufWriter.newLine();bufWriter.close();filerWriter.close();}} catch (Exception e) {Log.e(TAG, "an error occured while writing file...", e);}return null;}private String getOutputLoggerFileFloder() {File directory = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator + "app_log" + File.separator + "Loggers");if (!directory.exists()) {directory.mkdirs();}return directory.getPath();}public void addActivity(Activity activity) {activityList.add(activity);}// 遍历所有Activity并finishpublic void exit() {for (Activity activity : activityList) {activity.finish();}android.os.Process.killProcess(android.os.Process.myPid());System.exit(1);}}

1.2打印日志类

import java.util.Date;public class CrashLogUpload {private String id;private String fileName;private String filePath;private Date recordTime;private Boolean hasUpload;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getFileName() {return fileName;}public void setFileName(String fileName) {this.fileName = fileName;}public String getFilePath() {return filePath;}public void setFilePath(String filePath) {this.filePath = filePath;}public Date getRecordTime() {return recordTime;}public void setRecordTime(Date recordTime) {this.recordTime = recordTime;}public Boolean getHasUpload() {return hasUpload;}public void setHasUpload(Boolean hasUpload) {this.hasUpload = hasUpload;} }

1.3在appliaction的oncrate()方法中初始化

        //在这里为应用设置异常处理程序,然后我们的程序才能捕获未处理的异常        CrashHandler crashHandler = CrashHandler.getInstance();        crashHandler.init(getApplicationContext());

2.如何使用Acra捕捉异常

2.1下载Acra架包,as library

下载地址:http://pan.baidu.com/s/1kVydkjl,然后考入lib中,右击as library

2.1 application中初始化

  ACRA.init(this);
可根据自身需求设置

有弹出框提示,有保存本地提示,有上传信息到服务器,有吐司等

最基本的

 @ReportsCrashes(        formKey = "", // This is required for backward compatibility but not used        formUri = "http://www.backendofyourchoice.com/reportpath"    )

我们可以根据源码中和自己的需求来运用

ReportsCrashes源码:

//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package org.acra.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Inherited;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.acra.ReportField;import org.acra.ReportingInteractionMode;import org.acra.sender.HttpSender.Method;import org.acra.sender.HttpSender.Type;@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})@Inheritedpublic @interface ReportsCrashes {    String formKey();    String formUri() default "";    ReportingInteractionMode mode() default ReportingInteractionMode.SILENT;    int resDialogCommentPrompt() default 0;    int resDialogEmailPrompt() default 0;    int resDialogIcon() default 17301543;    int resDialogOkToast() default 0;    int resDialogText() default 0;    int resDialogTitle() default 0;    int resNotifIcon() default 17301624;    int resNotifText() default 0;    int resNotifTickerText() default 0;    int resNotifTitle() default 0;    int resToastText() default 0;    String sharedPreferencesName() default "";    int sharedPreferencesMode() default 0;    boolean includeDropBoxSystemTags() default false;    String[] additionalDropBoxTags() default {};    int dropboxCollectionMinutes() default 5;    String[] logcatArguments() default {"-t", "100", "-v", "time"};    String formUriBasicAuthLogin() default "ACRA-NULL-STRING";    String formUriBasicAuthPassword() default "ACRA-NULL-STRING";    ReportField[] customReportContent() default {};    String mailTo() default "";    boolean deleteUnapprovedReportsOnApplicationStart() default true;    boolean deleteOldUnsentReportsOnApplicationStart() default true;    int connectionTimeout() default 3000;    int socketTimeout() default 5000;    int maxNumberOfRequestRetries() default 3;    boolean forceCloseDialogAfterToast() default false;    String[] additionalSharedPreferences() default {};    boolean logcatFilterByPid() default false;    boolean sendReportsInDevMode() default true;    String[] excludeMatchingSharedPreferencesKeys() default {};    String[] excludeMatchingSettingsKeys() default {};    String applicationLogFile() default "";    int applicationLogFileLines() default 100;    String googleFormUrlFormat() default "https://docs.google.com/spreadsheet/formResponse?formkey=%s&ifq";    boolean disableSSLCertValidation() default false;    Method httpMethod() default Method.POST;    Type reportType() default Type.FORM;}


ACRA源码:
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package org.acra;import android.app.Application;import android.content.SharedPreferences;import android.content.SharedPreferences.OnSharedPreferenceChangeListener;import android.content.pm.PackageManager;import android.content.pm.PackageManager.NameNotFoundException;import android.preference.PreferenceManager;import org.acra.ACRAConfiguration;import org.acra.ACRAConfigurationException;import org.acra.ErrorReporter;import org.acra.annotation.ReportsCrashes;import org.acra.log.ACRALog;import org.acra.log.AndroidLogDelegate;public class ACRA {    public static final boolean DEV_LOGGING = false;    public static final String LOG_TAG = ACRA.class.getSimpleName();    public static ACRALog log = new AndroidLogDelegate();    public static final String PREF_DISABLE_ACRA = "acra.disable";    public static final String PREF_ENABLE_ACRA = "acra.enable";    public static final String PREF_ENABLE_SYSTEM_LOGS = "acra.syslog.enable";    public static final String PREF_ENABLE_DEVICE_ID = "acra.deviceid.enable";    public static final String PREF_USER_EMAIL_ADDRESS = "acra.user.email";    public static final String PREF_ALWAYS_ACCEPT = "acra.alwaysaccept";    public static final String PREF_LAST_VERSION_NR = "acra.lastVersionNr";    private static Application mApplication;    private static ReportsCrashes mReportsCrashes;    private static ErrorReporter errorReporterSingleton;    private static OnSharedPreferenceChangeListener mPrefListener;    private static ACRAConfiguration configProxy;    public ACRA() {    }    public static void <span style="color:#ff0000;">init</span>(Application app) {        if(mApplication != null) {            log.w(LOG_TAG, "ACRA#init called more than once. Won\'t do anything more.");        } else {            mApplication = app;            mReportsCrashes = (ReportsCrashes)mApplication.getClass().getAnnotation(ReportsCrashes.class);            if(mReportsCrashes == null) {                log.e(LOG_TAG, "ACRA#init called but no ReportsCrashes annotation on Application " + mApplication.getPackageName());            } else {                SharedPreferences prefs = getACRASharedPreferences();                try {                    checkCrashResources();                    log.d(LOG_TAG, "ACRA is enabled for " + mApplication.getPackageName() + ", intializing...");                    boolean e = !shouldDisableACRA(prefs);                    ErrorReporter errorReporter = new ErrorReporter(mApplication, prefs, e);                    errorReporter.setDefaultReportSenders();                    errorReporterSingleton = errorReporter;                } catch (ACRAConfigurationException var4) {                    log.w(LOG_TAG, "Error : ", var4);                }                mPrefListener = new OnSharedPreferenceChangeListener() {                    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {                        if("acra.disable".equals(key) || "acra.enable".equals(key)) {                            boolean enableAcra = !ACRA.shouldDisableACRA(sharedPreferences);                            ACRA.getErrorReporter().setEnabled(enableAcra);                        }                    }                };                prefs.registerOnSharedPreferenceChangeListener(mPrefListener);            }        }    }    public static ErrorReporter getErrorReporter() {        if(errorReporterSingleton == null) {            throw new IllegalStateException("Cannot access ErrorReporter before ACRA#init");        } else {            return errorReporterSingleton;        }    }    private static boolean shouldDisableACRA(SharedPreferences prefs) {        boolean disableAcra = false;        try {            boolean e = prefs.getBoolean("acra.enable", true);            disableAcra = prefs.getBoolean("acra.disable", !e);        } catch (Exception var3) {            ;        }        return disableAcra;    }    static void checkCrashResources() throws ACRAConfigurationException {        ACRAConfiguration conf = getConfig();        switch(ACRA.SyntheticClass_1.$SwitchMap$org$acra$ReportingInteractionMode[conf.mode().ordinal()]) {        case 1:            if(conf.resToastText() == 0) {                throw new ACRAConfigurationException("TOAST mode: you have to define the resToastText parameter in your application @ReportsCrashes() annotation.");            }            break;        case 2:            if(conf.resNotifTickerText() == 0 || conf.resNotifTitle() == 0 || conf.resNotifText() == 0 || conf.resDialogText() == 0) {                throw new ACRAConfigurationException("NOTIFICATION mode: you have to define at least the resNotifTickerText, resNotifTitle, resNotifText, resDialogText parameters in your application @ReportsCrashes() annotation.");            }            break;        case 3:            if(conf.resDialogText() == 0) {                throw new ACRAConfigurationException("DIALOG mode: you have to define at least the resDialogText parameters in your application @ReportsCrashes() annotation.");            }        }    }    public static SharedPreferences getACRASharedPreferences() {        ACRAConfiguration conf = getConfig();        return !"".equals(conf.sharedPreferencesName())?mApplication.getSharedPreferences(conf.sharedPreferencesName(), conf.sharedPreferencesMode()):PreferenceManager.getDefaultSharedPreferences(mApplication);    }    public static ACRAConfiguration getConfig() {        if(configProxy == null) {            if(mApplication == null) {                log.w(LOG_TAG, "Calling ACRA.getConfig() before ACRA.init() gives you an empty configuration instance. You might prefer calling ACRA.getNewDefaultConfig(Application) to get an instance with default values taken from a @ReportsCrashes annotation.");            }            configProxy = getNewDefaultConfig(mApplication);        }        return configProxy;    }    public static void setConfig(ACRAConfiguration conf) {        configProxy = conf;    }    public static ACRAConfiguration getNewDefaultConfig(Application app) {        return app != null?new ACRAConfiguration((ReportsCrashes)app.getClass().getAnnotation(ReportsCrashes.class)):new ACRAConfiguration((ReportsCrashes)null);    }    static boolean isDebuggable() {        PackageManager pm = mApplication.getPackageManager();        try {            return (pm.getApplicationInfo(mApplication.getPackageName(), 0).flags & 2) > 0;        } catch (NameNotFoundException var2) {            return false;        }    }    static Application getApplication() {        return mApplication;    }    public static void setLog(ACRALog log) {        log = log;    }}

同时,我们也可以自定义方法去实现实现显示的方式。






0 0
原创粉丝点击