Android 四大组件由于要和 AMS 频繁通信,AMS 在 SystemServer 进程中,无法直接修改,所以四大组件的插件化是重中之重。
Activity插件化
首先必须了解 Activity 启动过程,这里不多介绍启动流程,放一张图可以看下,图是7.0的启动顺序。

预先在 AndroidManifest.xml 中注册 Activity 来占坑,用来通过 AMS 的校验。VirtualAPK 中占坑的 Activity。
<activity
android:name=".A$1"
android:exported="false"
android:launchMode="standard" />
我们启动插件中的 activity 时
Intent intent = new Intent();
intent.setClassName(this, "com.didi.virtualapk.demo.aidl.BookManagerActivity");
startActivity(intent);
对于 AMS 甚至系统类,我们不能直接修改,但可以考虑 Hook 技术替换。
由上图我们关注两个函数,一个是 activity 的 startActivityForResult
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
//内部调用ActivityManager.getService().startActivity与AMS通信
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
//...
} else {
//...
}
}
一个是 ActivityThread 的 performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
//内部Activity activity = (Activity)clazz.newInstance();用类加载器创建Activity实例
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
//...
}
注意到 mInstrumentation.execStartActivity 与 mInstrumentation.newActivity 两个方法都是 mInstrumentation 调用,因此 mInstrumentation 是一个好的 Hook 点。
VirtualAPK 的实现
execStartActivity 方法,在自定义的 VAInstrumentation 类中 实现了 execStartActivity 方法
public class VAInstrumentation extends Instrumentation implements Handler.Callback {
protected Instrumentation mBase;
protected final ArrayList<WeakReference<Activity>> mActivities = new ArrayList<>();
protected PluginManager mPluginManager;
public VAInstrumentation(PluginManager pluginManager, Instrumentation base) {
this.mPluginManager = pluginManager;
this.mBase = base;
}
@Override
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
injectIntent(intent);
return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode, options);
}
}
其中 injectIntent() 处理 intent
protected void injectIntent(Intent intent) {
mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent);
// null component is an implicitly intent
if (intent.getComponent() != null) {
Log.i(TAG, String.format("execStartActivity[%s : %s]", intent.getComponent().getPackageName(), intent.getComponent().getClassName()));
// resolve intent with Stub Activity if needed
this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent);
}
}
transformIntentToExplicitAsNeeded() 方法查找插件 apk 中是否包含与调用的 intent 参数一致的组件。
public Intent transformIntentToExplicitAsNeeded(Intent intent) {
//应用程序组件信息
ComponentName component = intent.getComponent();
//判断组件包名是否是自身
if (component == null
|| component.getPackageName().equals(mContext.getPackageName())) {
//查找插件中,最终会调到 List<ResolveInfo> query = LoadedPlugin的queryIntentActivities(intent, flags);方法
//并返回第一个匹配的
ResolveInfo info = mPluginManager.resolveActivity(intent);
if (info != null && info.activityInfo != null) {
//创建设置intent的Component
component = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
intent.setComponent(component);
}
}
return intent;
}
看下匹配逻辑 queryIntentActivities(),其中 mPackage.activities 即为上文解析插件 apk 后缓存的 Activity 集合。
public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
ComponentName component = intent.getComponent();
List<ResolveInfo> resolveInfos = new ArrayList<ResolveInfo>();
//ContentProvider共享数据是通过定义一个对外开放的统一的接口来实现的
//封装了ContentProvider保存数据
ContentResolver resolver = this.mPluginContext.getContentResolver();
//mPackage保存了解析完成后的apk信息,遍历插件清单文件中的依次比对
for (PackageParser.Activity activity : this.mPackage.activities) {
if (match(activity, component)) {
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.activityInfo = activity.info;
resolveInfos.add(resolveInfo);
} else if (component == null) {
//不是插件中的则去系统中查找对应的intent
for (PackageParser.ActivityIntentInfo intentInfo : activity.intents) {
/**
* match action-> match data-> match categories,任意一步失败就不在进行后续处理。
* match action就是匹配intent的action是否包含在filter的actions里。
* match data的思路和uri的语法组成有关[scheme:]scheme-specific-part[#fragment]
* 先判filter和intent的types(即MIMI) schemes是否全是null,若是则匹配,取值MATCH_CATEGORY_EMPTY,否则继续
* 再匹配scheme,不匹配则结束,匹配则继续
* SchemeSpecificPart部分,若匹配,则进行最后的type(MIME)判断
* SchemeSpecificPart部分,若不匹配,则
* 匹配host port部分,成功则继续,否则不匹配
* 匹配path部分,成功则继续,否则不匹配
* 最后进行type(MIME)的匹配
* match categories就是判断intent的所有category是否全都包含啊filter的categories里。
*/
if (intentInfo.match(resolver, intent, true, TAG) >= 0) {
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.activityInfo = activity.info;
resolveInfos.add(resolveInfo);
break;
}
}
}
}
return resolveInfos;
}
匹配的时候先通过自身方法 match 查找插件,不是则再调用 IntentFilter 规则匹配系统中的 intent。
自身 match
protected boolean match(PackageParser.Component component, ComponentName target) {
ComponentName source = component.getComponentName();//com.didi.virtualapk.demo.MainActivity
if (source == target) return true;//target要启动的
//source不为null、target不为null、类名包名一致
if (source != null && target != null
&& source.getClassName().equals(target.getClassName())
&& (source.getPackageName().equals(target.getPackageName())
|| mHostContext.getPackageName().equals(target.getPackageName()))) {
return true;
}
return false;
}
markIntentIfNeeded() 中判断要启动的Activity是否是插件的,是插件的则改变 intent 参数
public void markIntentIfNeeded(Intent intent) {
if (intent.getComponent() == null) {
return;
}
String targetPackageName = intent.getComponent().getPackageName();//com.didi.virtualapk.demo
String targetClassName = intent.getComponent().getClassName();//com.didi.virtualapk.demo.aidl.BookManagerActivity
// search map and return specific launchmode stub activity
if (!targetPackageName.equals(mContext.getPackageName()) && mPluginManager.getLoadedPlugin(targetPackageName) != null) {
//是启动插件Activity,intent中添加三个参数
intent.putExtra(Constants.KEY_IS_PLUGIN, true);//"isPlugin"
intent.putExtra(Constants.KEY_TARGET_PACKAGE, targetPackageName);//"target.package"
intent.putExtra(Constants.KEY_TARGET_ACTIVITY, targetClassName);//"target.activity"
dispatchStubActivity(intent);
}
}
private void dispatchStubActivity(Intent intent) {
//获取ComponentName
ComponentName component = intent.getComponent();
//获取intent中要启动的class类名
String targetClassName = intent.getComponent().getClassName();
//再次判断
LoadedPlugin loadedPlugin = mPluginManager.getLoadedPlugin(intent);
ActivityInfo info = loadedPlugin.getActivityInfo(component);
//获取启动模式launchMode
int launchMode = info.launchMode;
//获取主题themeObj
Resources.Theme themeObj = loadedPlugin.getResources().newTheme();
themeObj.applyStyle(info.theme, true);
//由launchMode、themeObj来获取占坑的Activity名字
String stubActivity = mStubActivityInfo.getStubActivity(targetClassName, launchMode, themeObj);
//将占坑的Activity名字设置给intent
intent.setClassName(mContext, stubActivity);
}
injectIntent 方法实际就是两点,一是判断要启动的 Activity 是否是插件中的,而是如果是的话,将 intent 替换为占坑的 Activity。最后调用系统原有的 execStartActivity 方法启动 Activity。
其中 intent 中的三个参数表明要启动的插件 Activity
public static final String KEY_IS_PLUGIN = "isPlugin";
public static final String KEY_TARGET_PACKAGE = "target.package";
public static final String KEY_TARGET_ACTIVITY = "target.activity";
handleMessage 的 LAUNCH_ACTIVITY,由上图 execStartActivity 后会走到 handler mH 的 LAUNCH_ACTIVITY 判断中,这里仅仅设置了主题。
ActivityThread 会通过H类将代码的逻辑切换到主线程,H 类是 ActivityThread 的内部类并继承 Handler。H 类重写 handlerMessage 方法会对 LAUNCH_ACTIVITY 类型的消息进行处理。这里也可以作为还原插件 Activity 的 Hook 点。
@Override
public boolean handleMessage(Message msg) {
if (msg.what == LAUNCH_ACTIVITY) {
// r为ActivityClientRecord类型
Object r = msg.obj;
try {
/**
* static final class ActivityClientRecord {
* //...
* Intent intent;
* //...
* ActivityInfo activityInfo;
* //...
* }
*/
Reflector reflector = Reflector.with(r);
Intent intent = reflector.field("intent").get();//获取intent对象
intent.setExtrasClassLoader(mPluginManager.getHostContext().getClassLoader());//设置自己的ClassLoader
ActivityInfo activityInfo = reflector.field("activityInfo").get();
if (PluginUtil.isIntentFromPlugin(intent)) {
int theme = PluginUtil.getTheme(mPluginManager.getHostContext(), intent);
if (theme != 0) {
//设置主题
activityInfo.theme = theme;
}
}
} catch (Exception e) {
Log.w(TAG, e);
}
}
}
newActivity(),之后通过上图流程到达 newActivity() 方法,newActivity 方法会用类加载器来创建 Activity 实例,这里必须从 intent 中读取要创建的插件 Activity 来创建
@Override
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
try {
//解析ClassName
cl.loadClass(className);
} catch (ClassNotFoundException e) {
//由intent获取原来插件的信息
ComponentName component = PluginUtil.getComponent(intent);
if (component == null) {
return newActivity(mBase.newActivity(cl, className, intent));
}
String targetClassName = component.getClassName();//com.didi.virtualapk.demo.aidl.BookManagerActivity
Log.i(TAG, String.format("newActivity[%s : %s/%s]", className, component.getPackageName(), targetClassName));
LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(component);//从缓存中找到相关信息
//...
//调用系统的newActivity
Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);
activity.setIntent(intent);
// for 4.1+
return newActivity(activity);
}
return newActivity(mBase.newActivity(cl, className, intent));
}
此方法还原了插件 Activity,并调用系统 newActivity 方法去创建还原的实例。其中自身的 newActivity 实际是将创建出来的 activity 实例进行了缓存。
callActivityOnCreate(),最后调用与 onCreate 方法相关的方法中
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) {
injectActivity(activity);
mBase.callActivityOnCreate(activity, icicle, persistentState);
}
protected void injectActivity(Activity activity) {
final Intent intent = activity.getIntent();
if (PluginUtil.isIntentFromPlugin(intent)) {
Context base = activity.getBaseContext();
try {
LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent);
//反射Context将mResources替换为解析插件时候创建的Resources
Reflector.with(base).field("mResources").set(plugin.getResources());
//得到插件中Activity实例,替换实例中的mBase、mApplication分别为解析插件时创建的
Reflector reflector = Reflector.with(activity);
reflector.field("mBase").set(plugin.createPluginContext(activity.getBaseContext()));
reflector.field("mApplication").set(plugin.getApplication());
//设置横竖屏
ActivityInfo activityInfo = plugin.getActivityInfo(PluginUtil.getComponent(intent));
if (activityInfo.screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
activity.setRequestedOrientation(activityInfo.screenOrientation);
}
//
ComponentName component = PluginUtil.getComponent(intent);
Intent wrapperIntent = new Intent(intent);
wrapperIntent.setClassName(component.getPackageName(), component.getClassName());
activity.setIntent(wrapperIntent);
} catch (Exception e) {
Log.w(TAG, e);
}
}
}
设置了 mResources、mBase、mApplication、横竖屏等。注意这里的 mBase 的替换,下文介绍 ContentProvider 插件化时会用到。
VirtualAPK 已有两年没有更新了,有很多坑需要自己挖掘,google 也不支持插件化,但是相关的技术需要了解。
最后 Hook mInstrumentation和Handler。
protected void hookInstrumentationAndHandler() {
try {
//绕过系统隐藏API,获取ActivityThread实例
ActivityThread activityThread = ActivityThread.currentActivityThread();
//拿到系统原有的Instrumentation
Instrumentation baseInstrumentation = activityThread.getInstrumentation();
//创建自己的Instrumentation
/**
* protected VAInstrumentation createInstrumentation(Instrumentation origin) throws Exception {
* return new VAInstrumentation(this, origin);
* }
*/
final VAInstrumentation instrumentation = createInstrumentation(baseInstrumentation);
//activityThread中mInstrumentation替换为instrumentation
Reflector.with(activityThread).field("mInstrumentation").set(instrumentation);
//获取activityThread中getHandler()方法
/**
* final Handler getHandler() {
* return mH;
* }
*/
Handler mainHandler = Reflector.with(activityThread).method("getHandler").call();
//Handler中mCallback替换为instrumentation,instrumentation实现了Handler.Callback接口
Reflector.with(mainHandler).field("mCallback").set(instrumentation);
//给mInstrumentation赋值
this.mInstrumentation = instrumentation;
Log.d(TAG, "hookInstrumentationAndHandler succeed : " + mInstrumentation);
} catch (Exception e) {
Log.w(TAG, e);
}
}
还有另一种 Hook
在 android源码中的反射、代理的应用 文章中对 8.0 和 7.0 的 AMS 做了介绍。7.0 调用getDefault() 方法,返回了 IActivityManager 类型的对象,IActivityManager 借助了Singleton 来实现单例,而 getDefault() 又是静态的,因此 IActivityManager 是一个比较好的 Hook 点。8.0 中 getService 方法返回了 IActivityManager 类型的对象,并且 IActivityManager 借助了 Singleton 类实现单例,因此 IActivityManager 是比较好的 Hook 点。
protected void hookSystemServices() {
try {
//Hook IActivityManager开始
//Singleton也是隐藏API,这里也实现了
/**
* public abstract class Singleton<T> {
* public Singleton() {
* throw new RuntimeException("Stub!");
* }
*
* protected abstract T create();
*
* public T get() {
* throw new RuntimeException("Stub!");
* }
* }
*/
Singleton<IActivityManager> defaultSingleton;
//8.0后不一致
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//8.0中getService方法返回了IActivityManager类型的对象,并且IActivityManager借助了Singleton类实现单例
//获取ActivityManager中IActivityManagerSingleton实例,类型是Singleton<IActivityManager>
/**
* 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;
* }
* };
*/
defaultSingleton = Reflector.on(ActivityManager.class).field("IActivityManagerSingleton").get();
} else {
//7.0调用getDefault()方法,返回了IActivityManager类型的对象,IActivityManager借助了Singleton来实现单例,而getDefault()又是静态的
defaultSingleton = Reflector.on(ActivityManagerNative.class).field("gDefault").get();
}
//获取IActivityManager实例
IActivityManager origin = defaultSingleton.get();
//创建ActivityManagerProxy对象,将原来的作为参数,实现了InvocationHandler函数
ActivityManagerProxy proxy = createActivityManagerProxy(origin);
//代理对象activityManagerProxy
IActivityManager activityManagerProxy = (IActivityManager) Proxy.newProxyInstance(mContext.getClassLoader(), new Class[]{IActivityManager.class}, proxy);
//将defaultSingleton中mInstance替换为我们创建的代理对象activityManagerProxy
/**
* public abstract class Singleton<T> {
* private T mInstance;
* }
*/
Reflector.with(defaultSingleton).field("mInstance").set(activityManagerProxy);
if (defaultSingleton.get() == activityManagerProxy) {
//赋值mActivityManager
this.mActivityManager = activityManagerProxy;
Log.d(TAG, "hookSystemServices succeed : " + mActivityManager);
}
} catch (Exception e) {
Log.w(TAG, e);
}
}
自然还原 Activity 可以 Hook H 类。
Service 插件化
Activity 插件化需要保证它的生命周期,而 Service 插件化重点是保证它的优先级,这时候就需要一个真正的 Service 来实现。这就会用到上文 Hook 的 ActivityManagerProxy 对象。
启动插件 Service
Intent intent = new Intent();
intent.setClassName(this, "com.didi.virtualapk.demo.messenger.MessengerService");
startService(intent);
VirtualAPK 中注册的 Service
<service
android:name="com.didi.virtualapk.delegate.LocalService"
android:exported="false" />
<!-- Daemon Service running in child process -->
<service
android:name="com.didi.virtualapk.delegate.RemoteService"
android:exported="false"
android:process=":daemon">
<intent-filter>
<action android:name="${applicationId}.intent.ACTION_DAEMON_SERVICE" />
</intent-filter>
</service>
ActivityManagerProxy 的 Hook 已说,看下它的实现。
public class ActivityManagerProxy implements InvocationHandler {
private PluginManager mPluginManager;
private IActivityManager mActivityManager;
public ActivityManagerProxy(PluginManager pluginManager, IActivityManager activityManager) {
this.mPluginManager = pluginManager;
this.mActivityManager = activityManager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e("ActivityManagerProxy invoke", method.getName());
if ("startService".equals(method.getName())) {
try {
return startService(proxy, method, args);
} catch (Throwable e) {
Log.e(TAG, "Start service error", e);
}
} else if ("stopService".equals(method.getName())) {
//其他
}
}
}
我们在 Activity 中调用 startService 会调用 invoke 方法,这里根据方法名进行拦截,走进自己实现的 startService(proxy, method, args);
protected Object startService(Object proxy, Method method, Object[] args) throws Throwable {
IApplicationThread appThread = (IApplicationThread) args[0];
Intent target = (Intent) args[1];
//与Activity一致,从缓存中匹配intent,判断Service是否在插件中
ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0);
if (null == resolveInfo || null == resolveInfo.serviceInfo) {
//不存在
return method.invoke(this.mActivityManager, args);
}
return startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null, RemoteService.EXTRA_COMMAND_START_SERVICE);
}
由 Activity 的基础,这里与之相似,直接看下 startDelegateServiceForTarget() 的实现。
protected ComponentName startDelegateServiceForTarget(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) {
Intent wrapperIntent = wrapperTargetIntent(target, serviceInfo, extras, command);
return mPluginManager.getHostContext().startService(wrapperIntent);
}
protected Intent wrapperTargetIntent(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) {
//com.didi.virtualapk.demo com.didi.virtualapk.demo.messenger.MessengerService
target.setComponent(new ComponentName(serviceInfo.packageName, serviceInfo.name));
///storage/emulated/0/app-beijing-release.apk
String pluginLocation = mPluginManager.getLoadedPlugin(target.getComponent()).getLocation();
//这里启动RemoteService
boolean local = PluginUtil.isLocalService(serviceInfo);//false
Class<? extends Service> delegate = local ? LocalService.class : RemoteService.class;
Intent intent = new Intent();
intent.setClass(mPluginManager.getHostContext(), delegate);
intent.putExtra(RemoteService.EXTRA_TARGET, target);//"target"
intent.putExtra(RemoteService.EXTRA_COMMAND, command);//"command" RemoteService.EXTRA_COMMAND_START_SERVICE
intent.putExtra(RemoteService.EXTRA_PLUGIN_LOCATION, pluginLocation);//"plugin_location"
if (extras != null) {
intent.putExtras(extras);
}
return intent;
}
startDelegateServiceForTarget 将原本要启动的插件 Service 对象在 intent 内保存,之后启动 RemoteService。之后就是在 RemoteService 中代理分发了。
public static final String EXTRA_TARGET = "target";
public static final String EXTRA_COMMAND = "command";
public static final String EXTRA_PLUGIN_LOCATION = "plugin_location";
RemoteService 继承 LocalService
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
return super.onStartCommand(intent, flags, startId);
}
//得到要启动的Intent target
Intent target = intent.getParcelableExtra(EXTRA_TARGET);
if (target != null) {
//得到插件Service的位置
String pluginLocation = intent.getStringExtra(EXTRA_PLUGIN_LOCATION);
//插件Service的包名信息
ComponentName component = target.getComponent();
LoadedPlugin plugin = PluginManager.getInstance(this).getLoadedPlugin(component);
//如果没有解析插件Apk,则需要解析下
if (plugin == null && pluginLocation != null) {
try {
PluginManager.getInstance(this).loadPlugin(new File(pluginLocation));
} catch (Exception e) {
Log.w(TAG, e);
}
}
}
//调用父类LocalService的方法
return super.onStartCommand(intent, flags, startId);
}
LocalService 的 onStartCommand()
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (null == intent || !intent.hasExtra(EXTRA_TARGET) || !intent.hasExtra(EXTRA_COMMAND)) {
return START_STICKY;
}
//得到要启动的target
Intent target = intent.getParcelableExtra(EXTRA_TARGET);
//得到command
int command = intent.getIntExtra(EXTRA_COMMAND, 0);
if (null == target || command <= 0) {
return START_STICKY;
}
//启动的intent target获取插件的包名信息
ComponentName component = target.getComponent();
LoadedPlugin plugin = mPluginManager.getLoadedPlugin(component);
if (plugin == null) {
Log.w(TAG, "Error target: " + target.toURI());
return START_STICKY;
}
//设置ClassLoader
target.setExtrasClassLoader(plugin.getClassLoader());
//startService传入的是EXTRA_COMMAND_START_SERVICE
switch (command) {
case EXTRA_COMMAND_START_SERVICE: {
//获取当前的ActivityThread实例,隐藏API
ActivityThread mainThread = ActivityThread.currentActivityThread();
//获取IApplicationThread 的binder对象
IApplicationThread appThread = mainThread.getApplicationThread();
Service service;
//缓存中检测是否已经存在了,存在直接返回,不存在则创建
/**
* ComponentsHandler类中
* private ArrayMap<ComponentName, Service> mServices = new ArrayMap<ComponentName, Service>();
*/
if (this.mPluginManager.getComponentsHandler().isServiceAvailable(component)) {
service = this.mPluginManager.getComponentsHandler().getService(component);
} else {
try {
//创建Service实例,已经加载插件Service实例
service = (Service) plugin.getClassLoader().loadClass(component.getClassName()).newInstance();
//得到Application实例
Application app = plugin.getApplication();
//得到Token,隐藏API,同样方法获取
IBinder token = appThread.asBinder();
//获取方法attach
/**
* public final void attach(
* Context context,
* ActivityThread thread, String className, IBinder token,
* Application application, Object activityManager) {
* attachBaseContext(context);
* mThread = thread; // NOTE: unused - remove?
* mClassName = className;
* mToken = token;
* mApplication = application;
* mActivityManager = (IActivityManager)activityManager;
* mStartCompatibility = getApplicationInfo().targetSdkVersion
* < Build.VERSION_CODES.ECLAIR;
* }
*/
Method attach = service.getClass().getMethod("attach", Context.class, ActivityThread.class, String.class, IBinder.class, Application.class, Object.class);
//得到IActivityManager实例
IActivityManager am = mPluginManager.getActivityManager();
//反射执行插件Service的attach方法,并传入此前得到的参数
attach.invoke(service, plugin.getPluginContext(), mainThread, component.getClassName(), token, app, am);
//执行onCreate方法
service.onCreate();
//放入缓存
this.mPluginManager.getComponentsHandler().rememberService(component, service);
} catch (Throwable t) {
return START_STICKY;
}
}
//执行Service的onStartCommand方法
/**
* ComponentsHandler类中
* private ArrayMap<Service, AtomicInteger> mServiceCounters = new ArrayMap<Service, AtomicInteger>();
*/
service.onStartCommand(target, 0, this.mPluginManager.getComponentsHandler().getServiceCounter(service).getAndIncrement());
break;
}
}
return START_STICKY;
}
ActivityManagerProxy 类中还定义了 stopService、bindService、unbindService 等方法,原理与之类似。
ContentProvider 插件化
ContentProvider 插件化的关键在于将 ContentProvider 插件共享给整个系统。和 Service 插件化类似,需要一个真正的 ContentProvider 作为代理 ContentProvider,并把这个代理 ContentProvider 共享给整个系统,对于插件 ContentProvider 请求会全部交由代理 ContentProvider 处理并分发给对应的插件 ContentProvider。
ContentProvider 流程图
VirtualAPK 中注册的 ContentProvider
<provider
android:name="com.didi.virtualapk.delegate.RemoteContentProvider"
android:authorities="${applicationId}.VirtualAPK.Provider"
android:exported="false"
android:process=":daemon" />
启动插件 ContentProvider
Uri bookUri = Uri.parse("content://com.didi.virtualapk.demo.book.provider/book");
LoadedPlugin plugin = PluginManager.getInstance(this).getLoadedPlugin(pkg);
bookUri = PluginContentResolver.wrapperUri(plugin, bookUri);
Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null);
if (bookCursor != null) {
while (bookCursor.moveToNext()) {
int bookId = bookCursor.getInt(0);
String bookName = bookCursor.getString(1);
Log.d("ryg", "query book:" + bookId + ", " + bookName);
}
bookCursor.close();
}
getContentResolver() 方法调用 mBase 的 getContentResolver() 方法
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
在执行 VAInstrumentation 的 callActivityOnCreate 时,已经将 mBase 替换为我们创建的插件 mBase。创建 PluginContext 在上文解析 apk 是提到。
reflector.field("mBase").set(plugin.createPluginContext(activity.getBaseContext()));
所以这里调用 getContentResolver 方法实际是调用 PluginContext 的 getContentResolver 方法。
@Override
public ContentResolver getContentResolver() {
return new PluginContentResolver(getHostContext());
}
创建了 PluginContentResolver
public class PluginContentResolver extends ContentResolverWrapper {
private PluginManager mPluginManager;
public PluginContentResolver(Context context) {
super(context);
mPluginManager = PluginManager.getInstance(context);
}
}
public abstract class ContentResolverWrapper extends ContentResolver {
ContentResolver mBase;
public ContentResolverWrapper(Context context) {
super(context);
mBase = context.getContentResolver();
}
}
调用 query 方法,query 方法会调用到 acquireUnstableProvider 方法。
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
protected IContentProvider acquireUnstableProvider(Context context, String auth) {
//判断插件中是否存在
if (mPluginManager.resolveContentProvider(auth, 0) != null) {
return mPluginManager.getIContentProvider();
}
return super.acquireUnstableProvider(context, auth);
}
getIContentProvider()
public synchronized IContentProvider getIContentProvider() {
if (mIContentProvider == null) {
hookIContentProviderAsNeeded();
}
return mIContentProvider;
}
重点在 hookIContentProviderAsNeeded()中
protected void hookIContentProviderAsNeeded() {
//获取代理ContentResolverde Uri
Uri uri = Uri.parse(RemoteContentProvider.getUri(mContext));
//调用call方法,mContext是宿主的,所以这里调用的是宿主的ContentProviderde call方法
mContext.getContentResolver().call(uri, "wakeup", null, null);
try {
Field authority = null;
Field provider = null;
//获取ActivityThread
ActivityThread activityThread = ActivityThread.currentActivityThread();
//得到ActivityThread中mProviderMap
/**
* final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
*/
Map providerMap = Reflector.with(activityThread).field("mProviderMap").get();
//遍历mProviderMap找到匹配的IContentProvider
Iterator iter = providerMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Object key = entry.getKey();
//ProviderClientRecord实例
Object val = entry.getValue();
String auth;
if (key instanceof String) {
auth = (String) key;
} else {
if (authority == null) {
authority = key.getClass().getDeclaredField("authority");
authority.setAccessible(true);
}
auth = (String) authority.get(key);
}
if (auth.equals(RemoteContentProvider.getAuthority(mContext))) {
if (provider == null) {
/**
* final class ProviderClientRecord {
* final String[] mNames;
* final IContentProvider mProvider;
* }
*/
provider = val.getClass().getDeclaredField("mProvider");
provider.setAccessible(true);
}
IContentProvider rawProvider = (IContentProvider) provider.get(val);
/**
* public static IContentProvider newInstance(Context context, IContentProvider iContentProvider) {
* return (IContentProvider) Proxy.newProxyInstance(iContentProvider.getClass().getClassLoader(),
* new Class[] { IContentProvider.class }, new IContentProviderProxy(context, iContentProvider));
* }
*/
IContentProvider proxy = IContentProviderProxy.newInstance(mContext, rawProvider);
//用代理的IContentProviderProxy替换IContentProvider,完成Hook IContentProvider
mIContentProvider = proxy;
Log.d(TAG, "hookIContentProvider succeed : " + mIContentProvider);
break;
}
}
} catch (Exception e) {
Log.w(TAG, e);
}
}
将系统的 IContentProvider 替换成我们自己的代理对象 IContentProviderProxy,之后相关逻辑就在 IContentProviderProxy 中处理。
IContentProviderProxy 的 invoke 实现
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.v(TAG, method.toGenericString() + " : " + Arrays.toString(args));
wrapperUri(method, args);
try {
return method.invoke(mBase, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
wrapperUri()
private void wrapperUri(Method method, Object[] args) {
//...
PluginManager pluginManager = PluginManager.getInstance(mContext);
ProviderInfo info = pluginManager.resolveContentProvider(uri.getAuthority(), 0);
if (info != null) {
String pkg = info.packageName;
LoadedPlugin plugin = pluginManager.getLoadedPlugin(pkg);
//获取插件Uri,并拼接成一个新的Uri
/**
* String pkg = loadedPlugin.getPackageName();
* String pluginUriString = Uri.encode(pluginUri.toString());
* StringBuilder builder = new StringBuilder(RemoteContentProvider.getUri(loadedPlugin.getHostContext()));
* builder.append("/?plugin=" + loadedPlugin.getLocation());
* builder.append("&pkg=" + pkg);
* builder.append("&uri=" + pluginUriString);
* Uri wrapperUri = Uri.parse(builder.toString());
*/
Uri wrapperUri = PluginContentResolver.wrapperUri(plugin, uri);
if (method.getName().equals("call")) {
bundleInCallMethod.putString(RemoteContentProvider.KEY_WRAPPER_URI, wrapperUri.toString());
} else {
//替换Uir
args[index] = wrapperUri;
}
}
}
这里并没有和 Activity、Service 一致用三个静态属性,但是却一一对应。
public static final String KEY_PKG = "pkg";
public static final String KEY_PLUGIN = "plugin";
public static final String KEY_URI = "uri";
这样我们启动插件的 ContentProvider 时,会先启动代理的 ContentProvider。
RemoteContentProvider 中 query()
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
//获取ContentProvider ,调用它的query()
ContentProvider provider = getContentProvider(uri);
Uri pluginUri = Uri.parse(uri.getQueryParameter(KEY_URI));
if (provider != null) {
return provider.query(pluginUri, projection, selection, selectionArgs, sortOrder);
}
return null;
}
getContentProvider() 和 service 中 onStartCommand 方法类似,创建 ContentProvider,条例清晰。
private ContentProvider getContentProvider(final Uri uri) {
final PluginManager pluginManager = PluginManager.getInstance(getContext());
Uri pluginUri = Uri.parse(uri.getQueryParameter(KEY_URI));
final String auth = pluginUri.getAuthority();
//缓存
/**
* private static Map<String, ContentProvider> sCachedProviders = new HashMap<>();
*/
ContentProvider cachedProvider = sCachedProviders.get(auth);
if (cachedProvider != null) {
return cachedProvider;
}
synchronized (sCachedProviders) {
//由pkg得到LoadedPlugin
LoadedPlugin plugin = pluginManager.getLoadedPlugin(uri.getQueryParameter(KEY_PKG));
//插件没有加载则加载插件apk
if (plugin == null) {
try {
pluginManager.loadPlugin(new File(uri.getQueryParameter(KEY_PLUGIN)));
} catch (Exception e) {
Log.w(TAG, e);
}
}
//从已经加载的apk中得到匹配auh的ProviderInfo,根据ProviderInfo就可以创建插件ContentProvider
final ProviderInfo providerInfo = pluginManager.resolveContentProvider(auth, 0);
if (providerInfo != null) {
//切主线程
RunUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
LoadedPlugin loadedPlugin = pluginManager.getLoadedPlugin(uri.getQueryParameter(KEY_PKG));
//创建ContentProvider实例
ContentProvider contentProvider = (ContentProvider) Class.forName(providerInfo.name).newInstance();
//调用插件的attachInfo,其内部会为插件ContentProvider配置参数并调用它的onCreate方法,这样插件就启动了。
contentProvider.attachInfo(loadedPlugin.getPluginContext(), providerInfo);
//放入缓存。
sCachedProviders.put(auth, contentProvider);
} catch (Exception e) {
Log.w(TAG, e);
}
}
}, true);
return sCachedProviders.get(auth);
}
}
return null;
}
BroadcastReceiver 插件化
BroadcastReceiver 插件化上文已经提到,因为<intent-filer>标签无法预料,所以不能用占坑方式创建,只有全部动态的注册来处理,这里不说介绍了。
总结
Google 在 2018 年推出了Android App Bundles,它可以实现模块的动态下载,但是与插件化不同的是,它并不支持四大组件代理的能力。

和热修复一样,如果使用 AppComponentFactory API,我们也可以实现插件化的四大组件代理。但是具体实现上依然需要在 AndroidManifest 中预先注册四大组件,然后具体的替换规则可以在我们自定义的 AppComponentFactory 实现类中埋好。
热修复和插件化作为 Native 动态化方案,它们有一定的局限性。随着移动技术的发展,部分功能可能会被替换成小程序等其他动态化方案。但是从目前来看,它们依然有非常大的存在价值和使用场景。

本文深入探讨Android四大组件插件化的实现原理,包括Activity、Service、ContentProvider和BroadcastReceiver的插件化策略,以及如何利用Hook技术实现组件代理。
491

被折叠的 条评论
为什么被折叠?



