主目录见:Android高级进阶知识(这是总目录索引)
框架地址:VirtualApk
在线源码查看:AndroidXRef
今天我们就从滴滴的插件化VirtualApk来结合我们之前说的一些知识讲讲插件化,如果之前未涉及的知识点,在这里也会讲到,希望我们的插件化之旅能圆满愉快。同时今天这是我们的第一篇,我们在这里先从初始化开始。
初始化
这里我们就不讲怎么使用了,想必怎么使用大家看下文档就知道了,可以参照这里https://github.com/didi/VirtualAPK/wiki。
我们都知道,插件化框架一般会分出宿主程序和插件程序部分,今天我们会先从Host(宿主工程)开始讲,在使用这个插件化框架的时候,我们除了要添加依赖之外,最开始的就是要初始化:
//Initialize PluginManager in YourApplication::attachBaseContext().
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
PluginManager.getInstance(base).init();
}
我们从这个使用入手,我们来看看PluginManager#getInstance(base)
方法做了什么:
public static PluginManager getInstance(Context base) {
if (sInstance == null) {
synchronized (PluginManager.class) {
if (sInstance == null)
sInstance = new PluginManager(base);
}
}
return sInstance;
}
我们看到这里是通过单例模式获取PluginManager
对象,那么我们这里直接看到构造函数:
private PluginManager(Context context) {
Context app = context.getApplicationContext();
if (app == null) {
this.mContext = context;
} else {
this.mContext = ((Application)app).getBaseContext();
}
prepare();
}
前面很容易,获取宿主程序的上下文对象,然后调用prepare
方法:
private void prepare() {
Systems.sHostContext = getHostContext();
this.hookInstrumentationAndHandler();
if (Build.VERSION.SDK_INT >= 26) {
this.hookAMSForO();
} else {
this.hookSystemServices();
}
}
首先将宿主的上下文对象保存在Systems
类中的静态变量sHostContext
中。然后后面会hook几个Android系统的类,我们先来看第一个hookInstrumentationAndHandler()
方法:
private void hookInstrumentationAndHandler() {
try {
Instrumentation baseInstrumentation = ReflectUtil.getInstrumentation(this.mContext);
if (baseInstrumentation.getClass().getName().contains("lbe")) {
// reject executing in paralell space, for example, lbe.
System.exit(0);
}
final VAInstrumentation instrumentation = new VAInstrumentation(this, baseInstrumentation);
Object activityThread = ReflectUtil.getActivityThread(this.mContext);
ReflectUtil.setInstrumentation(activityThread, instrumentation);
ReflectUtil.setHandlerCallback(this.mContext, instrumentation);
this.mInstrumentation = instrumentation;
} catch (Exception e) {
e.printStackTrace();
}
}
我们一步一步来看,首先来看ReflectUtil#getInstrumentation()
做了啥:
public static Instrumentation getInstrumentation(Context base) {
if (getActivityThread(base) != null) {
try {
sInstrumentation = (Instrumentation) ReflectUtil.invoke(
sActivityThread.getClass(), sActivityThread, "getInstrumentation");
} catch (Exception e) {
e.printStackTrace();
}
}
return sInstrumentation;
}
这个方法主要是通过反射来获取Instrumentation
对象(为什么要反射得到这个对象呢?其实在启动activity的时候会进行用占坑的Activity来欺骗过系统检查),我们知道在ActivityThread
中可以获取到Instrumentation
实例 ,所以同样的,程序先要通过调用getActivityThread()
方法来获取ActivityThread对象:
@UiThread
public static Object getActivityThread(Context base) {
if (sActivityThread == null) {
try {
Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
Object activityThread = null;
try {
activityThread = ReflectUtil.getField(activityThreadClazz, null, "sCurrentActivityThread");
} catch (Exception e) {
// ignored
}
if (activityThread == null) {
activityThread = ((ThreadLocal<?>) ReflectUtil.getField(activityThreadClazz, null, "sThreadLocal")).get();
}
sActivityThread = activityThread;
} catch (Exception e) {
e.printStackTrace();
}
}
return sActivityThread;
}
我们看到这里反射了ActivityThread
类,然后通过获取sCurrentActivityThread
实例来得到ActivityThread
实例,如果没有获取到则通过获取sThreadLocal
来得到ActivityThread
实例,为什么这样呢?这是因为早期的版本中ActivityThread
是放在ThreadLocal
中的(static final ThreadLocal<ActivityThread> sThreadLocal = new ThreadLocal()),到这里,我们的ActivityTthread
实例已经获取成功,我们继续通过反射调用这个实例中的getInstrumentation()
来获取Instrumentation
实例,即上面ReflectUtil#getInstrumentation()
做的事情。然后我们继续看hookInstrumentationAndHandler()
方法,这个方法里面会实例化VAInstrumentation
对象,并将我们获取到的系统的Instrumentation
当做参数传进构造函数,然后将我们实例化出来的VAInstrumentation
实例替换ActivityThread
实例中的Instrumentation
,并将Handler
的回调设置为VAInstrumentation
类,因为这个类实现了Handler.Callback
接口。接着我们看prepare()
方法中:
if (Build.VERSION.SDK_INT >= 26) {
this.hookAMSForO();
} else {
this.hookSystemServices();
}
这两个方法都是使用了动态代理来代理系统的AMS(也就是hook AMS在客户端的代理ActivityManagerProxy),这样在以后所有执行该代理的方法,都是默认先走到我们自己定义的ActivityManagerProxy(继承自InvocationHanlder)的invoke方法中(这是动态代理的机制)。这里有个版本判断,是为了兼容8.0系统的,因为8.0系统把AMS客户端代理获取放在IActivityManagerSingleton
属性中,如下所示,如果熟悉FrameWork的应该知道:
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
我们先来看8.0系统下的hookAMSForO()
方法是怎么做的:
private void hookAMSForO() {
try {
Singleton<IActivityManager> defaultSingleton = (Singleton<IActivityManager>) ReflectUtil.getField(ActivityManager.class, null, "IActivityManagerSingleton");
IActivityManager activityManagerProxy = ActivityManagerProxy.newInstance(this, defaultSingleton.get());
ReflectUtil.setField(defaultSingleton.getClass().getSuperclass(), defaultSingleton, "mInstance", activityManagerProxy);
} catch (Exception e) {
e.printStackTrace();
}
}
一样的,我们这里通过反射取到ActivityManager
类中IActivityManagerSingleton
属性,然后我们通过调用ActivityManagerProxy#newInstance()
方法来实例化新的ActivityManagerProxy
,我们跟进去看看:
public static IActivityManager newInstance(PluginManager pluginManager, IActivityManager activityManager) {
return (IActivityManager) Proxy.newProxyInstance(activityManager.getClass().getClassLoader(), new Class[] { IActivityManager.class }, new ActivityManagerProxy(pluginManager, activityManager));
}
这个地方使用了动态代理来代理系统中的AMS客户端代理ActivityManagerProxy
,所以之后调用通过这个代理调用远程AMS的服务都会先到我们创建的ActivityManagerProxy
中的invoke方法里,因为这个类实现了InvocationHandler
接口。为什么要hook掉这个类呢?其实主要是要拦截Service启动,停止,绑定等等的方法。接着我们看8.0版本之前的hook AMS服务的方法hookSystemServices()
:
private void hookSystemServices() {
try {
Singleton<IActivityManager> defaultSingleton = (Singleton<IActivityManager>) ReflectUtil.getField(ActivityManagerNative.class, null, "gDefault");
IActivityManager activityManagerProxy = ActivityManagerProxy.newInstance(this, defaultSingleton.get());
// Hook IActivityManager from ActivityManagerNative
ReflectUtil.setField(defaultSingleton.getClass().getSuperclass(), defaultSingleton, "mInstance", activityManagerProxy);
if (defaultSingleton.get() == activityManagerProxy) {
this.mActivityManager = activityManagerProxy;
}
} catch (Exception e) {
e.printStackTrace();
}
}
我们看到这里面主要是通过获取ActivityManagerNative
类中的gDefault属性来获取AMS客户端代理,我们可以看到这个gDefault属性如下:
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
这个也主要是获取远程的AMS的客户端代理,因为服务启动完会注册在ServiceManager
中,所以我们只要从这里查找即可。然后下面的步骤其实跟hookAMSForO()
一样了。到这里我们PluginManager
的构造函数里面的初始化已经完成,然后我们会调用PluginManager#getInstance(base)#init()
方法:
public void init() {
mComponentsHandler = new ComponentsHandler(this);
RunUtil.getThreadPool().execute(new Runnable() {
@Override
public void run() {
doInWorkThread();
}
});
}
这个方法很简单,实例化ComponentsHandler
类,然后在子线程中调用doInWorkThread()
方法,doInWorkThread()
方法暂时为空实现。到这里我们的初始化工作已经完成了。
总结:我们看到我们这里初始化主要是hook了两个中的类,一个是Instrumentation
,另一个是系统服务AMS的客户端代理ActivityManagerProxy
,在后面都会用到,系统大家有个印象,好啦,祝大家插件化之路愉快。
网友评论