这是性能优化系列之matrix框架的第19
篇文章,我将在性能优化专栏中对matrix apm框架做一个全面的代码分析,性能优化是Android高级工程师必知必会的点,也是面试过程中的高频题目,对性能优化感兴趣的读者可以去我主页查看所有关于matrix的分享。
前言
在上一篇Android性能优化系列-腾讯matrix-电量优化之BatteryMonitorPlugin源码分析中我们对matrix电量监控进行了初步分析,BatteryMonitorPlugin的工作机制大致可以分为两种,一种是通过各个feature类进行的不同场景下的监控;另一种是通过监听如电量、亮屏灭屏等广播来监控电量信息。在上一篇文中我们对第二种机制已经有了了解,接下来我们的任务是对BatteryMonitorPlugin中的feature进行逐个分析,以达到对BatteryMonitorPlugin有一个全面认识的目的。BatteryMonitorPlugin中的feature如下,今天我们从WifiMonitorFeature的源码开始。
- WifiMonitorFeature 用于记录wifi相关的操作
- BlueToothMonitorFeature 用于记录蓝牙相关的操作
- WakeLockMonitorFeature 用于记录亮屏相关的操作
- TrafficMonitorFeature 用于记录流量相关的操作
- NotificationMonitorFeature 用于记录通知相关的操作
- LooperTaskMonitorFeature 用于记录主线程消息队列相关的操作
- LocationMonitorFeature 用于记录定位相关的操作
- JiffiesMonitorFeature 用于记录jiffies相关的操作
- InternalMonitorFeature 用于记录内部状态相关的操作
- DeviceStatMonitorFeature 用于记录设备状态相关的操作
- CpuStatFeature 用于记录cpu状态相关的操作
- AppStatMonitorFeature 用于记录app状态相关的操作
- AlarmMonitorFeature 用于记录闹钟相关的操作
- BatteryStatsFeature 用于记录电池温度,电池剩余电量等信息
- CompositeMonitors 非AbsMonitorFeature子类,但是也比较关键
WifiMonitorFeature继承自AbsMonitorFeature,AbsMonitorFeature实现了接口MonitorFeature,所以我们就从接口定义的几个关键方法入手:
- configure
- onTurnOn
- onTurnOff
- onForeground
- onBackgroundCheck
- weight
configure
configure方法就是接收了BatteryMonitorCore引用并保存在变量this.mCore中,此方法定义在父类AbsMonitorFeature中,所以每个feature都持有BatteryMonitorCore的引用。
less
@CallSuper
@Override
public void configure(BatteryMonitorCore monitor) {
MatrixLog.i(getTag(), "#configure");
this.mCore = monitor;
}
onTurnOn
onTurnOn表示开启,在BatteryMonitorPlugin启动的时候,它会一次性启动所有配置的feature,也就是调用它的onTurnOn方法。方法在开启了ams hook的条件下,才会真正执行,执行时通过WifiManagerServiceHooker调用addListener方法将当前listener添加到监听队列中,从而可以拿到两个回调,onStartScan和onGetScanResults,这两个方法分别对应了WifiManager中的startScan和getScanResults方法,也就是说当WifiManager的这两个方法被调用时,就会回调到这里。
它是怎么做到的?我们先留一个疑问。
当两个方法调用时,会获取当前堆栈信息存入mTracing对象中,mTracing对象的类是WifiTracing,用于记录wifi信息,包括扫描次数,查询次数,和当前堆栈。
scss
@Override
public void onTurnOn() {
super.onTurnOn();
if (mCore.getConfig().isAmsHookEnabled) {
mListener = new WifiManagerServiceHooker.IListener() {
@Override
public void onStartScan() {
//如果开启了记录,则获取到当前堆栈信息,设置到mTracing对象中
String stack = shouldTracing() ? mCore.getConfig().callStackCollector.collectCurr() : "";
mTracing.setStack(stack);
mTracing.onStartScan();
}
@Override
public void onGetScanResults() {
//同样,记录当前堆栈信息到mTracing对象中,注意这些信息都是实时的,onGetScanResults
//调用时的堆栈会覆盖掉onStartScan时的堆栈
String stack = shouldTracing() ? mCore.getConfig().callStackCollector.collectCurr() : "";
mTracing.setStack(stack);
mTracing.onGetScanResults();
}
};
WifiManagerServiceHooker.addListener(mListener);
}
}
onTurnOff
onTurnOff表示监控被关闭,当方法调用时,会从WifiManagerServiceHooker中移除mListener监听,并清空mTracing中的数据。
scss
@Override
public void onTurnOff() {
super.onTurnOff();
WifiManagerServiceHooker.removeListener(mListener);
mTracing.onClear();
}
onForeground
WifiMonitorFeature并没有实现这个方法,所以不必关注。
onBackgroundCheck
WifiMonitorFeature并没有实现这个方法,所以不必关注。
weight
weight是一个数值,表示当前feature的权重,BatteryMonitorPlugin创建时会根据这个值对所有feature进行排序。
csharp
@Override
public int weight() {
return Integer.MIN_VALUE;
}
至此,我们就把WifiMonitorFeature第一层的实现分析完了,可以直观的了解到,WifiMonitorFeature就是通过hook WifiManager的startScan和getScanResults两个方法,来监听到wifi scan和query两个操作的次数,以及操作时的堆栈调用信息,那么具体hook是怎么实现的,我们就要到WifiManagerServiceHooker中看一下了。
WifiManagerServiceHooker
前边提到WifiManagerServiceHooker通过调用addListener方法将当前listener加入监听队列,我们看看addListener的实现。
addListener
可以看到,添加listener的同时,还会调用doHook,这个应该就是真正进行hook操作的地方了。
scss
public synchronized static void addListener(IListener listener) {
sListeners.add(listener);
checkHook();
}
ini
private static void checkHook() {
if (sTryHook) {
return;
}
boolean hookRet = sHookHelper.doHook();
sTryHook = true;
}
doHook是SystemServiceBinderHooker中的方法,SystemServiceBinderHooker是WifiManagerServiceHooker中的一个静态对象,所以在类加载阶段,它就会被创建,注意它传入的两个参数,WIFI_SERVICE就是"wifi",然后是"android.net.wifi.IWifiManager"。
java
private static SystemServiceBinderHooker sHookHelper = new SystemServiceBinderHooker(Context.WIFI_SERVICE, "android.net.wifi.IWifiManager", sHookCallback);
doHook
ini
public boolean doHook() {
//第一步
BinderProxyHandler binderProxyHandler = new BinderProxyHandler(mServiceName, mServiceClass, mHookCallback);
//第二步
IBinder delegateBinder = binderProxyHandler.createProxyBinder();
//第三步
Class<?> serviceManagerCls = Class.forName("android.os.ServiceManager");
Field cacheField = serviceManagerCls.getDeclaredField("sCache");
cacheField.setAccessible(true);
Map<String, IBinder> cache = (Map) cacheField.get(null);
cache.put(mServiceName, delegateBinder);
mDelegateServiceBinder = delegateBinder;
mOriginServiceBinder = binderProxyHandler.getOriginBinder();
return true;
}
第一步
上边的代码我们一行一行来看,首先创建BinderProxyHandler,传入的serviceName就是"wifi",serviceClass为"android.net.wifi.IWifiManager"。
scss
BinderProxyHandler(String serviceName, String serviceClass, HookCallback callback) throws Exception {
//获取到wifi服务的binder对象,保存在BinderProxyHandler的mOriginBinder变量上
mOriginBinder = getCurrentBinder(serviceName);
mServiceManagerProxy = createServiceManagerProxy(serviceClass, mOriginBinder, callback);
}
看看getCurrentBinder的实现,通过反射获取到ServiceManager类中的Method-getService,然后invoke获取到wifi服务的binder对象(ServiceManager中map集合保存了所有服务的binder)。
arduino
static IBinder getCurrentBinder(String serviceName) throws Exception {
Class<?> serviceManagerCls = Class.forName("android.os.ServiceManager");
Method getService = serviceManagerCls.getDeclaredMethod("getService", String.class);
return (IBinder) getService.invoke(null, serviceName);
}
继续再看下createServiceManagerProxy方法,传入的serviceClassName就是上边提到的"android.net.wifi.IWifiManager",它本身是一个aidl文件,编译后会生成对应的Java文件,所以这里是获取到aidl生成的Java类中的静态内部类Stub,然后反射得到它的Method-asInterface,再调用asInterface传入getCurrentBinder方法中获取到的binder对象,拿到的object对象是谁?就是WifiManager对象(这块逻辑不太清楚的读者,可以回顾一下实用aidl进行进程间通信的实现流程)。拿到WifiManager对象之后对IWifiManager等接口做动态代理,所以就可以拦截到其方法的执行,于是下一步就进入前边传入的callback了。
dart
private static Object createServiceManagerProxy(String serviceClassName, IBinder originBinder, final HookCallback callback) throws Exception {
Class<?> serviceManagerCls = Class.forName(serviceClassName);
Class<?> serviceManagerStubCls = Class.forName(serviceClassName + "$Stub");
ClassLoader classLoader = serviceManagerStubCls.getClassLoader();
if (classLoader == null) {
throw new IllegalStateException("get service manager ClassLoader fail!");
}
Method asInterfaceMethod = serviceManagerStubCls.getDeclaredMethod("asInterface", IBinder.class);
//originManagerService就是WifiManager对象,拿到WifiManager对象之后对IWifiManager等接口做动态代理
final Object originManagerService = asInterfaceMethod.invoke(null, originBinder);
return Proxy.newProxyInstance(classLoader,
new Class[]{IBinder.class, IInterface.class, serviceManagerCls},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (callback != null) {
//拦截到相关方法
callback.onServiceMethodInvoke(method, args);
Object result = callback.onServiceMethodIntercept(originManagerService, method, args);
if (result != null) {
return result;
}
}
return method.invoke(originManagerService, args);
}
}
);
}
BinderProxyHandler的构造方法就做了这些,反射获取到WifiManager相关的信息,并动态代理了IWifiManager接口,从而可以拦截到startScan,getScanResults等方法。
第二步
下一步,创建BinderProxyHandler对象之后,调用binderProxyHandler.createProxyBinder(),又对IBinder做了动态代理。
csharp
public IBinder createProxyBinder() throws Exception {
Class<?> serviceManagerCls = Class.forName("android.os.ServiceManager");
ClassLoader classLoader = serviceManagerCls.getClassLoader();
if (classLoader == null) {
throw new IllegalStateException("Can not get ClassLoader of " + serviceManagerCls.getName());
}
return (IBinder) Proxy.newProxyInstance(
classLoader,
new Class<?>[]{IBinder.class},
this
);
}
从它的invoke方法可以看出,它是想拦截queryLocalInterface方法,当queryLocalInterface方法被调用时,直接返回前边创建的mServiceManagerProxy对象,也就是WifiManager的代理对象。
typescript
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("queryLocalInterface".equals(method.getName())) {
return mServiceManagerProxy;
}
return method.invoke(mOriginBinder, args);
}
第三步
接下来的代码,通过再次反射ServiceManager拿到它的成员变量sCache,sCache是一个HashMap集合,内部存储着所有的Android服务的binder对象,所以这里sCache拿到这个Map集合,然后将上一步获取到的代理binder对象添加到Map集合中,也就是用代理对象覆盖掉原来的binder对象,
ini
Class<?> serviceManagerCls = Class.forName("android.os.ServiceManager");
Field cacheField = serviceManagerCls.getDeclaredField("sCache");
cacheField.setAccessible(true);
Map<String, IBinder> cache = (Map) cacheField.get(null);
cache.put(mServiceName, delegateBinder);
//保存代理对象和源wifi服务的binder对象,用于后边恢复
mDelegateServiceBinder = delegateBinder;
mOriginServiceBinder = binderProxyHandler.getOriginBinder();
总结一下以上三步的内容,一共创建了两个代理对象,一个是远程wifi服务的Binder代理对象,通过对IBinder接口做动态代理,并将代理对象设置到ServiceManager中,这样一来,wifi的binder对象被调用时,都会走到这里。
vbnet
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("queryLocalInterface".equals(method.getName())) {
return mServiceManagerProxy;
}
return method.invoke(mOriginBinder, args);
}
另一个是WifiManager本地服务的代理对象,通过对IWifiManager接口做动态代理,从而拦截到WifiManager的所有接口调用,两个动态代理衔接起来,调用binder的queryLocalInterface接口时,返回WifiManager本地服务的代理对象,这样一来,所有WifiManager的接口调用就都在监控范围之内了。
invoke
hook的实现我们分析完了,接下来回到WifiMonitorFeature的逻辑中,当WifiManager动态代理拦截到接口执行时,会通过onServiceMethodInvoke和onServiceMethodIntercept方法回调到调用方,我们看下WifiMonitorFeature中的这两个方法。
sql
if (callback != null) {
callback.onServiceMethodInvoke(method, args);
Object result = callback.onServiceMethodIntercept(originManagerService, method, args);
if (result != null) {
return result;
}
}
return method.invoke(originManagerService, args);
onServiceMethodInvoke
可以看到,WifiMonitorFeature只处理了onServiceMethodInvoke方法,当方法执行时,如果执行的方法是startScan和getScanResults,WifiMonitorFeature会做处理将事件向下分发。
typescript
public void onServiceMethodInvoke(Method method, Object[] args) {
if ("startScan".equals(method.getName())) {
WifiManagerServiceHooker.dispatchStartScan();
} else if ("getScanResults".equals(method.getName())) {
WifiManagerServiceHooker.dispatchGetScanResults();
}
}
@Nullable
public Object onServiceMethodIntercept(Object receiver, Method method, Object[] args) {
return null;
}
事件分发后来到这两个方法中,看到这里,终于和我们在文章开头的分析形成了闭环,WifiMonitorFeature通过hook WifiManager的方式,拦截到startScan和getScanResults两个方法,并在方法被调用时,记录调用的次数和调用时方法的堆栈信息,实时存储在mTracing对象中。外界就可以实时的拿到wifi执行的信息了。
arduino
public void onStartScan() {
String stack = WifiMonitorFeature.this.shouldTracing() ? WifiMonitorFeature.this.mCore.getConfig().callStackCollector.collectCurr() : "";
WifiMonitorFeature.this.mTracing.setStack(stack);
WifiMonitorFeature.this.mTracing.onStartScan();
}
public void onGetScanResults() {
String stack = WifiMonitorFeature.this.shouldTracing() ? WifiMonitorFeature.this.mCore.getConfig().callStackCollector.collectCurr() : "";
WifiMonitorFeature.this.mTracing.setStack(stack);
WifiMonitorFeature.this.mTracing.onGetScanResults();
}
总结
经过今天的分析,我们知道了,WifiMonitorFeature作为matrix电量监控中的一环,它的实现是通过hook WifiManager从而拦截到wifi扫描和获取wifi列表这两个方法的执行,并在执行时记录执行的次数和执行时方法调用的堆栈,将信息实时的记录到对象中,供外界取用的。那为什么matrix针对wifi的电量消耗只针对这两个执行方法呢?其实也很简单,因为这两个方法的执行就是相对来说,最耗电的。