组件加载

来源:互联网 发布:如何下载网站的源码 编辑:程序博客网 时间:2024/04/20 03:56

1,组件加载

1.1 四大组件加载

1,activity加载方法

还是在ActivityThread的performLaunchActivity方法中,匹配完成之后,

ComponentName component = r.intent.getComponent();if (component == null) {     component = r.intent.resolveActivity(          mInitialApplication.getPackageManager());     r.intent.setComponent(component);}•••Activity activity = null;   try {       java.lang.ClassLoader cl = r.packageInfo.getClassLoader();       activity = mInstrumentation.newActivity(               cl, component.getClassName(), r.intent);

通过ClassLoader加载了需要的Activity类并通过反射调用构造函数创建出了Activity对象。

如果Activity组件存在于独立于宿主程序的文件之中,系统的ClassLoader怎么知道去哪里加载呢?

2,service加载方法

Startservice方法最后,在ActivityThread的handleCreateService方法中,

java.lang.ClassLoader cl = packageInfo.getClassLoader();service = (Service) cl.loadClass(data.info.name).newInstance();

同样的, 通过ClassLoader加载了需要的Service类并通过反射调用构造函数创建出了Service对象。

Bindservice 方法最后在ActivityThread的handleBindService方法中,

data.intent.setExtrasClassLoader(s.getClassLoader());

3, BroadcastReceiver加载方法

在ActivityThread的handleReceiver方法中,

java.lang.ClassLoader cl = packageInfo.getClassLoader();data.intent.setExtrasClassLoader(cl);data.intent.prepareToEnterProcess();data.setExtrasClassLoader(cl);receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();

4, ContentProvider加载方法

 在ActivityThread的installProvider方法中,

final java.lang.ClassLoader cl = c.getClassLoader();localProvider = (ContentProvider)cl. loadClass(info.name).newInstance();provider = localProvider.getIContentProvider();

c为Context(进程上下文)对象。

首先,这些组价都独立于宿主程序的文件之中, 要使存在于独立文件或者网络中的插件被成功启动,就需要解决插件类加载的问题。

1.2 框架加载过程

1,activity加载

acitvity 启动的时候必然会调用PluginCallback.java的handleLaunchActivity方法,当然,首先匹配,然后加载匹配的acitvity。加载activity代码如下,

ResolveInfo resolveInfo = mHostContext.getPackageManager().resolveActivity(stubIntent, 0);ActivityInfo stubActivityInfo = resolveInfo != null ? resolveInfo.activityInfo : null;•••PluginManager.getInstance().reportMyProcessName(stubActivityInfo.processName, targetActivityInfo.processName, targetActivityInfo.packageName); //组件的进程管理•••PluginProcessManager.preLoadApk(mHostContext, targetActivityInfo);

2,service加载

同样的, service在ServcesManager的handleCreateServiceOne方法完成匹配,匹配之后就是加载,

ResolveInfo resolveInfo = hostContext.getPackageManager().resolveService(stubIntent, 0);ServiceInfo stubInfo = resolveInfo != null ? resolveInfo.serviceInfo : null;•••PluginManager.getInstance().reportMyProcessName(stubInfo.processName, info.processName, info.packageName); //组件的进程管理PluginProcessManager.preLoadApk(hostContext, info);

3, BroadcastReceiver加载

BroadcastReceiver和其他三个组件有些不同,插件中的BroadcastReceiver最后都是以静态的方法注册到系统里面去了,

所以加载和进程管理都是由系统进行。

4, ContentProvider加载

String authority = getMyAuthority();stubInfo = getContext().getPackageManager().resolveContentProvider(authority, 0);targetInfo = PluginManager.getInstance().resolveContentProvider(targetAuthority, 0);•••PluginManager.getInstance().reportMyProcessName(stubInfo.processName, targetInfo.processName, targetInfo.packageName); //组件的进程管理•••PluginProcessManager.preLoadApk(getContext(), targetInfo);

除了BroadcastReceiver以外,其他三个组件都是调用PluginProcessManager的preLoadApk方法进行。

1.3 ClassLoader Hook

插件的加载主要是通过ClassLoader完成。ActivityThread中mPackages变量保存着整个系统中所有apk对应的LoadedApk,

一般一个apk对应一个LoadedApk, LoadedApk主要负责apk的加载。mPackages变量定义如下,

final ArrayMap<String, WeakReference<LoadedApk>> mPackages            = new ArrayMap<String, WeakReference<LoadedApk>>();

加载的时候调用LoadedApk的getClassLoader方法mClassLoader变量, mClassLoader变量定义如下,

private ClassLoader mClassLoader;

所以,如果想要将mClassLoader替换成自己的ClassLoader,只需要Hook 这个mClassLoader变量就可以,这样就可以使用自己的ClassLoader来加载插件了。

PluginProcessManager的preLoadApk方法主要逻辑如下,

1,获取当前的ActivityThread的mPackages变量,

Object object = ActivityThreadCompat.currentActivityThread();if (object != null) {     Object mPackagesObj = FieldUtils.readField(object, "mPackages");

2,通过通过ActivityThread的getPackageInfoNoCheck方法创建自己的LoadedApk对象

if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {     loadedApk = MethodUtils.invokeMethod(object, "getPackageInfoNoCheck", pluginInfo.applicationInfo, CompatibilityInfoCompat.DEFAULT_COMPATIBILITY_INFO()); } else {      loadedApk = MethodUtils.invokeMethod(object, "getPackageInfoNoCheck", pluginInfo.applicationInfo); }

3,将loadedApk对象添加到sPluginLoadedApkCache中,进行管理。

sPluginLoadedApkCache.put(pluginInfo.packageName, loadedApk);

同样的,一个插件对应一个loadedApk对象。

4,获取插件的相关路径信息,

String optimizedDirectory = PluginDirHelper.getPluginDalvikCacheDir(hostContext, pluginInfo.packageName);String libraryPath = PluginDirHelper.getPluginNativeLibraryDir(hostContext, pluginInfo.packageName);String apk = pluginInfo.applicationInfo.publicSourceDir;

5,构造PluginClassLoader对象,

ClassLoader classloader = null;try {     classloader = new PluginClassLoader(apk, optimizedDirectory, libraryPath, ClassLoader.getSystemClassLoader());} catch (Exception e) { }if(classloader==null){     PluginDirHelper.cleanOptimizedDirectory(optimizedDirectory);    classloader = new PluginClassLoader(apk, optimizedDirectory, libraryPath, ClassLoader.getSystemClassLoader());}

当然,在构造PluginClassLoader对象的是就加载插件, 详细过程请见ClassLoader机制。

6,将loadedApk的mClassLoader对象设置成PluginClassLoader对象。

synchronized (loadedApk) {    FieldUtils.writeDeclaredField(loadedApk, "mClassLoader", classloader);  } sPluginClassLoaderCache.put(pluginInfo.packageName, classloader);Thread.currentThread().setContextClassLoader(classloader);

并且将PluginClassLoader对象放入sPluginClassLoaderCache变量中。

sPluginLoadedApkCache和sPluginClassLoaderCache定义如下,

private static Map<String, ClassLoader> sPluginClassLoaderCache = new WeakHashMap<String, ClassLoader>(1);    private static Map<String, Object> sPluginLoadedApkCache = new WeakHashMap<String, Object>(1);

存放在插件对应的loadedApk和ClassLoader 对象。

PluginClassLoader比较简单,继承于DexClassLoader。详细的ClassLoader机制在此就不论述了。

原创粉丝点击