Android 如何自定义CrashHandler与崩溃后自动重启
2017-06-10 21:58
429 查看
为什么要捕获Crash信息:
开发阶段,我们会使用Monitor查看logcat 信息,但由于很多原因,Android Monitor抓不到logcat信息。 因此需要一个CrashHandler来将Crash写入到本地或者上传到服务器方便我们分析。
线上应用:
崩溃率是衡量应用稳定性的重要标准,那么应用上线以后 我们无法向用户借手机来分析崩溃原因。为了减低崩溃率,这个时候需要CrashHandler 来帮我们将崩溃信息返回给后台,以便及时修复。
如何捕获捕获Crash分如下三步:
1、实现Thread.UncaughtExceptionHandler接口,并重写uncaughtException方法,在uncaughtException 方法中接收异常信息
2、调用Thread.setDefaultUncaughtExceptionHandler(CrashHandler) ,来使用我们自定义的CrashHandler来取代系统默认的CrashHandler
3、异常信息的获取、本地写入以及异常信息的上传
首先贴自定义的RCrashHandler
/**
* Created by Owen Chan
* On 2017-06-07.
*/
Application 中的初始化:
开发阶段,我们会使用Monitor查看logcat 信息,但由于很多原因,Android Monitor抓不到logcat信息。 因此需要一个CrashHandler来将Crash写入到本地或者上传到服务器方便我们分析。
线上应用:
崩溃率是衡量应用稳定性的重要标准,那么应用上线以后 我们无法向用户借手机来分析崩溃原因。为了减低崩溃率,这个时候需要CrashHandler 来帮我们将崩溃信息返回给后台,以便及时修复。
如何捕获捕获Crash分如下三步:
1、实现Thread.UncaughtExceptionHandler接口,并重写uncaughtException方法,在uncaughtException 方法中接收异常信息
2、调用Thread.setDefaultUncaughtExceptionHandler(CrashHandler) ,来使用我们自定义的CrashHandler来取代系统默认的CrashHandler
3、异常信息的获取、本地写入以及异常信息的上传
首先贴自定义的RCrashHandler
/**
* Created by Owen Chan
* On 2017-06-07.
*/
public class CrashHandler implements Thread.UncaughtExceptionHandler { private static CrashHandler INSTANCE; private static Context mContext; private static PendingIntent restartIntent; private Thread.UncaughtExceptionHandler mDefaultHandler; private CrashUploader crashUploader; private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); public static final String EXCEPTION_INFO = "EXCEPTION_INFO"; public static final String PACKAGE_INFO = "PACKAGE_INFO"; public static final String DEVICE_INFO = "DEVICE_INFO"; public static final String SYSTEM_INFO = "SYSTEM_INFO"; public static final String SECURE_INFO = "SECURE_INFO"; public static final String MEM_INFO = "MEM_INFO"; private String mExceptionInfo; private String mMemInfo; private ConcurrentHashMap<String, String> mPackageInfo = new ConcurrentHashMap<>(); private ConcurrentHashMap<String, String> mDeviceInfo = new ConcurrentHashMap<>(); private ConcurrentHashMap<String, String> mSystemInfo = new ConcurrentHashMap<>(); private ConcurrentHashMap<String, String> mSecureInfo = new ConcurrentHashMap<>(); private ConcurrentHashMap<String, Object> totalInfo = new ConcurrentHashMap<>(); private CrashHandler() { } public static CrashHandler getInstance() { if (INSTANCE == null) { synchronized (CrashHandler.class) { if (INSTANCE == null) { INSTANCE = new CrashHandler(); 10a4d } } } return INSTANCE; } public void init(Context context, CrashUploader crashUploader, PendingIntent pendingIntent) { mContext = context; this.crashUploader = crashUploader; this.restartIntent = pendingIntent; //保存一份系统默认的CrashHandler mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); //使用我们自定义的异常处理器替换程序默认的 Thread.setDefaultUncaughtExceptionHandler(this); } /** * @param t 出现未捕获异常的线程 * @param e 未捕获的异常,有了这个ex,我们就可以得到异常信息 */ @Override public void uncaughtException(Thread t, Throwable e) { if (!catchCrashException(e) && mDefaultHandler != null) { //没有自定义的CrashHandler的时候就调用系统默认的异常处理方式 mDefaultHandler.uncaughtException(t, e); } else { //退出应用 killProcess(); } } /** * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. * * @param ex 未捕获的异常 * @return true:如果处理了该异常信息;否则返回false. */ private boolean catchCrashException(Throwable ex) { if (ex == null) { return false; } new Thread() { @Override public void run() { Looper.prepare(); Toast.makeText(ClientApp.getInstance().getApplicationContext(), "Duang~~崩啦~~崩啦~~~~", Toast.LENGTH_SHORT).show(); Looper.loop(); } }.start(); collectInfo(ex); //保存日志文件 saveCrashInfo2File(); //上传崩溃信息 uploadCrashMessage(totalInfo); return true; } /** * 退出应用 */ private static void killProcess() { try { Thread.sleep(1000); } catch (InterruptedException e) { Log.e("application", "error : ", e); } // 退出程序 AlarmManager mgr = (AlarmManager) ClientApp.getInstance().getApplicationContext().getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 2000, restartIntent); // 2秒钟后重启应用 Process.killProcess(Process.myPid()); System.exit(1); } /** * 获取异常信息和设备参数信息 */ private void collectInfo(Throwable ex) { mExceptionInfo = collectExceptionInfo(ex); collectPackageInfo(); collectBuildInfos(); collectSystemInfos(); collectSecureInfo(); mMemInfo = collectMemInfo(); totalInfo.put(EXCEPTION_INFO, mExceptionInfo); totalInfo.put(PACKAGE_INFO, mPackageInfo); totalInfo.put(DEVICE_INFO, mDeviceInfo); totalInfo.put(SYSTEM_INFO, mSecureInfo); totalInfo.put(SECURE_INFO, mSecureInfo); totalInfo.put(MEM_INFO, MEM_INFO); } /** * 获取捕获异常的信息 */ private String collectExceptionInfo(Throwable ex) { Writer mWriter = new StringWriter(); PrintWriter mPrintWriter = new PrintWriter(mWriter); ex.printStackTrace(mPrintWriter); ex.printStackTrace(); Throwable mThrowable = ex.getCause(); // 迭代栈队列把所有的异常信息写入writer中 while (mThrowable != null) { mThrowable.printStackTrace(mPrintWriter); // 换行 每个个异常栈之间换行 mPrintWriter.append("\r\n"); mThrowable = mThrowable.getCause(); } // 记得关闭 mPrintWriter.close(); return mWriter.toString(); } /** * 获取应用包参数信息 */ private void collectPackageInfo() { try { // 获得包管理器 PackageManager mPackageManager = mContext.getPackageManager(); // 得到该应用的信息,即主Activity PackageInfo mPackageInfo = mPackageManager.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES); if (mPackageInfo != null) { String versionName = mPackageInfo.versionName == null ? "null" : mPackageInfo.versionName; String versionCode = mPackageInfo.versionCode + ""; this.mPackageInfo.put("VersionName", versionName); this.mPackageInfo.put("VersionCode", versionCode); } } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } /** * 从系统属性中提取设备硬件和版本信息 */ private void collectBuildInfos() { // 反射机制 Field[] mFields = Build.class.getDeclaredFields(); // 迭代Build的字段key-value 此处的信息主要是为了在服务器端手机各种版本手机报错的原因 for (Field field : mFields) { try { field.setAccessible(true); mDeviceInfo.put(field.getName(), field.get("").toString()); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } /** * 获取系统常规设定属性 */ private void collectSystemInfos() { Field[] fields = Settings.System.class.getFields(); for (Field field : fields) { if (!field.isAnnotationPresent(Deprecated.class) && field.getType() == String.class) { try { String value = Settings.System.getString(mContext.getContentResolver(), (String) field.get(null)); if (value != null) { mSystemInfo.put(field.getName(), value); } } catch (IllegalAccessException e) { e.printStackTrace(); } } } } /** * 获取系统安全设置信息 */ private void collectSecureInfo() { Field[] fields = Settings.Secure.class.getFields(); for (Field field : fields) { if (!field.isAnnotationPresent(Deprecated.class) && field.getType() == String.class && field.getName().startsWith("WIFI_AP")) { try { String value = Settings.Secure.getString(mContext.getContentResolver(), (String) field.get(null)); if (value != null) { mSecureInfo.put(field.getName(), value); } } catch (IllegalAccessException e) { e.printStackTrace(); } } } } /** * 获取内存信息 */ private String collectMemInfo() { BufferedReader br = null; StringBuffer sb = new StringBuffer(); ArrayList<String> commandLine = new ArrayList<>(); commandLine.add("cat"); commandLine.add("/proc/meminfo"); commandLine.add(Integer.toString(Process.myPid())); try { java.lang.Process process = Runtime.getRuntime() .exec(commandLine.toArray(new String[commandLine.size()])); br = new BufferedReader(new InputStreamReader(process.getInputStream()), 1024); while (true) { String line = br.readLine(); if (line == null) { break; } sb.append(line); sb.append("\n"); } } catch (IOException e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); } /** * 将崩溃日志信息写入本地文件 */ private String saveCrashInfo2File() { StringBuffer mStringBuffer = getInfoStr(mPackageInfo); mStringBuffer.append(mExceptionInfo); // 保存文件,设置文件名 String mTime = formatter.format(new Date()); String mFileName = "crash-" + mTime + ".log"; if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { try { File mDirectory = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/hive-crash"); if (!mDirectory.exists()) { boolean success = mDirectory.mkdirs(); } FileOutputStream mFileOutputStream = new FileOutputStream(mDirectory + File.separator + mFileName); mFileOutputStream.write(mStringBuffer.toString().getBytes()); mFileOutputStream.close(); return mFileName; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return null; } /** * 将HashMap遍历转换成StringBuffer */ @NonNull private static StringBuffer getInfoStr(ConcurrentHashMap<String, String> info) { StringBuffer mStringBuffer = new StringBuffer(); for (Map.Entry<String, String> entry : info.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); mStringBuffer.append(key + "=" + value + "\r\n"); } return mStringBuffer; } /** * 上传崩溃信息到服务器 */ private void uploadCrashMessage(ConcurrentHashMap<String, Object> info) { crashUploader.uploadCrashMessage(info); } /** * 崩溃信息上传接口回调 */ public interface CrashUploader { void uploadCrashMessage(ConcurrentHashMap<String, Object> info); } }
Application 中的初始化:
/** * Created by Owen Chan * On 2017-06-07. */ public class ClientApp extends Application { private static ClientApp INSTANCE; private PendingIntent restartIntent; @Override public void onCreate() { super.onCreate(); CrashHandler.CrashUploader crashUploader = new CrashHandler.CrashUploader() { @Override public void uploadCrashMessage(ConcurrentHashMap<String, Object> info) { //这个地方自定义崩溃信息的上传 } }; INSTANCE = this; // 以下用来捕获程序崩溃异常 Intent intent = new Intent(); intent.setClassName("com.owen.crashhandler", "com.owen.crashhandler.main.MainActivity"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); restartIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0); CrashHandler.getInstance().init(this, crashUploader, restartIntent); } public synchronized static ClientApp getInstance() { return INSTANCE; } }
相关文章推荐
- 【Android】App应用崩溃(Crash/Force Close)之后如何让它自动重启?
- Android CrashHandler 自定义崩溃异常捕获
- Android学习杂记(四):Android应用遭遇错误导致崩溃之后自动重启
- Android 如何实现download软件后自动重启 M
- android app崩溃后自动重启
- 如何在Android程序Force Closed后自动重启
- Android中如何自定义我们想要的控件(以可以自动获取焦点的TextView为例)
- Android 开发之Service 探索如何保证Service不被杀死或被kill之后自动重启
- android开发 自定义自动重启的方法
- 如何监视一个进程,崩溃挂掉后自动重启
- Android 如何让service被管理器杀死后自动重启
- Android 开发之Service 探索如何保证Service不被杀死或被kill之后自动重启
- 如何配置Linux服务,确保崩溃或重启后自动加以运行——第一部分:应用实例
- Android开发之app崩溃后捕获异常或自动重启
- Android 通过组合键进入recovery模式选择sdcard菜单升级,升级完如何自动重启?
- 如何监视一个进程,崩溃挂掉后自动重启
- 如何配置一项在崩溃或重启后自动启用的Linux服务——第二部分:参考
- 进程意外崩溃,如何自动重启
- Android教程之如何使用自定义字体
- 如何实现自定义及自动逐页打印DataGrid显示的内容