dependencies {
classpath 'com.didi.virtualapk:gradle:0.9.8.6'
}
複製代碼
apply plugin: 'com.didi.virtualapk.host'
複製代碼
dependencies {
implementation 'com.didi.virtualapk:core:0.9.8'
}
複製代碼
public class VirtualApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
PluginManager.getInstance(base).init();
}
}
複製代碼
classpath 'com.didi.virtualapk:gradle:0.9.8.6'
複製代碼
apply plugin: 'com.didi.virtualapk.plugin'
複製代碼
virtualApk{
// 插件資源表中的packageId,須要確保不一樣插件有不一樣的packageId
// 範圍 0x1f - 0x7f
packageId = 0x6f
// 宿主工程application模塊的路徑,插件的構建須要依賴這個路徑
// targetHost能夠設置絕對路徑或相對路徑
targetHost = '../../../VirtualAPkDemo/app'
// 默認爲true,若是插件有引用宿主的類,那麼這個選項可使得插件和宿主保持混淆一致
//這個標誌會在加載插件時起做用
applyHostMapping = true
}
複製代碼
signingConfigs {
release {
storeFile file('/Users/wuliangliang/AndroidSubjectStudyProject/PluginProject/VirtualAPkDemo/keystore/keystore') storePassword '123456' keyAlias = 'key'
keyPassword '123456'
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
複製代碼
執行assemablePlugin 產生Plugin文件java
將插件Plugin安裝到手機中android
adb push ./app/build/outputs/plugin/release/com.alex.kotlin.virtualplugin_20190729172001.apk /sdcard/plugin_test.apk
複製代碼
private void loadApk() {
File apkFile = new File(Environment.getExternalStorageDirectory(), "Test.apk");
if (apkFile.exists()) {
try {
PluginManager.getInstance(this).loadPlugin(apkFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}
複製代碼
在插件下載或安裝到設備後,獲取插件的文件,調用PluginManager.loadPlugin()加載插件,PluginManager會完成全部的代碼解析和資源加載,詳細內容後面的源碼分析; 2. 執行界面跳轉至插件中緩存
final String pkg = "com.alex.kotlin.virtualplugin」; //插件Plugin的包名
Intent intent = new Intent();
intent.setClassName(pkg, "com.alex.kotlin.virtualplugin.MainPluginActivity」); //目標Activity的全路徑
startActivity(intent);
複製代碼
PluginManager.getInstance(base).init();
複製代碼
public static PluginManager getInstance(Context base) {
if (sInstance == null) {
synchronized (PluginManager.class) {
if (sInstance == null) {
sInstance = createInstance(base); // 調用createInstance()方法建立PluginManager,單例對外提供
}
}
}
return sInstance;
}
複製代碼
private static PluginManager createInstance(Context context) {
try {
//一、獲取metaData
Bundle metaData = context.getPackageManager()
.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA)
.metaData;
if (metaData == null) {
return new PluginManager(context); //二、
}
String factoryClass = metaData.getString("VA_FACTORY」); //三、此處獲取的是什麼?
if (factoryClass == null) {
return new PluginManager(context);
}
//四、建立PluginManager
PluginManager pluginManager = Reflector.on(factoryClass).method("create", Context.class).call(context);
if (pluginManager != null) {
return pluginManager;
}
} catch (Exception e) {
Log.w(TAG, "Created the instance error!", e);
}
return new PluginManager(context);
}
複製代碼
createInstance()執行邏輯:cookie
protected PluginManager(Context context) {
if (context instanceof Application) { // 一、
this.mApplication = (Application) context;
this.mContext = mApplication.getBaseContext();
} else {
final Context app = context.getApplicationContext();
if (app == null) {
this.mContext = context;
this.mApplication = ActivityThread.currentApplication();
} else {
this.mApplication = (Application) app;
this.mContext = mApplication.getBaseContext();
}
}
mComponentsHandler = createComponentsHandler(); //二、
hookCurrentProcess(); //三、
}
複製代碼
PluginManager()執行流程:app
protected void hookCurrentProcess() {
hookInstrumentationAndHandler();
hookSystemServices();
}
複製代碼
ActivityThread activityThread = ActivityThread.currentActivityThread();
Instrumentation baseInstrumentation = activityThread.getInstrumentation(); //一、
final VAInstrumentation instrumentation = createInstrumentation(baseInstrumentation); //二、
//三、
Reflector.with(activityThread).field("mInstrumentation").set(instrumentation);
//四、
Handler mainHandler = Reflector.with(activityThread).method("getHandler").call();
Reflector.with(mainHandler).field("mCallback").set(instrumentation);
this.mInstrumentation = instrumentation; // 賦值 PluginManager的mInstrumentation
複製代碼
執行流程:ide
Singleton<IActivityManager> defaultSingleton;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//一、
//8.0 以上獲取IActivityManagerSingleton,採用AIDL獲取AMS
defaultSingleton = Reflector.on(ActivityManager.class).field("IActivityManagerSingleton").get();
} else {
//8.0 以前獲取gDefault,使用代理執行AMS
defaultSingleton = Reflector.on(ActivityManagerNative.class).field("gDefault").get();
}
IActivityManager origin = defaultSingleton.get(); //2
IActivityManager activityManagerProxy = (IActivityManager) Proxy.newProxyInstance(mContext.getClassLoader(), new Class[]{IActivityManager.class},createActivityManagerProxy(origin));//3
Reflector.with(defaultSingleton).field("mInstance").set(activityManagerProxy); // 四、
if (defaultSingleton.get() == activityManagerProxy) {
this.mActivityManager = activityManagerProxy;//五、
}
複製代碼
hook系統服務在Hook 技術 文章中已經接收過程了,這裏簡單介紹邏輯:函數
由四大組件啓動過程源碼分析,Hook了Instrumentation對象就能夠完成對Activity的建立過程的修改,Hook了系統的IActivityManager能夠實現對AMS工做的攔截,而AMS對四大組件的整個工做過程相當重要,也就是說咱們已經掌控了四大組件;工具
public void loadPlugin(File apk) throws Exception {
//一、
LoadedPlugin plugin = createLoadedPlugin(apk);
//二、
this.mPlugins.put(plugin.getPackageName(), plugin);
}
複製代碼
loadPlugin()中主要完成兩件事:源碼分析
public LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws Exception {
//一、保存安裝包的APk路徑、pluginManager、context
this.mPluginManager = pluginManager;
this.mHostContext = context;
this.mLocation = apk.getAbsolutePath();
//二、解析Plugin的AndroidManifest.xml文件
this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
this.mPackage.applicationInfo.metaData = this.mPackage.mAppMetaData;
//三、建立並實例化PackageInfo對象
this.mPackageInfo = new PackageInfo();
this.mPackageInfo.applicationInfo = this.mPackage.applicationInfo;
this.mPackageInfo.applicationInfo.sourceDir = apk.getAbsolutePath();
if (Build.VERSION.SDK_INT >= 28
|| (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0)) {
try {
this.mPackageInfo.signatures = this.mPackage.mSigningDetails.signatures;
} catch (Throwable e) {
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
this.mPackageInfo.signatures = info.signatures;
}
} else {
this.mPackageInfo.signatures = this.mPackage.mSignatures;
}
//保存包名
this.mPackageInfo.packageName = this.mPackage.packageName;
this.mPackageInfo.versionCode = this.mPackage.mVersionCode;
this.mPackageInfo.versionName = this.mPackage.mVersionName;
this.mPackageInfo.permissions = new PermissionInfo[0];
//四、建立插件中本身的PackManager
this.mPackageManager = createPluginPackageManager();
//五、建立插件中本身的PluginContext
this.mPluginContext = createPluginContext(null);
this.mNativeLibDir = getDir(context, Constants.NATIVE_DIR);
//獲取so文件路徑
this.mPackage.applicationInfo.nativeLibraryDir = this.mNativeLibDir.getAbsolutePath();
//六、建立Resource和ClassLoader對象
this.mResources = createResources(context, getPackageName(), apk);
//七、建立ClassLoader對象
this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader());
tryToCopyNativeLib(apk);
Map<ComponentName, ActivityInfo> activityInfos = new HashMap<ComponentName, ActivityInfo>();
for (PackageParser.Activity activity : this.mPackage.activities) {
activity.info.metaData = activity.metaData;
activityInfos.put(activity.getComponentName(), activity.info); // 八、緩存插件中解析的Activity
}
this.mPackageInfo.activities = activityInfos.values().toArray(new ActivityInfo[activityInfos.size()]);
Map<ComponentName, ServiceInfo> serviceInfos = new HashMap<ComponentName, ServiceInfo>();
for (PackageParser.Service service : this.mPackage.services) {
serviceInfos.put(service.getComponentName(), service.info); // 九、緩存插件中解析的服務Service
}
this.mPackageInfo.services = serviceInfos.values().toArray(new ServiceInfo[serviceInfos.size()]);
Map<String, ProviderInfo> providers = new HashMap<String, ProviderInfo>();
Map<ComponentName, ProviderInfo> providerInfos = new HashMap<ComponentName, ProviderInfo>();
for (PackageParser.Provider provider : this.mPackage.providers) {
providers.put(provider.info.authority, provider.info);
providerInfos.put(provider.getComponentName(), provider.info); // 十、緩存插件中解析的ContentProvider
}
this.mPackageInfo.providers = providerInfos.values().toArray(new ProviderInfo[providerInfos.size()]);
// 十一、緩存插件中解析的廣播,對於靜態註冊的廣播改成動態註冊
Map<ComponentName, ActivityInfo> receivers = new HashMap<ComponentName, ActivityInfo>();
for (PackageParser.Activity receiver : this.mPackage.receivers) {
receivers.put(receiver.getComponentName(), receiver.info);
BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance());
for (PackageParser.ActivityIntentInfo aii : receiver.intents) {
this.mHostContext.registerReceiver(br, aii); // 註冊廣播
}
}
this.mPackageInfo.receivers = receivers.values().toArray(new ActivityInfo[receivers.size()]);
invokeApplication(); // 十二、初始化Plugin的Application
}
複製代碼
protected class PluginPackageManager extends PackageManager {
protected PackageManager mHostPackageManager = mHostContext.getPackageManager(); //1
@Override
public PackageInfo getPackageInfo(String packageName, int flags) throws NameNotFoundException {
LoadedPlugin plugin = mPluginManager.getLoadedPlugin(packageName); // 2
if (null != plugin) {
return plugin.mPackageInfo; // 3
}
return this.mHostPackageManager.getPackageInfo(packageName, flags); //4
}
}
複製代碼
PluginPackageManager繼成PackageManager重些其中的方法,在調用方法時判斷獲取的是插件APK仍是宿主APK,並對應處理返回,具體以下:佈局
public PluginContext createPluginContext(Context context) {
if (context == null) {
return new PluginContext(this); //一、由前面知道傳入contetx爲null,執行此處傳遞LoadedPlugin對象
}
return new PluginContext(this, context);
}
複製代碼
class PluginContext extends ContextWrapper { // 一、繼承ContextWrapper,用於在Activity中使用替換原來的Context
private final LoadedPlugin mPlugin;
public PluginContext(LoadedPlugin plugin) {
super(plugin.getPluginManager().getHostContext()); // 賦值mBase對象,此處使用HostContext
this.mPlugin = plugin; // 保存插件建立的Plugin對象
}
@Override
public Context getApplicationContext() {
return this.mPlugin.getApplication();
}
private Context getHostContext() {
return getBaseContext();
}
@Override
public ContentResolver getContentResolver() {
return new PluginContentResolver(getHostContext());
}
@Override
public ClassLoader getClassLoader() {
return this.mPlugin.getClassLoader();
}
@Override
public Resources getResources() {
return this.mPlugin.getResources();
}
@Override
public void startActivity(Intent intent) {
// 重寫startActivity處理Intent
ComponentsHandler componentsHandler = mPlugin.getPluginManager().getComponentsHandler();
componentsHandler.transformIntentToExplicitAsNeeded(intent);
super.startActivity(intent);
}
}
複製代碼
在Activity中一些資源和服務都是經過Context獲取的,而Context是ContextWrapper的子類,在PluginContext中一樣繼承ContextWrapper,重寫一系列的Context方法,在加載插件時使用PluginContext替代插件APK中的Context、控制插件中資源和一些類的獲取,例如代碼中的getResources()、getContentResolver()等;
protected Resources createResources(Context context, String packageName, File apk) throws Exception {
if (Constants.COMBINE_RESOURCES) {// 1
return ResourcesManager.createResources(context, packageName, apk); // 2
} else {
Resources hostResources = context.getResources();
AssetManager assetManager = createAssetManager(context, apk);
return new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration());//3
}
}
複製代碼
由Android系統知道,程序在啓動時將資源加載到程序對應的Resource中,此處就是收動將插件apk中的資源加載到resource中,執行流程:
public static synchronized Resources createResources(Context hostContext, String packageName, File apk) throws Exception {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //一、處理7.0 以後 Resource的建立
return createResourcesForN(hostContext, packageName, apk);
}
//二、處理7.0 以前,使用AssetManager添加資源
Resources resources = ResourcesManager.createResourcesSimple(hostContext, apk.getAbsolutePath());
ResourcesManager.hookResources(hostContext, resources);
return resources;
}
複製代碼
private static Resources createResourcesForN(Context context, String packageName, File apk) throws Exception {
String newAssetPath = apk.getAbsolutePath(); // Plugin apk的路徑
ApplicationInfo info = context.getApplicationInfo();
String baseResDir = info.publicSourceDir; // host的文件路徑
info.splitSourceDirs = append(info.splitSourceDirs, newAssetPath); //二、
LoadedApk loadedApk = Reflector.with(context).field("mPackageInfo").get(); //三、
Reflector rLoadedApk = Reflector.with(loadedApk).field("mSplitResDirs");
String[] splitResDirs = rLoadedApk.get();
rLoadedApk.set(append(splitResDirs, newAssetPath));
final ResourcesManager resourcesManager = android.app.ResourcesManager.getInstance();
ArrayMap<ResourcesKey, WeakReference<ResourcesImpl>> originalMap = //四、Reflector.with(resourcesManager).field("mResourceImpls").get();
synchronized (resourcesManager) {
HashMap<ResourcesKey, WeakReference<ResourcesImpl>> resolvedMap = new HashMap<>();
if (Build.VERSION.SDK_INT >= 28
|| (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0)) { // P Preview
ResourcesManagerCompatForP.resolveResourcesImplMap(originalMap, resolvedMap, context, loadedApk);
} else {
ResourcesManagerCompatForN.resolveResourcesImplMap(originalMap, resolvedMap, baseResDir, newAssetPath);
}
originalMap.clear();
originalMap.putAll(resolvedMap);
}
android.app.ResourcesManager.getInstance().appendLibAssetForMainAssetPath(baseResDir, packageName + ".vastub");
//五、
Resources newResources = context.getResources();
for (LoadedPlugin plugin : PluginManager.getInstance(context).getAllLoadedPlugins()) {
plugin.updateResources(newResources);
}
return newResources;
}
複製代碼
Resource的建立流程:
Resources resources = ResourcesManager.createResourcesSimple(hostContext, apk.getAbsolutePath());//建立Resource對象
ResourcesManager.hookResources(hostContext, resources); // 反射更新設置宿主Context中的resource
private static Resources createResourcesSimple(Context hostContext, String apk) throws Exception {
Resources hostResources = hostContext.getResources();
Resources newResources = null;
AssetManager assetManager;
Reflector reflector = Reflector.on(AssetManager.class).method("addAssetPath", String.class); //一、
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { // Android 5.0 以前
assetManager = AssetManager.class.newInstance();
reflector.bind(assetManager);
//二、
final int cookie1 = reflector.call(hostContext.getApplicationInfo().sourceDir);
} else {
assetManager = hostResources.getAssets(); //三、
reflector.bind(assetManager);
}
final int cookie2 = reflector.call(apk); // 四、
List<LoadedPlugin> pluginList = PluginManager.getInstance(hostContext).getAllLoadedPlugins();
for (LoadedPlugin plugin : pluginList) { // 五、
final int cookie3 = reflector.call(plugin.getLocation());
}
//六、適配不一樣的手機類型,建立新的Resource對象
if (isMiUi(hostResources)) {
newResources = MiUiResourcesCompat.createResources(hostResources, assetManager);
} else if (isVivo(hostResources)) {
newResources = VivoResourcesCompat.createResources(hostContext, hostResources, assetManager);
} else if (isNubia(hostResources)) {
newResources = NubiaResourcesCompat.createResources(hostResources, assetManager);
} else if (isNotRawResources(hostResources)) {
newResources = AdaptationResourcesCompat.createResources(hostResources, assetManager);
} else {
newResources = new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration());
}
// 七、將全部LoadedPlugin同步到新的newResources
for (LoadedPlugin plugin : pluginList) {
plugin.updateResources(newResources);
}
return newResources;
}
複製代碼
實現原理:建立一個新的AssetManager對象,使用AssetManager的addAssetPath()依次將全部插件和宿主中的資源路徑添加到對象中,根據新的AssetManager對象建立Resource對象,具體細節:
protected ClassLoader createClassLoader(Context context, File apk, File libsDir, ClassLoader parent) throws Exception {
File dexOutputDir = getDir(context, Constants.OPTIMIZE_DIR);
String dexOutputPath = dexOutputDir.getAbsolutePath();//一、獲取原APP中的dex文件路徑
//二、建立DexClassLoader對象,同時加載host已經存在的dex文件和插件apk中的文件
DexClassLoader loader = new DexClassLoader(apk.getAbsolutePath(), dexOutputPath, libsDir.getAbsolutePath(), parent);
if (Constants.COMBINE_CLASSLOADER) {
DexUtil.insertDex(loader, parent, libsDir);//三、將全部的dex資源合併到宿主的ClassLoader中
}
return loader;
}
複製代碼
createClassLoader()中先獲取當前app和插件的dex文件路徑,而後建立DexClassLoader同時加載兩個文件中的全部資源,在調用DexUtil.insertDex()合併dex資源,關於ClassLoader不瞭解的點擊Android熱修復之路(一)——ClassLoader
public static void insertDex(DexClassLoader dexClassLoader, ClassLoader baseClassLoader, File nativeLibsDir) throws Exception {
Object baseDexElements = getDexElements(getPathList(baseClassLoader)); // 一、獲取傳入的host默認的ClassLoader中加載的Elements集合
Object newDexElements = getDexElements(getPathList(dexClassLoader)); //二、獲取dexClassLoader中加載的Elements(host & plugin)
Object allDexElements = combineArray(baseDexElements, newDexElements); // 三、合併Elements
Object pathList = getPathList(baseClassLoader);
Reflector.with(pathList).field("dexElements").set(allDexElements); // 四、反射設置合併後的Elements集合
insertNativeLibrary(dexClassLoader, baseClassLoader, nativeLibsDir);
}
複製代碼
this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
this.mPackage.applicationInfo.metaData = this.mPackage.mAppMetaData;
//三、建立並實例化PackageInfo對象
this.mPackageInfo = new PackageInfo();
複製代碼
Map<ComponentName, ActivityInfo> receivers = new HashMap<ComponentName, ActivityInfo>();
for (PackageParser.Activity receiver : this.mPackage.receivers) {
receivers.put(receiver.getComponentName(), receiver.info);
BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance());
for (PackageParser.ActivityIntentInfo aii : receiver.intents) {
this.mHostContext.registerReceiver(br, aii);
}
}
複製代碼
public void invokeApplication() throws Exception {
final Exception[] temp = new Exception[1];
RunUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
mApplication = makeApplication(false, mPluginManager.getInstrumentation()); //
} catch (Exception e) {
temp[0] = e;
}
}
}, true);
}
複製代碼
protected Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) throws Exception {
String appClass = this.mPackage.applicationInfo.className;
if (forceDefaultAppClass || null == appClass) {
appClass = "android.app.Application」; //設置執行的類
}
//
this.mApplication = instrumentation.newApplication(this.mClassLoader, appClass, this.getPluginContext());
mApplication.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacksProxy());
//調用Application的onCreate(),此處執行的是插件APK中的Application
instrumentation.callApplicationOnCreate(this.mApplication);
return this.mApplication;
}
複製代碼
在makeApplication()內部調用instrumentation.newApplication()建立Application對象和執行Application的onCreate(),關於instrumentation中如何建立的參考(四大組件的啓動過程)
Activity在啓動過程當中調用Instrumentation的execStartActivity(),在VAInstrumentation中重寫此方法攔截啓動Activity過程,在此方法中修改處理Intent
@Override
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode) {
injectIntent(intent); //一、
return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode); //二、調用原來邏輯繼續執行
}
複製代碼
protected void injectIntent(Intent intent) {
//此處調用的getComponentsHandler()就是拿到前面建立的ComponentsHandler對象
mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent);
if (intent.getComponent() != null) {
this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent);
}
}
複製代碼
public Intent transformIntentToExplicitAsNeeded(Intent intent) {
ComponentName component = intent.getComponent();
if (component == null || component.getPackageName().equals(mContext.getPackageName())) {
ResolveInfo info = mPluginManager.resolveActivity(intent); // 1
if (info != null && info.activityInfo != null) {
component = new ComponentName(info.activityInfo.packageName, info.activityInfo.name); //2
intent.setComponent(component);
}
}
return intent;
}
複製代碼
public void markIntentIfNeeded(Intent intent) {
String targetPackageName = intent.getComponent().getPackageName(); //一、 名
String targetClassName = intent.getComponent().getClassName();
if (!targetPackageName.equals(mContext.getPackageName()) && mPluginManager.getLoadedPlugin(targetPackageName) != null) {
intent.putExtra(Constants.KEY_IS_PLUGIN, true); //二、
intent.putExtra(Constants.KEY_TARGET_PACKAGE, targetPackageName);
intent.putExtra(Constants.KEY_TARGET_ACTIVITY, targetClassName);
dispatchStubActivity(intent); //三、
}
}
複製代碼
在markIntentIfNeeded()中:
private void dispatchStubActivity(Intent intent) {
ComponentName component = intent.getComponent();
String targetClassName = intent.getComponent().getClassName();
LoadedPlugin loadedPlugin = mPluginManager.getLoadedPlugin(intent); // 一、
ActivityInfo info = loadedPlugin.getActivityInfo(component); //二、
int launchMode = info.launchMode; // 三、
Resources.Theme themeObj = loadedPlugin.getResources().newTheme();
themeObj.applyStyle(info.theme, true);
//四、
String stubActivity = mStubActivityInfo.getStubActivity(targetClassName, launchMode, themeObj);
intent.setClassName(mContext, stubActivity); //五、
}
複製代碼
在VirtualAPk的註冊清單中提早註冊好了不一樣條件的佔位Activity,dispatchStubActivity()中主要從VirtualAPk中選擇合適的佔位Activity:
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
try {
cl.loadClass(className); // 一、ClassLoader加載類名
} catch (ClassNotFoundException e) { // 二、拋出ClassNotFoundException,表示啓動插件Activity
ComponentName component = PluginUtil.getComponent(intent);
String targetClassName = component.getClassName(); //三、
LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(component);
// 四、建立Plugin的Activity真正的實例
Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);
activity.setIntent(intent);
//五、
Reflector.QuietReflector.with(activity).field("mResources").set(plugin.getResources());
return newActivity(activity);
}
return newActivity(mBase.newActivity(cl, className, intent)); // 對於ClassLoader中查找到的直接調用原來方法初始化Activity
}
複製代碼
在Activity的啓動過程當中會回調newActivity()建立對象,因此在VAInstrumentation重寫此方法:
public ActivityManagerProxy(PluginManager pluginManager, IActivityManager activityManager) {
this.mPluginManager = pluginManager;
this.mActivityManager = activityManager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startService".equals(method.getName())) {
try {
return startService(proxy, method, args); // 處理啓動服務
}
} else if ("stopService".equals(method.getName())) {
try {
return stopService(proxy, method, args); // 處理中止服務
}
} else if ("stopServiceToken".equals(method.getName())) {
try {
return stopServiceToken(proxy, method, args);
}
} else if ("bindService".equals(method.getName())) {
try {
return bindService(proxy, method, args); // 處理綁定服務
}
} else if ("unbindService".equals(method.getName())) {
try {
return unbindService(proxy, method, args); // 處理解綁服務
}
}
try {
return method.invoke(this.mActivityManager, args);
}
}
複製代碼
由前面Pluginmanager初始化過程知道VirtualAPk Hook了系統的AMS,Hook中傳遞的Binder對象就是ActivityManagerProxy對象,因此AMS對Service的任何操做都會通過此處的invoke(),咱們就能夠在invoke()方法中干涉啓動過程;
protected Object startService(Object proxy, Method method, Object[] args) throws Throwable {
IApplicationThread appThread = (IApplicationThread) args[0]; // 一、
Intent target = (Intent) args[1];
ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0);//二、查找處理Intent,務
if (null == resolveInfo || null == resolveInfo.serviceInfo) {
return method.invoke(this.mActivityManager, args); //三、
}
return startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null, RemoteService.EXTRA_COMMAND_START_SERVICE);//四、處理
}
protected ComponentName startDelegateServiceForTarget(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) {
Intent wrapperIntent = wrapperTargetIntent(target, serviceInfo, extras, command); //五、構造代理服務的Intent
return mPluginManager.getHostContext().startService(wrapperIntent); //六、
}
複製代碼
在ActivityManagerProxy的invoke()中攔截到startService()後調用startService(),在startService()中執行一下邏輯:
protected Intent wrapperTargetIntent(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) {
target.setComponent(new ComponentName(serviceInfo.packageName, serviceInfo.name));
String pluginLocation = mPluginManager.getLoadedPlugin(target.getComponent()).getLocation();//一、
boolean local = PluginUtil.isLocalService(serviceInfo); // 二、
Class<? extends Service> delegate = local ? LocalService.class : RemoteService.class;//三、
Intent intent = new Intent();
intent.setClass(mPluginManager.getHostContext(), delegate); // 三、
intent.putExtra(RemoteService.EXTRA_TARGET, target);
intent.putExtra(RemoteService.EXTRA_COMMAND, command);
intent.putExtra(RemoteService.EXTRA_PLUGIN_LOCATION, pluginLocation);
return intent;
}
複製代碼
構造代理服務Intent的流程以下:
public int onStartCommand(Intent intent, int flags, int startId) {
Intent target = intent.getParcelableExtra(EXTRA_TARGET); //一、
ComponentName component = target.getComponent();
LoadedPlugin plugin = mPluginManager.getLoadedPlugin(component);
target.setExtrasClassLoader(plugin.getClassLoader());
switch (command) {
case EXTRA_COMMAND_START_SERVICE: {
ActivityThread mainThread = ActivityThread.currentActivityThread();
IApplicationThread appThread = mainThread.getApplicationThread();
Service service;
if (this.mPluginManager.getComponentsHandler().isServiceAvailable(component)) {
service = this.mPluginManager.getComponentsHandler().getService(component); // 二、
} else {
try {
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();
attach.invoke(service, plugin.getPluginContext(), mainThread, component.getClassName(), token, app, am); //四、
service.onCreate();
this.mPluginManager.getComponentsHandler().rememberService(component, service); //五、
} catch (Throwable t) {
return START_STICKY;
}
}
//六、執行插件Service的onStartCommand()傳入參數
service.onStartCommand(target, 0, this.mPluginManager.getComponentsHandler().getServiceCounter(service).getAndIncrement());
break;
}
}
return START_STICKY;
}
複製代碼
LocalService啓動後,程序執行到onStartCommand()中,在onStartCommand()會建立插件Service的實例並完成服務的分發:
case EXTRA_COMMAND_STOP_SERVICE: {
//一、獲取Service並移除當前緩存
Service service = this.mPluginManager.getComponentsHandler().forgetService(component);
if (null != service) {
try {
service.onDestroy(); //二、調用onDestroy()
}
}
break;
}
複製代碼
protected Object bindService(Object proxy, Method method, Object[] args) throws Throwable {
Intent target = (Intent) args[2];
ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0);
Bundle bundle = new Bundle();
PluginUtil.putBinder(bundle, "sc", (IBinder) args[4]); // 一、保存綁定時傳入的ServiceConnection對象
startDelegateServiceForTarget(target, resolveInfo.serviceInfo, bundle, RemoteService.EXTRA_COMMAND_BIND_SERVICE);
mPluginManager.getComponentsHandler().remberIServiceConnection((IBinder) args[4], target); // 二、緩存IServiceConnection
return 1;
}
複製代碼
public void remberIServiceConnection(IBinder iServiceConnection, Intent intent) {
synchronized (this.mBoundServices) {
mBoundServices.put(iServiceConnection, intent); //
}
}
複製代碼
case EXTRA_COMMAND_BIND_SERVICE: {
ActivityThread mainThread = ActivityThread.currentActivityThread();
IApplicationThread appThread = mainThread.getApplicationThread();
Service service = null;
if (this.mPluginManager.getComponentsHandler().isServiceAvailable(component)) {
service = this.mPluginManager.getComponentsHandler().getService(component);
} else {
try {
service = (Service) plugin.getClassLoader().loadClass(component.getClassName()).newInstance();
......
try {
IBinder binder = service.onBind(target); // 一、回調Service的onBind()
IBinder serviceConnection = PluginUtil.getBinder(intent.getExtras(), "sc");
IServiceConnection iServiceConnection = IServiceConnection.Stub.asInterface(serviceConnection);
if (Build.VERSION.SDK_INT >= 26) {
iServiceConnection.connected(component, binder, false);//二、獲取傳入的serviceConnection,回調connected()
} else {
Reflector.QuietReflector.with(iServiceConnection).method("connected", ComponentName.class, IBinder.class).call(component, binder);
}
}
break;
}
複製代碼
綁定服務總體過程和啓動相似,只是多了兩個步驟:
protected Object unbindService(Object proxy, Method method, Object[] args) throws Throwable {
IBinder iServiceConnection = (IBinder) args[0];
Intent target = mPluginManager.getComponentsHandler().forgetIServiceConnection(iServiceConnection);
ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0);
startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null, RemoteService.EXTRA_COMMAND_UNBIND_SERVICE);
return true;
}
public Intent forgetIServiceConnection(IBinder iServiceConnection) {
synchronized (this.mBoundServices) {
Intent intent = this.mBoundServices.remove(iServiceConnection);
return intent;
}
}
複製代碼
case EXTRA_COMMAND_UNBIND_SERVICE: {
Service service = this.mPluginManager.getComponentsHandler().forgetService(component); // 獲取服務
if (null != service) {
try {
service.onUnbind(target); // 解綁服務
service.onDestroy();
}
}
break;
}
複製代碼
Uri bookUri = Uri.parse("content://com.didi.virtualapk.demo.book.provider/book」); // 查找的Uri
LoadedPlugin plugin = PluginManager.getInstance(this).getLoadedPlugin(pkg); // 一、獲取插件的LoadedPlugin
bookUri = PluginContentResolver.wrapperUri(plugin, bookUri); //二、替換Uri,用於啓動宿主代理的ContentProvider
//三、獲取ContentResolver對象,執行查詢
Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null);
複製代碼
<provider
android:exported="false"
android:name="com.didi.virtualapk.delegate.RemoteContentProvider"
android:authorities="${applicationId}.VirtualAPK.Provider"
android:process=":daemon" />
複製代碼
public static Uri wrapperUri(LoadedPlugin loadedPlugin, Uri pluginUri) {
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
Uri wrapperUri = Uri.parse(builder.toString());
return wrapperUri;
}
public static String getAuthority(Context context) {
return context.getPackageName() + ".VirtualAPK.Provider」; // 建立代理Provider啓動權限
}
public static String getUri(Context context) {
return "content://" + getAuthority(context);
}
複製代碼
和代理Service大體類似,Provider的使用也分爲兩部分,處理啓動的Intent和啓動Provider的代理分發,Intent處理主要將請求的Uri轉換爲啓動代理Provider的Uri,同時保存插件的請求信息:
@Override
public ContentResolver getContentResolver() {
return new PluginContentResolver(getHostContext());
}
複製代碼
由前面的PluginContext知道,在插件的四大組件中使用PluginContext代替Context。對於插件此處調用的是PluginContext中的getContentResolver(),建立PluginContentResolver對象
public class PluginContentResolver extends ContentResolverWrapper {
private PluginManager mPluginManager;
public PluginContentResolver(Context context) {
super(context);
mPluginManager = PluginManager.getInstance(context);
}
@Override
protected IContentProvider acquireProvider(Context context, String auth) {
//二、處理插件中的Provider
if (mPluginManager.resolveContentProvider(auth, 0) != null) {
return mPluginManager.getIContentProvider();
}
return super.acquireProvider(context, auth);
}
}
複製代碼
PluginContentResolver繼承ContentResolverWrapper類,做爲插件中使用的ContentProvider,由Provider的工做過程知道(ContentProvider使用和工做過程詳解),系統在獲取Provider對象時會調用getIContentProvider()
public synchronized IContentProvider getIContentProvider() {
if (mIContentProvider == null) {
hookIContentProviderAsNeeded(); //Hook ContentProvider 攔截請求
}
return mIContentProvider;
}
複製代碼
protected void hookIContentProviderAsNeeded() {
Uri uri = Uri.parse(RemoteContentProvider.getUri(mContext));
mContext.getContentResolver().call(uri, "wakeup", null, null);
try {
Field authority = null;
Field provider = null;
ActivityThread activityThread = ActivityThread.currentActivityThread();
//一、
Map providerMap = Reflector.with(activityThread).field("mProviderMap").get();
Iterator iter = providerMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Object key = entry.getKey();
Object val = entry.getValue();
String auth;
if (auth.equals(RemoteContentProvider.getAuthority(mContext))) { //二、
if (provider == null) {
provider = val.getClass().getDeclaredField("mProvider");
provider.setAccessible(true);
}
IContentProvider rawProvider = (IContentProvider) provider.get(val);
//三、代理指定的Provider
IContentProvider proxy = IContentProviderProxy.newInstance(mContext, rawProvider);
mIContentProvider = proxy;
break;
}
}
}
}
複製代碼
Hook ContentProvider的過程:
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
ContentProvider provider = getContentProvider(uri); // 一、獲取真正執行的Provider對象
Uri pluginUri = Uri.parse(uri.getQueryParameter(KEY_URI)); //二、獲取啓動Plugin真正的Uri
if (provider != null) {
return provider.query(pluginUri, projection, selection, selectionArgs, sortOrder);//三、
}
return null;
}
複製代碼
在宿主代理Provider中:
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(); // 二、
ContentProvider cachedProvider = sCachedProviders.get(auth); // 三、
if (cachedProvider != null) {
return cachedProvider;
}
synchronized (sCachedProviders) {
LoadedPlugin plugin = pluginManager.getLoadedPlugin(uri.getQueryParameter(KEY_PKG)); //四、
if (plugin == null) {
try {
pluginManager.loadPlugin(new File(uri.getQueryParameter(KEY_PLUGIN))); // 五、
}
}
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) Class.forName(providerInfo.name).newInstance(); // 八、
contentProvider.attachInfo(loadedPlugin.getPluginContext(), providerInfo); // 九、
sCachedProviders.put(auth, contentProvider); //十、
}
}
}, true);
return sCachedProviders.get(auth);
}
}
return null;
}
複製代碼
前面的整個過程都是在操縱代理Provider,真正牽涉到插件的Provider就在此處,整個建立Provider和執行方法流程以下:
到此整個VirtualApk的使用和源碼分析就到此結束了,從開始接觸到如今源碼分析整個過程拖了很久,經過整個流程的學習對以前的源碼知識也有了更好的鞏固,但願對想學習插件化的同窗有所幫助;