Android PluginManager 源码解析2--FrameworkClassLoader


学习Android PluginManager加载插件的机制,需要先了解系统是如何加载Activity的。

Android 启动Activity的机制

Android系统的Activity是通过 AndroidThread.performLaunchActivity()方法生成并启动的。


Activity activity = null;
try {
    java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
    activity = mInstrumentation.newActivity(
            cl, component.getClassName(), r.intent);
    StrictMode.incrementExpectedActivityCount(activity.getClass());
    r.intent.setExtrasClassLoader(cl);
    r.intent.prepareToEnterProcess();
    if (r.state != null) {
        r.state.setClassLoader(cl);
    }
} catch (Exception e) {
    if (!mInstrumentation.onException(activity, e)) {
        throw new RuntimeException(
            "Unable to instantiate activity " + component
            + ": " + e.toString(), e);
    }
}

其中r.packageInfo是一个LoadedApk对象,保存了加载dex中class文件的ClassLoader,默认是系统的PathClassLoader

通过AndroidThread源码,看出AndroidThread通过委托给InstrumentationnewActivity()方法生成Activity。


public Activity newActivity(ClassLoader cl, String className,
        Intent intent)
        throws InstantiationException, IllegalAccessException,
        ClassNotFoundException {
    return (Activity)cl.loadClass(className).newInstance();
}

newActivity()方法使用注入的ClassLoader直接load Activity的类名生成activity。

由于默认是系统的PathClassLoader,不支持从apk中加载类文件,所以我们需要更改ClassLoader来实现加载插件apk中的类文件。

之前Android PluginManager在初始化的时候通过反射更改了Context的ClassLoader .


try {
            Object mPackageInfo = ReflectionUtils.getFieldValue(ctx,
                    "mBase.mPackageInfo", true);
            frameworkClassLoader = new FrameworkClassLoader(
                    ctx.getClassLoader());
            // set Application's classLoader to FrameworkClassLoader
            ReflectionUtils.setFieldValue(mPackageInfo, "mClassLoader",
                    frameworkClassLoader, true);
        } catch (Exception e) {
            e.printStackTrace();
        }

其中mBase就是ContextImpl,具体原因参见分析1中ContextWapper的解释。

r.packageInfoContexImplmPackageInfo是同一对象。

这样之后在加载Activity类文件时就会使用替换过的FrameworkClassLoader了。

FrameworkClassLoader

FrameworkClassLoader职责比较简单,主要负责根据不同的类名选择不同的classLoader加载。


if (plugId != null) {
	String pluginId = plugId;

	PlugInfo plugin = PluginManager.getInstance().getPluginById(
			pluginId);
	if (plugin != null) {
		try {
			if (className.equals(ActivityOverider.targetClassName)) {
				String actClassName = actName;
				return plugin.getClassLoader().loadActivityClass(
						actClassName);
			} else {
				return plugin.getClassLoader().loadClass(className);
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

return super.loadClass(className, resolv);

这里有三种情况:

  • 加载插件中的Activity类

    classNameandroidx.pluginmgr.PluginActivity时,说明要加载的类是由Android PluginManager自动生成的activity代理类,具体加载实现交由PluginClassLoader.loadActivityClass()处理。

  • 加载插件中非Activity类

    className为插件的其它自定义类时,选择PluginClassLoader.loadClass()加载。

  • 加载宿主中的类

不是插件中的类,或者插件中的类加载异常了,选择默认的ClassLoader加载类。

引用作者hkxxx大神的两张图作说明:

插件启动流程图

framwork classloader类加载结构

PluginClassLoader

PluginClassLoader继承自DexClassLoader,使用FrameworkClassLoader作为其委托的父ClassLoader。

// pluginManager初始化时实例化的PluginClassLoader
PluginClassLoader loader = new PluginClassLoader(dexPath,
               dexOutputPath, frameworkClassLoader, info);
// PluginClassLoader构造函数
public PluginClassLoader(String dexPath, String optimizedDir, ClassLoader parent, PlugInfo plugin) {
	super(dexPath, optimizedDir,plugin.getPackageInfo().applicationInfo.nativeLibraryDir,parent);
	thisPlugin = plugin;
	proxyActivityLoaderMap = new HashMap<String, ClassLoader>(plugin.getActivities().size());
	this.libraryPath = plugin.getPackageInfo().applicationInfo.nativeLibraryDir;
	this.optimizedDirectory = optimizedDir;
}

PluginClassLoader维护一个DexClassLoader的map,缓存插件每个Activity的classLoader。

为什么需要为每一个Activity都生成一个classloader呢?

接着看loadActivityClass()方法


Class<?> loadActivityClass(final String actClassName) throws ClassNotFoundException {
	File dexSavePath = ActivityOverider.createProxyDex(thisPlugin, actClassName, true);
	ClassLoader actLoader = proxyActivityLoaderMap.get(actClassName);
	if (actLoader == null) {
		actLoader = new DexClassLoader(dexSavePath.getAbsolutePath(), optimizedDirectory,libraryPath, this){
			@Override
			protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
				Log.d("PlugActClassLoader("+ actClassName+")", "loadClass: " + name);
				if (ActivityOverider.targetClassName.equals(name)) {
					Class<?> c = findLoadedClass(name);
					if (c == null) {
						Log.d("PlugActClassLoader("+ actClassName+")", "findClass");
						c = findClass(name);
					}
					if (resolve) {
						resolveClass(c);
					}
					return c;
				}
				return super.loadClass(name, resolve);
			}
		};
		proxyActivityLoaderMap.put(actClassName, actLoader);
	}
	return actLoader.loadClass(ActivityOverider.targetClassName);
}

原来,loadActivityClass()方法在加载插件的Activity之前,为每个Activity使用dexmaker动态生成了一个代理的PluginActivity继承自原Activity,这样插件中的每个Activity都有一个PluginActivity.dex文件。

另外,由于ClassLoader隔离问题,必须把每个classloader缓存起来,以免下次重新加载同一类会报类型转换失败的错。

这里new的DexClassLoader重载了loadClass()方法,当要加载类是androidx.pluginmgr.PluginActivity时 ,更改了默认的查找方式,不再先从parent Classloader中查找,而是直接自己查找类文件。

值得注意的是,生成PluginActivity.dex的过程一直处于主线程,其中进行了大量的文件操作,可能会引起ANR。

剩下就是重载ClassLoader的loadClass()方法了。


protected Class<?> loadClass(String name, boolean resolve)
			throws ClassNotFoundException {
		synchronized (getClassLoadingLock(name)) {
			// First, check if the class has already been loaded
			Class<?> c = findLoadedClass(name);
			if (c == null) {
				if(name.startsWith("android.support.")){
					try {
						c = findClass(name);
					} catch (ClassNotFoundException e) {
					}
					if (c == null) {
						c = findByParent(name, true);
					}
				}else{
					c = findByParent(name, false);
					if (c == null) {
						c = findClass(name);
					}
				}
			}
			if (resolve) {
				resolveClass(c);
			}
			return c;
		}
	}

这里判断了如果类名是以android.support.开头,说明是support包里的类,优先从当前的classloader加载,在插件中找不到时再从宿主中查找,避免与宿主support包冲突。

如果类名不以android.support.开头,则还是遵循java的ClassLoader的加载类的方式,先委托给parent查找,找不到再从自己查找。

findByParent()方法是为了查找真正宿主的ClassLoader。先取当前ClassLoader的parent ClassLoader,如果是FrameworkClassLoader的话,继续向上查找,即是宿主的ClassLoader。


Class<?> c =null;
try {
	ClassLoader parent = getParent();
	if (parent != null) {
			if (parent.getClass() == FrameworkClassLoader.class) {
				parent = parent.getParent();
			}
			if (parent != null) {
				c = parent.loadClass(name);
			}
		}
} catch (ClassNotFoundException e) {
	if(throwEx){
		throw e;
	}
}

return c;

文章作者: Caden
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Caden !
 上一篇
Android PluginManager 源码解析3--PluginManifestUtil Android PluginManager 源码解析3--PluginManifestUtil
PluginManifestUtil分析PluginManifestUtil是Android PluginManager的一个工具类。只有一个公共方法setManifestInfo(),负责解析插件apk中的androidManifest.
2015-08-11
下一篇 
Android PluginManager 源码解析1--PluginManager Android PluginManager 源码解析1--PluginManager
Android PluginManager简介Android pluginManager是HouKangxi大神开发的一个android插件化开发框架,可以动态的加载apk,实现组件的热插拔。 项目地址:https://github.com
2015-08-09
  目录