插件加载

来源:互联网 发布:淘宝上兼职是真的吗 编辑:程序博客网 时间:2024/05/23 20:18

2,插件加载

插件加载调用的是PluginManager的loadPlugin方法,该方法主要逻辑如下,

1,利用LoadedPlugin来解析和加载插件

LoadedPlugin plugin = LoadedPlugin.create(this, this.mContext, apk);

2,将插件的包名和LoadedPlugin对象放入哈希表mPlugins中,

this.mPlugins.put(plugin.getPackageName(), plugin);

mPlugins的定义如下,

private Map<String, LoadedPlugin> mPlugins = new ConcurrentHashMap<>();

也就是说,一个插件对应一个LoadedPlugin对象,因为最后四大组件匹配的时候,是需要逐个插件匹配的,所以需要保存。

3,构造插件的Application对象,

plugin.invokeApplication();

一般插件加载完成之后,就会构造插件的Application对象,否则最后四大组件都构造了, Application对象还没构造,对在

Application对象初始化的apk肯定是不行的。

LoadedPlugin的invokeApplication方法如下,

RunUtil.runOnUiThread(new Runnable() {     @Override     public void run() {          mApplication = makeApplication(false, mPluginManager.getInstrumentation());     }}, true);

切换到主线程中执行, 这个不用说,构造Application肯定在主线程中。

makeApplication方法如下,

1,获取插件的application相关信息,

String appClass = this.mPackage.applicationInfo.className;

2,调用系统的instrumentation完成application的构造以及调用其OnCreate方法,

this.mApplication = instrumentation.newApplication(this.mClassLoader, appClass, this.getPluginContext());instrumentation.callApplicationOnCreate(this.mApplication);

因为VAInstrumentation并没有实现newApplication和callApplicationOnCreate方法,因此,这2个方法都是调用系统的instrumentation对象的方法。

LoadedPlugin

插件的解析和插件信息的保存都是在LoadedPlugin中完成, LoadedPlugin的create方法如下,

public static LoadedPlugin create(PluginManager pluginManager, Context host, File apk) throws Exception {    return new LoadedPlugin(pluginManager, host, apk);}

LoadedPlugin构造方法主要逻辑如下,

1,调用PackageParserCompat的parsePackage方法解析插件

this.mLocation = apk.getAbsolutePath();this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);

2,调用createResources 方法完成Hook ResourcesManager

this.mResources = createResources(context, apk);this.mAssets = this.mResources.getAssets();

3,加载插件

this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader());

4,插件相关信息的保存

Map<ComponentName, InstrumentationInfo> instrumentations = new HashMap<ComponentName, InstrumentationInfo>();for (PackageParser.Instrumentation instrumentation : this.mPackage.instrumentation) {     instrumentations.put(instrumentation.getComponentName(), instrumentation.info);}this.mInstrumentationInfos = Collections.unmodifiableMap(instrumentations);this.mPackageInfo.instrumentation = instrumentations.values().toArray(new InstrumentationInfo[instrumentations.size()]);•••

2.1 插件解析

PackageParserCompat的parsePackage方法如下,

public static final PackageParser.Package parsePackage(final Context context, final File apk, final int flags) throws PackageParser.PackageParserException {   if (Build.VERSION.SDK_INT >= 24) {        return PackageParserV24.parsePackage(context, apk, flags);   } else if (Build.VERSION.SDK_INT >= 21) {        return PackageParserLollipop.parsePackage(context, apk, flags);   } else {        return PackageParserLegacy.parsePackage(context, apk, flags);  }}

主要根据android系统版本的不同分别调用不同的子类的parsePackage方法,

PackageParserV24的parsePackage方法如下,

static final PackageParser.Package parsePackage(Context context, File apk, int flags) throws PackageParser.PackageParserException {     PackageParser parser = new PackageParser();     PackageParser.Package pkg = parser.parsePackage(apk, flags);     ReflectUtil.invokeNoException(PackageParser.class, null, "collectCertificates",           new Class[]{PackageParser.Package.class, int.class}, pkg, flags);      return pkg;}

利用android系统中的PackageParser的内部类Package完成解析.

2.2 Hook ResourcesManager

Resources的Hook是通过createResources方法完成的,具体实现后面论述。

this.mResources = createResources(context, apk);

2.3插件加载

插件加载调用的是createClassLoader方法,

this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader());

createClassLoader方法如下,

private static ClassLoader createClassLoader(Context context, File apk, File libsDir, ClassLoader parent) {   File dexOutputDir = context.getDir(Constants.OPTIMIZE_DIR, Context.MODE_PRIVATE);   String dexOutputPath = dexOutputDir.getAbsolutePath();   DexClassLoader loader = new DexClassLoader(apk.getAbsolutePath(), dexOutputPath, libsDir.getAbsolutePath(), parent);   if (Constants.COMBINE_CLASSLOADER) {       try {           DexUtil.insertDex(loader);       } catch (Exception e) {           e.printStackTrace();      }   }return loader;}

逻辑主要分为2部分,首先构造DexClassLoader对象加载apk,然后dex文件统一管理,因为不同插件之间可以互相获取资源。

2.4 四大组件信息

1,保存Activity的信息,

Map<ComponentName, ActivityInfo> activityInfos = new HashMap<ComponentName, ActivityInfo>();for (PackageParser.Activity activity : this.mPackage.activities) {   activityInfos.put(activity.getComponentName(), activity.info);}this.mActivityInfos = Collections.unmodifiableMap(activityInfos);this.mPackageInfo.activities = activityInfos.values().toArray(new ActivityInfo[activityInfos.size()]);

2,保存Service的信息,

Map<ComponentName, ServiceInfo> serviceInfos = new HashMap<ComponentName, ServiceInfo>();for (PackageParser.Service service : this.mPackage.services) {     serviceInfos.put(service.getComponentName(), service.info);}this.mServiceInfos = Collections.unmodifiableMap(serviceInfos);this.mPackageInfo.services = serviceInfos.values().toArray(new ServiceInfo[serviceInfos.size()]);

3,保存ServProvider的信息,

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);}this.mProviders = Collections.unmodifiableMap(providers);this.mProviderInfos = Collections.unmodifiableMap(providerInfos);this.mPackageInfo.providers = providerInfos.values().toArray(new ProviderInfo[providerInfos.size()]);

4, 保存Receiver的信息,

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()]);

广播好像和其他三大组件有些不同,中间多了一个注册过程。

this.mHostContext.registerReceiver(br, aii);

就是将插件中的所有静态广播全部注册成动态广播。

为什么其他三个组件不行,因为其他三个组件必须在必须在AndroidManifest.xml中显式声明要启动的Activity。

所以说,广播的Hook最容易,但是有一个限制,因为静态广播当做动态处理,所以插件未启动时是无法接收对应的静态广播的。

原创粉丝点击