插件加载
来源:互联网 发布:淘宝上兼职是真的吗 编辑:程序博客网 时间: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最容易,但是有一个限制,因为静态广播当做动态处理,所以插件未启动时是无法接收对应的静态广播的。
- 插件加载
- Nutch插件加载分析
- 动态加载插件
- eclipse未加载插件
- Nutch插件加载分析
- nutch插件加载机制
- Eclipse插件加载日志
- QTP 插件加载
- nutch插件加载机制
- myeclipse 加载插件
- eclipse重新加载插件
- 加载自定义插件
- Jrebel 热加载插件
- Nutch插件加载流程
- JS插件如何加载
- 图片预加载插件
- VLC 加载插件仓库
- JQuery加载更多插件
- tarjin+暴力 [POI2008]枪战Maf
- java jdbc连接数据库
- 设计模式学习笔记(原型模式)
- Activity的四种启动模式
- HDU 2871 Memory Control(线段树区间合并+各种综合运用)【好题】
- 插件加载
- 解决PL/SQL Developer连接数据库时出现 “ORA-12541:TNS:无监听程序”错误。
- 面试
- 程序员必读书单 1.0
- 信号管理器
- Jmap
- <c++>多态
- 【File】File类基础
- C#利用反射调用基类私有方法 及 Unity实现自定义InputField