Cockroach
Cockroach copied to clipboard
性能检测工具和不崩溃的冲突
在一个项目中同时使用性能检测工具类和不会崩溃的App类时,会导致每次都定位至自定义的异常捕获类中的Looper.loop()。
①例如进入App时进行网络状态监听广播,并在子线程中通过使用url.openStream()方法去连接百度,检查是否真正能上网,但子线程网络请求时间过长时,就会定位置到自定义的异常捕获类中的Looper.loop()。 ②或者点击按钮调用SystemClock.sleep(2000),也会定位至自定义的异常捕获类中的Looper.loop(),请求什么方法可进行解决。
异常捕获类CrashCapture如下:
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
性能检测工具类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());
}
}
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]
用的是master分支的方案?可以使用X分支的方案 @what-isit