一、概述
1.1 相关技术点
- 设计模式 — 动态代理模式
- 源码分析 — Activity的清单注册校验
- 源码分析 — ActivityThread(二)之ActivityThread的浅析
- 源码分析 — Activity的启动流程
- 源码分析 — PackageManagerService(一)之启动流程
- 源码分析 — PackageManagerService(二)之resolveIntent()
- 源码分析 — Binder机制(一)(进程间通信)
- 源码分析 — Binder机制(二)之IActivityManager
- 源码分析 — VirtualAPK框架(一)之初始化
1.2 参考文章
1.3 版本
- Android Framework:
Api 23
- VirtualAPK:
com.didi.virtualapk:core 0.9.0
二、Activity
2.1 原理
Activity的启动会涉及到以下两个过程:( 参考:《源码分析 — Activity的清单注册校验》)
- 校验: 校验逻辑在
Instrumentation.execStartActivity()
方法中进行;- 创建: 创建Activity类在
Instrumentation.newActivity()
方法中进行;实现方案:Hook 、Activity占坑
- 预先在底座(宿主)中给 Activity 占坑;
- 在
Instrumentation.execStartActivity()
中将插件中被启动的Activity替换为底座(宿主)中的 Activity,从而绕过校验;- 在
Instrumentation.newActivity()
中将占坑的 Activity 还原回插件中的 Activity,然后创建 Activity;
2.2 代码分析
CoreLibrary 中 AndroidManifest 里占坑的Activity,各种启动模式都有;
<activity android:name="com.didi.virtualapk.core.A$1" android:launchMode="standard" />
<activity android:name="com.didi.virtualapk.core.A$2" android:launchMode="standard"
android:theme="@android:style/Theme.Translucent" />
<activity android:name="com.didi.virtualapk.core.B$1" android:launchMode="singleTop" />
<activity android:name="com.didi.virtualapk.core.C$1" android:launchMode="singleTask" />
<activity android:name="com.didi.virtualapk.core.D$1" android:launchMode="singleInstance" />
下面是执行流程:
说明: VAInstrumentation.execStartActivity()
方法主要有两步操作:
- 通过
VAInstrumentation.execStartActivity()
做一些预处理,将目标 Activity 替换为坑位 Activity; - 执行系统的
Instrumentation.execStartActivity()
方法完成校验过程;
// VAInstrumentation.class
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent);
// null component is an implicitly intent
if (intent.getComponent() != null) {
// 1.根据是否为插件中Activity,来判断是否替换为占坑的Activity;
this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent);
}
// 2.执行系统的 Instrumentation.execStartActivity() 方法完成校验过程;
ActivityResult result = realExecStartActivity(who, contextThread, token, target,
intent, requestCode, options);
return result;
}
说明: 当目标类在插件中时,执行以下步骤:
- 将目标类的包名和类名存储在Bundle中;
- 调用
dispatchStubActivity()
方法获取替换的坑位 Activity信息;
// ComponentsHandler.class
public void markIntentIfNeeded(Intent intent) {
if (intent.getComponent() == null) {
return;
}
String targetPackageName = intent.getComponent().getPackageName();
String targetClassName = intent.getComponent().getClassName();
// 这里判断启动的是否是底座(宿主)的Activity;
if (!targetPackageName.equals(mContext.getPackageName()) && mPluginManager.getLoadedPlugin(targetPackageName) != null) {
// 1.这里添加一个是否为插件的标记;
intent.putExtra(Constants.KEY_IS_PLUGIN, true);
/* * 将目标的PackageName和ClassName存储到Bundle中, * 便于在newActivity()中获取要还原的目标Activity信息; */
intent.putExtra(Constants.KEY_TARGET_PACKAGE, targetPackageName);
intent.putExtra(Constants.KEY_TARGET_ACTIVITY, targetClassName);
// 2.获取替换的坑位 Activity 信息;
dispatchStubActivity(intent);
}
}
说明: 将插件中的 Activity 替换为底座(宿主)中的坑位 Activity (启动模式要匹配);
- 根据 Intent 从插件中匹配出对应的 ActivityInfo;
- 从 ActivityInfo 中获取主题和启动模式;
- 根据启动模式去底座(宿主)中查找对应的坑位Activity信息;
- 将坑位的 Activity 信息添加进 Intent 中;
// ComponentsHandler.class
private void dispatchStubActivity(Intent intent) {
ComponentName component = intent.getComponent();
String targetClassName = intent.getComponent().getClassName();
// 1.根据Intent去插件中查询ActivityInfo;
LoadedPlugin loadedPlugin = mPluginManager.getLoadedPlugin(intent);
ActivityInfo info = loadedPlugin.getActivityInfo(component);
if (info == null) {
throw new RuntimeException("can not find " + component);
}
// 2.获取启动模式;
int launchMode = info.launchMode;
Resources.Theme themeObj = loadedPlugin.getResources().newTheme();
themeObj.applyStyle(info.theme, true);
/* * 3.根据启动模式,去选择与之匹配的坑位,然后通过目标类名作为Key缓存对应的坑位信息, * 便于在此启动时能快速进行类型匹配; */
String stubActivity = mStubActivityInfo.getStubActivity(targetClassName, launchMode, themeObj);
Log.i(TAG, String.format("dispatchStubActivity,[%s -> %s]", targetClassName, stubActivity));
// 4.这里就将我们目标的Component信息替换为坑位的信息了;
intent.setClassName(mContext, stubActivity);
}
说明:
- 将插件中的 Activity 替换为底座(宿主)中的坑位 Activity (启动模式要匹配);
- 将匹配到的信息存储在Map集合中,便于下次打开时能快速获取;
// StubActivityInfo.class
public String getStubActivity(String className, int launchMode, Theme theme) {
String stubActivity= mCachedStubActivity.get(className);
if (stubActivity != null) {
return stubActivity;
}
// 主题
TypedArray array = theme.obtainStyledAttributes(new int[]{
android.R.attr.windowIsTranslucent,
android.R.attr.windowBackground
});
boolean windowIsTranslucent = array.getBoolean(0, false);
array.recycle();
stubActivity = String.format(STUB_ACTIVITY_STANDARD, corePackage, usedStandardStubActivity);
// 1.根据给定的启动模式,选择启动模式相同的坑位;
switch (launchMode) {
case ActivityInfo.LAUNCH_MULTIPLE: {
stubActivity = String.format(STUB_ACTIVITY_STANDARD, corePackage, usedStandardStubActivity);
if (windowIsTranslucent) {
stubActivity = String.format(STUB_ACTIVITY_STANDARD, corePackage, 2);
}
break;
}
case ActivityInfo.LAUNCH_SINGLE_TOP: {
usedSingleTopStubActivity = usedSingleTopStubActivity % MAX_COUNT_SINGLETOP + 1;
stubActivity = String.format(STUB_ACTIVITY_SINGLETOP, corePackage, usedSingleTopStubActivity);
break;
}
case ActivityInfo.LAUNCH_SINGLE_TASK: {
usedSingleTaskStubActivity = usedSingleTaskStubActivity % MAX_COUNT_SINGLETASK + 1;
stubActivity = String.format(STUB_ACTIVITY_SINGLETASK, corePackage, usedSingleTaskStubActivity);
break;
}
case ActivityInfo.LAUNCH_SINGLE_INSTANCE: {
usedSingleInstanceStubActivity = usedSingleInstanceStubActivity % MAX_COUNT_SINGLEINSTANCE + 1;
stubActivity = String.format(STUB_ACTIVITY_SINGLEINSTANCE, corePackage, usedSingleInstanceStubActivity);
break;
}
default:break;
}
// 2.将启动的Activity类名作为键,底座中启动模式相同的坑位作为Value,便于二次打开后可以快速获取坑位类型;
mCachedStubActivity.put(className, stubActivity);
return stubActivity;
}
说明: 通过 VAInstrumentation.execStartActivity()
的预处理,将目标 Activity 替换为坑位 Activity;
- 调用
Instrumentation.execStartActivity()
方法完成校验步骤;
// VAInstrumentation.class
private ActivityResult realExecStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
ActivityResult result = null;
try {
Class[] parameterTypes = {Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class,
int.class, Bundle.class};
// 这里通过反射调用了Instrumentation.execStartActivity()方法;
result = (ActivityResult)ReflectUtil.invoke(Instrumentation.class, mBase,
"execStartActivity", parameterTypes,
who, contextThread, token, target, intent, requestCode, options);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
以上部分就是绕过Android系统校验Activity的过程;
实际上,在接下来的创建Activity过程中,如果不还原回我们的目标Activity,那么真正启动的会是坑位Activity;
那如何才能启动我们的目标Activity呢?
说明:
- 在 VirtualApk 初始化时,将 VAInstrumentation 通过反射赋值给
mCallback
( VAInstrumentation实现Handler.Callback
接口); mCallback
存在的情况下,会先执行mCallback.handleMessage(msg)
方法;- 执行
Handler.handlerMessage(msg)
方法;
// Handler.class
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
// 1.在VirtualApk初始化时,将VAInstrumentation通过反射赋值给mCallback;
if (mCallback != null) {
// 2.先执行
if (mCallback.handleMessage(msg)) {
return;
}
}
// 3.后执行(这就可以在系统ActivityThread.H.handleMessage()之前,做预处理了)
handleMessage(msg);
}
}
说明:
- 设置类加载器;
- 设置主题;
// VAInstrumentation.class
public boolean handleMessage(Message msg) {
if (msg.what == LAUNCH_ACTIVITY) {
// ActivityClientRecord r
Object r = msg.obj;
try {
Intent intent = (Intent) ReflectUtil.getField(r.getClass(), r, "intent");
// 1.设置类加载器
intent.setExtrasClassLoader(VAInstrumentation.class.getClassLoader());
ActivityInfo activityInfo = (ActivityInfo) ReflectUtil.getField(r.getClass(), r, "activityInfo");
// 2.设置插件中目标Activity的主题
if (PluginUtil.isIntentFromPlugin(intent)) {
int theme = PluginUtil.getTheme(mPluginManager.getHostContext(), intent);
if (theme != 0) {
activityInfo.theme = theme;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
说明: 通过 VAInstrumentation.newActivity()
进行预处理,将坑位 Activity 替换为目标 Activity;
- 从 Intent 的 Bundle 中获取之前的目标类;
- 调用系统的 Instrumentation.newActivity() 方法来创建目标 Activity 对象
- 将坑位接收的 Intent 信息传递给目标 Activity;
// VAInstrumentation.class
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
try {
// 这里是启动宿主(底座)的Activity;
cl.loadClass(className);
} catch (ClassNotFoundException e) {
LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent);
// 1.从Intent的Bundle中获取之前目标类;
String targetClassName = PluginUtil.getTargetActivity(intent);
if (targetClassName != null) {
/* * 2.调用系统的 Instrumentation.newActivity() 方法来创建目标 Activity 对象; * 注:这里要使用插件的ClassLoader加载器; */
Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);
// 3.将坑位接收的Intent信息传递给目标Activity;
activity.setIntent(intent);
try {
// for 4.1+
ReflectUtil.setField(ContextThemeWrapper.class, activity, "mResources", plugin.getResources());
} catch (Exception ignored) {
// ignored.
}
return activity;
}
}
// 4.调用系统的 Instrumentation.newActivity()方法;
return mBase.newActivity(cl, className, intent);
}
说明: 在 Activity 创建之后,会调用到 Instrumentation.callActivityOnCreate()
方法;
- 将目标Activity里的
mResources,mBase,mApplication
,替换为 LoadedPlugin 中生成的可以用于加载插件资源的相应 Resources 和 Context;- 每个Activity都持有一个ContextImpl实例;
- 启动插件的Activity,其内部的
mApplication、mResources、mBase
等仍然是宿主(底座)的,需要替换成插件的mApplication、mResources、mBase
才能操作插件内部的资源等;
- 设置Activity的横竖屏方向;
// VAInstrumentation.class
public void callActivityOnCreate(Activity activity, Bundle icicle) {
final Intent intent = activity.getIntent();
// 判断是否是插件
if (PluginUtil.isIntentFromPlugin(intent)) {
Context base = activity.getBaseContext();
try {
/* * 1.将目标Activity里的mResources,mBase,mApplication, * 替换为LoadedPlugin中生成的可以用于加载插件资源的相应Resources和Context; * 注: * 1)每个Activity都持有一个ContextImpl实例; * 2)启动插件的Activity,其内部的mApplication、mResources、mBase等仍然是宿主(底座)的,需要替换成插件的mApplication、mResources、mBase才能操作插件内部的资源等; */
LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent);
ReflectUtil.setField(base.getClass(), base, "mResources", plugin.getResources());
ReflectUtil.setField(ContextWrapper.class, activity, "mBase", plugin.getPluginContext());
ReflectUtil.setField(Activity.class, activity, "mApplication", plugin.getApplication());
ReflectUtil.setFieldNoException(ContextThemeWrapper.class, activity, "mBase", plugin.getPluginContext());
// 2.set screenOrientation
ActivityInfo activityInfo = plugin.getActivityInfo(PluginUtil.getComponent(intent));
if (activityInfo.screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
activity.setRequestedOrientation(activityInfo.screenOrientation);
}
} catch (Exception e) {
e.printStackTrace();
}
}
mBase.callActivityOnCreate(activity, icicle);
}
到此,整个Activity 的创建就已经结束了;
三、BroadcastReceiver
说明: 广播注册由 静态 转 动态
;
- 在初始化插件时,会将插件清单文件中静态广播动态的注册到底座(宿主)中;
LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws PackageParser.PackageParserException {
// Register broadcast receivers dynamically
Map<ComponentName, ActivityInfo> receivers = new HashMap<ComponentName, ActivityInfo>();
for (PackageParser.Activity receiver : this.mPackage.receivers) {
receivers.put(receiver.getComponentName(), receiver.info);
try {
BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance());
for (PackageParser.ActivityIntentInfo aii : receiver.intents) {
// 动态注册到底座(宿主)中;
this.mHostContext.registerReceiver(br, aii);
}
} catch (Exception e) {
e.printStackTrace();
}
}
this.mReceiverInfos = Collections.unmodifiableMap(receivers);
this.mPackageInfo.receivers = receivers.values().toArray(new ActivityInfo[receivers.size()]);
}
四、Service
CoreLibrary 中 AndroidManifest 里占坑的 Service,一种是主进程,一种的其他进程;
<!-- Local Service running in main process -->
<service android:name="com.didi.virtualapk.delegate.LocalService" />
<!-- Daemon Service running in child process -->
<service
android:name="com.didi.virtualapk.delegate.RemoteService"
android:process=":daemon" >
<intent-filter>
<action android:name="${applicationId}.intent.ACTION_DAEMON_SERVICE" />
</intent-filter>
</service>
说明: 启动Service时,会调用系统的 ActivityManagerProxy.startService()
方法,而之前提到过,系统的 ActivityManagerProxy
已经被 VirtualApk 中的 ActivityManagerProxy
动态代理了,所以最终会先调用 VirtualApk 中的 ActivityManagerProxy.invoke()
方法;
// ActivityManagerProxy.class
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startService".equals(method.getName())) {
return startService(proxy, method, args);
}
// ...省略stopService()、stopServiceToken()、bindService()、unbindService()等方法...
try {
// sometimes system binder has problems.
return method.invoke(this.mActivityManager, args);
} catch (Throwable th) {
// ...
}
说明:
- 根据给定的 Intent 去插件中匹配对应的 Service;
- 如果没有匹配到,说明目标 Service 在底座中;
- 如果匹配到,则说明目标 Service 在某一个已经被加载的插件中;
private Object startService(Object proxy, Method method, Object[] args) throws Throwable {
IApplicationThread appThread = (IApplicationThread) args[0];
Intent target = (Intent) args[1];
// 1.mPluginManager是ActivityManagerProxy对象
ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0);
if (null == resolveInfo || null == resolveInfo.serviceInfo) {
// 2.启动底座(宿主)的Service
return method.invoke(this.mActivityManager, args);
}
// 3.启动插件中的Service
return startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null, RemoteService.EXTRA_COMMAND_START_SERVICE);
}
说明:
- 给目标 Service 设置 ComponentName;
- 判断当前 Service 启动的进程:
- 若在主进程,使用
LocalService
进行分发; - 若不在主进程,则使用
RemoteService
进行分发;
- 若在主进程,使用
- 新建一个 Intent,存储底座中真正代理的 Service 信息;
- 将目标 Service 信息存入到代理 Service 的 Intent 中;
- 启动底座中的代理 Service;
private ComponentName startDelegateServiceForTarget(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) {
Intent wrapperIntent = wrapperTargetIntent(target, serviceInfo, extras, command);
// 5.启动底座的Service,让它去分发插件中的Service;
return mPluginManager.getHostContext().startService(wrapperIntent);
}
private Intent wrapperTargetIntent(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) {
// 1.fill in service with ComponentName
target.setComponent(new ComponentName(serviceInfo.packageName, serviceInfo.name));
String pluginLocation = mPluginManager.getLoadedPlugin(target.getComponent()).getLocation();
// 2.start delegate service to run plugin service inside
boolean local = PluginUtil.isLocalService(serviceInfo);
Class<? extends Service> delegate = local ? LocalService.class : RemoteService.class;
Intent intent = new Intent();
// 3.启动宿主的Service,用来分发插件中的Service
intent.setClass(mPluginManager.getHostContext(), delegate);
// 4.将插件中真正要启动的 Service 信息存入代理 Service 的 Intent 中;
intent.putExtra(RemoteService.EXTRA_TARGET, target);
intent.putExtra(RemoteService.EXTRA_COMMAND, command);
intent.putExtra(RemoteService.EXTRA_PLUGIN_LOCATION, pluginLocation);
if (extras != null) {
intent.putExtras(extras);
}
return intent;
}
说明: 由上一步可知,最终底座的代理 Service 被启动;
- 根据是否有EXTRA_TARGET 或 EXTRA_COMMAND 来判断启动的是否是插件的 Service;
- 取出了插件中真正要启动的 Service 信息;
- 判断这个插件的 Service 是否被启动过;
- 通过反射构造插件的 Service 对象;
- 调用 Service.attach() 进行关联;
- 代理执行插件中Service.onCreate()方法;
- 将启动过的 Service 存储起来(当Service被销毁时,从这里移除);
- 代理执行插件中Service.onStartCommand()方法;
// LocalService.class
public int onStartCommand(Intent intent, int flags, int startId) {
// 1.根据是否有EXTRA_TARGET 或 EXTRA_COMMAND 来判断启动的是否是插件的 Service;
if (null == intent || !intent.hasExtra(EXTRA_TARGET) || !intent.hasExtra(EXTRA_COMMAND)) {
return START_STICKY;
}
// 2.取出了插件中真正要启动的 Service 信息;
Intent target = intent.getParcelableExtra(EXTRA_TARGET);
int command = intent.getIntExtra(EXTRA_COMMAND, 0);
if (null == target || command <= 0) {
return START_STICKY;
}
ComponentName component = target.getComponent();
LoadedPlugin plugin = mPluginManager.getLoadedPlugin(component);
switch (command) {
case EXTRA_COMMAND_START_SERVICE: {
ActivityThread mainThread = (ActivityThread)ReflectUtil.getActivityThread(getBaseContext());
IApplicationThread appThread = mainThread.getApplicationThread();
Service service;
// 3.判断这个插件的 Service 是否被启动过;
if (this.mPluginManager.getComponentsHandler().isServiceAvailable(component)) {
service = this.mPluginManager.getComponentsHandler().getService(component);
} else {
try {
// 4.通过反射构造插件的 Service 对象;
service = (Service) plugin.getClassLoader().loadClass(component.getClassName()).newInstance();
Application app = plugin.getApplication();
IBinder token = appThread.asBinder();
Method attach = service.getClass().getMethod("attach", Context.class, ActivityThread.class, String.class, IBinder.class, Application.class, Object.class);
IActivityManager am = mPluginManager.getActivityManager();
// 5.调用 Service.attach() 进行关联;
attach.invoke(service, plugin.getPluginContext(), mainThread, component.getClassName(), token, app, am);
// 6.代理执行插件中Service.onCreate()方法
service.onCreate();
// 7.将启动过的 Service 存储起来(当Service被销毁时,从这里移除)
this.mPluginManager.getComponentsHandler().rememberService(component, service);
} catch (Throwable t) {
return START_STICKY;
}
}
// 8.代理执行插件中Service.onStartCommand()方法
service.onStartCommand(target, 0, this.mPluginManager.getComponentsHandler().getServiceCounter(service).getAndIncrement());
break;
}
case EXTRA_COMMAND_BIND_SERVICE:
case EXTRA_COMMAND_STOP_SERVICE:
case EXTRA_COMMAND_UNBIND_SERVICE:
}
return START_STICKY;
}
五、ContentProvider
略