前言:
作者本人负责公司的APM监控模块,因为工作的原因,对ANR,crash等流程研究的比较多,最近在打造APM监控平台的时候,顺带对DropBox的实现原理进行了一定的学习和研究,发现了一些妙用,因此写这篇文章来分享。
本系列分为两篇文章,
1.DropBox系列-安卓DropBox介绍:主要介绍DropBox的一些实现原理。
2.DropBox系列-利用DropBox打造APM框架:主要介绍利用DropBox打造一个面向所有APP的稳定性监控框架。
PS:本文的代码以安卓9(SDK=28)的源码来介绍,因为目前车载开发基本上都是以安卓9为基础来定制的,而且DropBox对于那些拥有系统权限的APP来讲意义更大一些。
一.DropBox简介
dropbox是安卓系统中用来记录一些异常信息的模块,比如某个应用crash了或者anr了,系统最终都会把记录的异常信息记录到dropbox中。
其一般会把异常信息记录到中:data/system/dropbox文件夹下,记录的类型会有很多种,主要有CRASH,ANR,墓碑日志,native崩溃等等,如下图所示:
当然,由于SELinux权限的限制,这些文件夹对于普通的APP来讲说不可访问的,只有拥有系统签名的APP才可以访问的。
二.DropBox记录流程
2.1 binder接口
DropBoxManagerService中,提供了一个binder实现类,主要提供了四个方法,其实最核心的,就是add方法,也就是真正实现记录的方法。我们的源码阅读,也从这里开始。
private final IDropBoxManagerService.Stub mStub = new IDropBoxManagerService.Stub() {
@Override
public void add(DropBoxManager.Entry entry) {
DropBoxManagerService.this.add(entry);
}
@Override
public boolean isTagEnabled(String tag) {
return DropBoxManagerService.this.isTagEnabled(tag);
}
@Override
public DropBoxManager.Entry getNextEntry(String tag, long millis) {
return DropBoxManagerService.this.getNextEntry(tag, millis);
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
DropBoxManagerService.this.dump(fd, pw, args);
}
};
2.2 add流程
binder的add方法之后,会调用到DropBoxManagerService的add方法。
其核心实现逻辑就是add方法,add方法主要做了一下的工作:
1.把接收到的Entry内容写入到临时文件tmp中。
temp = new File(mDropBoxDir, "drop" + Thread.currentThread().getId() + ".tmp");
2.写入完成后,把tmp文件修改文件名,改成data_app_anr@111这样的文件名。
final File file = this.getFile(dir);
if (!temp.renameTo(file)) {
throw new IOException("Can't rename " + temp + " to " + file);
}
3.切换成主线程,发送广播。
final Intent dropboxIntent = new Intent(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED);
dropboxIntent.putExtra(DropBoxManager.EXTRA_TAG, tag);
dropboxIntent.putExtra(DropBoxManager.EXTRA_TIME, time);
if (!mBooted) {
dropboxIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
// Call sendBroadcast after returning from this call to avoid deadlock. In particular
// the caller may be holding the WindowManagerService lock but sendBroadcast requires a
// lock in ActivityManagerService. ActivityManagerService has been caught holding that
// very lock while waiting for the WindowManagerService lock.
mHandler.sendMessage(mHandler.obtainMessage(MSG_SEND_BROADCAST, dropboxIntent));
三.DropBox妙用
既然在系统发现异常后,会主动把对应的记录文件进行记录,那么,我们是不是可以利用dropbox,针对系统中所有的APP应用进行一个切面编程,捕获所有的异常进行上报。这样就不需要每个应用单独去接入注入bugly一类的框架了,而且由于少了bugly的影响,性能,稳定性会更好。
实现对dropbox的切面编程,主要有以下三个方案:
方案一:监听dropbox文件夹变化
利用FileObserver对data/system/dropbox文件夹进行修改监听,bugly其实也是用的这个原理,6.0以上会因为SELinux的权限失效。所以要对SELinux的文件夹进行配置。
方案二:修改系统源码进行binder通讯
修改系统AMS中的源码,APP进行binder的注册。在发现崩溃或者ANR的之后,利用APP注册的binder,通知APP一侧。
方案三:利用广播进行通知
上面介绍,保存完成后,回发送一个广播。那么我们在APP侧监听这个广播,就可以知道什么时候出现了异常。
具体的实现和结果,我们下一章APM框架再来细细讲解。