android全局异常捕获

xiaoxiao2021-02-28  50

一个项目上线总会遇到各种各样未被预测的bug导致app莫名闪退,更何况在android这个机型满天飞的操作系统下。

好今天给大家讲解下android全局异常捕获的处理方案,让你完全掌控每个用户每个机型所发生的闪退原因!

首先大家先来了解下Thread.UncaughtExceptionHandler:

/** * Interface for handlers invoked when a <tt>Thread</tt> abruptly * terminates due to an uncaught exception. * <p>When a thread is about to terminate due to an uncaught exception * the Java Virtual Machine will query the thread for its * <tt>UncaughtExceptionHandler</tt> using * {@link #getUncaughtExceptionHandler} and will invoke the handler's * <tt>uncaughtException</tt> method, passing the thread and the * exception as arguments. * If a thread has not had its <tt>UncaughtExceptionHandler</tt> * explicitly set, then its <tt>ThreadGroup</tt> object acts as its * <tt>UncaughtExceptionHandler</tt>. If the <tt>ThreadGroup</tt> object * has no * special requirements for dealing with the exception, it can forward * the invocation to the {@linkplain #getDefaultUncaughtExceptionHandler * default uncaught exception handler}. * * @see #setDefaultUncaughtExceptionHandler * @see #setUncaughtExceptionHandler * @see ThreadGroup#uncaughtException * @since 1.5 */ @FunctionalInterface public interface UncaughtExceptionHandler { /** * Method invoked when the given thread terminates due to the * given uncaught exception. * <p>Any exception thrown by this method will be ignored by the * Java Virtual Machine. * @param t the thread * @param e the exception */ void uncaughtException(Thread t, Throwable e); }

通过源码可以知道: Thread.UncaughtExceptionHandler是一个当线程由于未捕获的异常突然终止而调用处理程序的接口. 当线程由于未捕获的异常即将终止时,Java虚拟机将使用它来查询其UncaughtExceptionHandler的线程Thread.getUncaughtExceptionHandler(),并将调用处理程序的 uncaughtException方法,将线程和异常作为参数传递。如果一个线程没有显示它的UncaughtExceptionHandler,那么它的ThreadGroup对象充当它的 UncaughtExceptionHandler。如果ThreadGroup对象没有处理异常的特殊要求,它可以将调用转发到默认的未捕获的异常处理程序。

因此我们可以自定一个类去实现该接口,该类主要用于收集错误信息和保存错误信息.

代码实现:

package demo.com.androidglobalexception; import android.app.Application; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; import android.os.Environment; import android.util.Log; import java.io.File; import java.io.FileOutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Field; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * <p>名称:CrashHandler<p> * <p> 描述:闪退异常处理<p> * @author guoweiquan * @version 1.0 * @data 2018/5/18 上午9:22 */ public class CrashHandler implements Thread.UncaughtExceptionHandler{ public static final String TAG = "CrashHandler"; private static CrashHandler instance = new CrashHandler(); private Context mContext; private Application app; // 系统默认的 UncaughtException 处理类 private Thread.UncaughtExceptionHandler mDefaultHandler; // 用来存储设备信息和异常信息 private Map<String, String> infos = new HashMap<String, String> (); // 用于格式化日期,作为日志文件名的一部分 private DateFormat formatter = new SimpleDateFormat ("yyyy-MM-dd-HH-mm-ss"); public UpdataErrorInfoLinster delegete; private CrashHandler() { } public static CrashHandler getInstance() { return instance; } /** * @Title: init * @Description: 初始化 * @param context * @param app * 传入的app * @throws */ public void init(Context context, Application app, UpdataErrorInfoLinster l) { this.app = app; mContext = context; this.delegete = l; // 获取系统默认的 UncaughtException 处理器 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); // 设置该 CrashHandler 为程序的默认处理器 Thread.setDefaultUncaughtExceptionHandler(this); } /** * 当 UncaughtException 发生时会转入该函数来处理 */ @Override public void uncaughtException(Thread thread, Throwable ex) { if (!handleException(ex) && mDefaultHandler != null) { // 如果用户没有处理则让系统默认的异常处理器来处理 mDefaultHandler.uncaughtException(thread, ex); } else { app.onTerminate(); } } /** * 自定义错误处理,收集错误信息,发送错误报告等操作均在此完成 * * @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 (PackageManager.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()); } 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); Log.e(TAG,sb.toString());//答应错误信息 /******************************/ //上传服务器 delegete.onUpdataErrorinfo(sb.toString()); String filePath = ""; try { long timestamp = System.currentTimeMillis(); String time = formatter.format(new Date ()); String fileName = "error-" + time + "-" + timestamp + ".log"; if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { String path = Environment.getExternalStorageDirectory() .getAbsolutePath() + "/crashs"; // Toast.makeText(mContext, path, Toast.LENGTH_LONG).show(); File dir = new File (path); if (!dir.exists()) { dir.mkdirs(); } filePath = path + fileName; FileOutputStream fos = new FileOutputStream (path + fileName); fos.write(sb.toString().getBytes()); fos.flush(); fos.close(); } return filePath; } catch (Exception e) { Log.e(TAG, "an error occured while writing file...", e); } return null; } public interface UpdataErrorInfoLinster { void onUpdataErrorinfo(String str); } }

本类主要核心方法是

public void uncaughtException(Thread thread, Throwable ex)

当系统捕获到异常时uncaughtException将会被调用,并把异常传递过来。

其他的方法就不多赘述注释都写的很明白。

重点讲下:

public interface UpdataErrorInfoLinster { void onUpdataErrorinfo(String str); }

这个接口是将捕获的信息回调出去的。

代理者是:public UpdataErrorInfoLinster delegete;

详细使用: package demo.com.androidglobalexception; import android.app.Application; import android.util.Log; /** * <p>描述:<p> * * @author guoweiquan * @version 1.0 * @data 2018/5/18 上午9:22 */ public class MyApp extends Application { public static String TAG = "MyApp"; @Override public void onCreate() { super.onCreate (); Log.e(TAG,"onCreate is running"); CrashHandler crashHandler = CrashHandler.getInstance(); crashHandler.init(getApplicationContext(), this ,new CrashHandler.UpdataErrorInfoLinster() { @Override public void onUpdataErrorinfo(String str) { //发送错误信息给服务器 Log.e("TAG","exception is-->" + str); } }); int a = 1/0 ; } }

在MyApp类中创建CrashHandler对象并初始化设置回调对象。

然后在 onUpdataErrorinfo(String str)方法中获取到excaption信息,然后就可以按照业务需求处理,比如发送给服务器,那出现闪退就可以在线分析了。

OK,就这么简单;

项目地址:https://github.com/seaeel/AndroidGloadException.git

博主技术交流QQ群:239025382

转载请注明原文地址: https://www.6miu.com/read-2612788.html

最新回复(0)