Closed iscmd closed 1 year ago
在一个项目中同时使用性能检测工具类和不会崩溃的App类时,会导致每次都定位至自定义的异常捕获类中的Looper.loop()。
①例如进入App时进行网络状态监听广播,并在子线程中通过使用url.openStream()方法去连接百度,检查是否真正能上网,但子线程网络请求时间过长时,就会定位置到自定义的异常捕获类中的Looper.loop()。 ②或者点击按钮调用SystemClock.sleep(2000),也会定位至自定义的异常捕获类中的Looper.loop(),请求什么方法可进行解决。
异常捕获类CrashCapture如下: Java
Java
public class CrashCapture {
private static final String TAG = "CrashCapture"; private static CrashCapture mInstance; private Context mContext; // 用来存储设备信息和异常信息 private Map<String, String> infos = new HashMap<String, String>(); // 用于格式化日期,作为日志文件名的一部分 private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); private Handler mHandler = new Handler() { @Override public void handleMessage(@NonNull final Message msg) { super.handleMessage(msg); Log.d(TAG, "当前线程:" + Thread.currentThread() + " isMain:" + isMainThread()); DialogManager.showNormalDialog(mContext, "异常捕获", (String) msg.obj, "复制"); } }; private CrashCapture() { } public static CrashCapture getInstance() { if (mInstance == null) { synchronized (CrashCapture.class) { if (mInstance == null) { mInstance = new CrashCapture(); } } } return mInstance; } public static void init(CrashHandler crashHandler) { getInstance().setCrashHandler(crashHandler); } public void init(final Context context) { if (context == null) { Log.e(TAG, "=================================================="); Log.e(TAG, "异常捕捉类的上下文为空,请检查是否在Application中初始化"); Log.e(TAG, "=================================================="); return; } mContext = context; final String[] log = new String[1]; getInstance().setCrashHandler(new CrashHandler() { @Override public void uncaughtException(Thread t, final Throwable e) { if (isMainThread()) { new Thread(new Runnable() { @Override public void run() { // 收集设备参数信息 collectDeviceInfo(context); // 保存日志文件 log[0] = saveCrashInfo2File(e, new CrashResultListener() { @Override public void onCrashResult(Object result) { Message message = Message.obtain(); message.obj = result; message.what = 1; mHandler.sendMessage(message); Log.d(TAG, Thread.currentThread().getName() + "【线程抛出】:" + result); } }); Log.d(TAG, Thread.currentThread().getName() + "【主线程抛出】:" + log[0]); } }).start(); } else { // 收集设备参数信息 collectDeviceInfo(mContext); // 保存日志文件 log[0] = saveCrashInfo2File(e, new CrashResultListener() { @Override public void onCrashResult(Object result) { Message message = Message.obtain(); message.obj = result; message.what = 2; mHandler.sendMessage(message); Log.d(TAG, Thread.currentThread().getName() + "【线程抛出】:" + result); } }); Log.d(TAG, Thread.currentThread().getName() + "【子线程抛出】:" + log[0]); } } }); } private void setCrashHandler(final CrashHandler crashHandler) { // 主线程产生异常,进行捕获操作 new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { //主线程异常拦截 while (true) { try { Looper.loop(); //主线程的异常会从这里抛出 } catch (Throwable e) { if (crashHandler != null) {//捕获异常处理 crashHandler.uncaughtException(Looper.getMainLooper().getThread(), e); } } } } }); // 子线程产生异常,进行捕获操作 // 所有线程异常拦截,由于主线程的异常都被我们catch住了,所以下面的代码拦截到的都是子线程的异常 Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { if (crashHandler != null) {//捕获异常处理 crashHandler.uncaughtException(t, e); } } }); } /** * 判断当前线程是否为主线程 * * @return true为主线程,false为子线程 */ private boolean isMainThread() { return Looper.getMainLooper().getThread() == Thread.currentThread(); } /** * 收集设备参数信息 * * @param ctx */ private 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()); 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, CrashResultListener crashResultListener) { 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); crashResultListener.onCrashResult(sb.toString()); try { long timestamp = System.currentTimeMillis(); String time = formatter.format(new Date()); String fileName = "crash-" + time + "-" + timestamp + ".log"; // 应用缓存文件夹地址 // String path = getPath(mContext); // 使用应用file文件路径 String path = getFile(mContext, "crash").getPath(); path = new File(path, fileName).getPath(); FileOutputStream fos = new FileOutputStream(path); fos.write(sb.toString().getBytes()); fos.close(); return fileName; } catch (Exception e) { Log.e(TAG, "an error occured while writing file...", e); } return null; } /** * 应用缓存文件夹地址 * * @return */ private static String getPath(Context context) { String path; boolean isExternalStorageLegacy = false; try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { isExternalStorageLegacy = Environment.isExternalStorageLegacy(); } } catch (Exception ignored) { } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !isExternalStorageLegacy) { path = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath() + File.separator + "crashcapture" + File.separator + "crash"; } else { if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { path = context.getExternalCacheDir().getPath() + File.separator + "crashcapture" + File.separator + "crash"; } else { path = context.getCacheDir().getPath() + File.separator + "crashcapture" + File.separator + "crash"; } } File dir = new File(path); dir.mkdirs(); Log.d(TAG, path); return path; } /** * 应用file文件夹 * * @return */ private static File getFile(Context context, String dirName) { File file; if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) { //外部存储可用 file = context.getExternalFilesDir(File.separator + "crashcapture" + File.separator + dirName); } else { //外部存储不可用 String filePath = context.getFilesDir().getAbsolutePath() + File.separator + "crashcapture"; file = new File(filePath, dirName); if (!file.exists()) { boolean canMake = file.mkdirs(); if (!canMake) { file = null; } } } return file; } public interface CrashHandler { void uncaughtException(Thread t, Throwable e); } public interface CrashResultListener { void onCrashResult(Object result); }
} java
java
性能检测工具类PerformanceMonitorUtil如下: Java public class PerformanceMonitorUtil {
//使用 printerStart 进行判断是方法执行前还是执行后调用 println private boolean printerStart = true; //记录方法执行前的时间 private long printerStartTime = 0L; //方法执行时间超过该值才输出 private long minTime = 1000L; // 创建一个Handler对象 private Handler delayHandler; //获取栈信息进行输出 private StringBuilder stringBuilder = new StringBuilder(); // 将获取栈消息功能单独抽取出来 private Runnable runnable = new Runnable() { @Override public void run() { //获取栈信息进行记录 Log.i("Printer", "【线程列表】:" + Thread.currentThread().toString()); Log.i("Printer", "【线程】:id = " + Thread.currentThread().getId() + ", name = " + Thread.currentThread().getName()); for (StackTraceElement stackTraceElement : Looper.getMainLooper().getThread().getStackTrace()) { stringBuilder.append(BlockInfo.SEPARATOR) .append(stackTraceElement); } } }; volatile private static PerformanceMonitorUtil mInstance; public static PerformanceMonitorUtil getInstance() { if (mInstance == null) { synchronized (PerformanceMonitorUtil.class) { mInstance = new PerformanceMonitorUtil(); } } return mInstance; } private PerformanceMonitorUtil() { initDelayHandle(); } // 重写Printer()方法 public void initPrinter() { Looper.getMainLooper().setMessageLogging(new Printer() { @Override public void println(String x) { if (printerStart) { printerStart = false; printerStartTime = System.currentTimeMillis(); // runnable的销毁 delayHandler.removeCallbacks(runnable); //延迟minTime * 0.8发送,用于记录阻塞时的栈信息 delayHandler.postDelayed(runnable, (long) (minTime * 0.8)); // delayHandler.postDelayed(runnable, (long) (minTime * 1.0)); } else { printerStart = true; // runnable的销毁 delayHandler.removeCallbacks(runnable); long temp; if ((temp = System.currentTimeMillis() - printerStartTime) >= minTime) { Log.i("Printer", "方法运行的总时长:" + temp); Log.i("Printer", "StackTrace:" + stringBuilder.toString()); // 清空StringBuilder stringBuilder.delete(0, stringBuilder.length()); } } } }); } // 初始化Handler,并让Handler的消息在子线程中运行 private void initDelayHandle() { // 生成一个HandlerThread对象 HandlerThread handlerThread = new HandlerThread("DelayThread"); // 在使用HandlerThread的getLooper()方法之前,必须先调用该类的start(),同时开启一个新线程; handlerThread.start(); // 将由HandlerThread获取的Looper传递给Handler对象,即由处于另外线程的Looper代替handler初始化时默认绑定的消息队列来处理消息。 // HandlerThread顾名思义就是可以处理消息循环的线程,它是一个拥有Looper的线程,可以处理消息循环; // 其实与其说Handler和一个线程绑定,倒不如说Handler和Looper是一一对应的。 delayHandler = new Handler(handlerThread.getLooper()); }
}
学习链接如下: 1、来,带你手写个性能检测工具[https://blog.csdn.net/m0_46278918/article/details/114952287#comments_15603973] 2、制作一个永远不会崩溃的App[https://blog.csdn.net/m0_46278918/article/details/115339393?spm=1001.2014.3001.5501]
用的是master分支的方案?可以使用X分支的方案 @what-isit
在一个项目中同时使用性能检测工具类和不会崩溃的App类时,会导致每次都定位至自定义的异常捕获类中的Looper.loop()。
①例如进入App时进行网络状态监听广播,并在子线程中通过使用url.openStream()方法去连接百度,检查是否真正能上网,但子线程网络请求时间过长时,就会定位置到自定义的异常捕获类中的Looper.loop()。 ②或者点击按钮调用SystemClock.sleep(2000),也会定位至自定义的异常捕获类中的Looper.loop(),请求什么方法可进行解决。
异常捕获类CrashCapture如下:
Java
public class CrashCapture {
}
java
性能检测工具类PerformanceMonitorUtil如下:
Java
public class PerformanceMonitorUtil {}
Java
学习链接如下: 1、来,带你手写个性能检测工具[https://blog.csdn.net/m0_46278918/article/details/114952287#comments_15603973] 2、制作一个永远不会崩溃的App[https://blog.csdn.net/m0_46278918/article/details/115339393?spm=1001.2014.3001.5501]