https://www.jianshu.com/p/a59763bde6b5
类加载的 hook 原理:
Replugin框架支持 插件访问另一个插件或宿主中的class,主要基于每个插件的DexClassloader加载类时,如果加载不到,使用宿主的RepluginClassLoader去加载,在 RepluginClassLoader中,会从已加载的插件或宿主中去加载类.
1 用RepluginClassLoader 去代替系统的PathClassLoader
2 在RepluginClassLoader去loadClass时,优先让插件的DexClassLoader去查找,如果插件中查找不到在使用RepluginClassLoader查找
RepluginClassLoader.java extends PathClassLoader
@Override
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
//
Class<?> c = null;
c = PMF.loadClass(className, resolve); // 优先让每个插件使用其DexClassLoader去加载
if (c != null) {
return c;
}
//
try {
c = mOrig.loadClass(className); // 使用PathClassLoader去加载
// 只有开启“详细日志”才会输出,防止“刷屏”现象
if (LogDebug.LOG && RePlugin.getConfig().isPrintDetailLog()) {
LogDebug.d(TAG, "loadClass: load other class, cn=" + className);
}
return c;
} catch (Throwable e) {
//
}
//
return super.loadClass(className, resolve);
}
插件中的资源加载:
不同于滴滴的VirtualApk 那种通过发射AssetManager. addAssetPath(File apk)的方式,而是使用每个插件维护自己的资源
这里简单介绍一下Resource加载原理
不管是Activity,Service 中的getResource(),还是Application的context.getResource(),最终都调用到ContextWrapper.java 中的getResouces()
Context mBase;
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
@Override
public Resources getResources() {
return mBase.getResources(); // mBase
这段代码可以知道,getResource最终都是通过Context去获取getResource(), 所以插件如果要访问自己的资源,需要使用插件自身的context(PluginContext),
PluginContext.java
@Override
public Resources getResources() {
if (mNewResources != null) {
return mNewResources;
}
return super.getResources();
}
@Override
public AssetManager getAssets() {
if (mNewResources != null) {
return mNewResources.getAssets();
}
return super.getAssets();
}
@Override
public Object getSystemService(String name) {
//通过hook getSystemService,可动态的修改inflater的factory
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
LayoutInflater inflater = (LayoutInflater) super.getSystemService(name);
// 新建一个,设置其工厂
mInflater = inflater.cloneInContext(this);
mInflater.setFactory(mFactory);
// 再新建一个,后续可再次设置工厂
mInflater = mInflater.cloneInContext(this);
}
return mInflater;
}
return super.getSystemService(name);
}
Loader.java
final Context createBaseContext(Context newBase) {
return new PluginContext(newBase, android.R.style.Theme, mClassLoader, mPkgResources, mPluginName, this);
}
PluginActivity.java
public abstract class PluginActivity extends Activity {
@Override
protected void attachBaseContext(Context newBase) {
newBase = RePluginInternal.createActivityContext(this, newBase);
super.attachBaseContext(newBase);
}
Replugin在编译期 (使用groovy技术,在编译时通过javaAssist 动态修改类的继承关系) 动态的将所有的Activity改为PluginActivity,然后在attachBaseContext时,使用RepluginContext,从而使用资源访问的控制
插件Application的初始化:
Plugin.java
private void callAppLocked() {
// 获取并调用Application的几个核心方法
if (!mDummyPlugin) {
// NOTE 不排除A的Application中调到了B,B又调回到A,或在同一插件内的onCreate开启Service/Activity,而内部逻辑又调用fetchContext并再次走到这里
// NOTE 因此需要对mApplicationClient做判断,确保永远只执行一次,无论是否成功
if (mApplicationClient != null) {
// 已经初始化过,无需再次处理
return;
}
mApplicationClient = PluginApplicationClient.getOrCreate(
mInfo.getName(), mLoader.mClassLoader, mLoader.mComponents, mLoader.mPluginObj.mInfo);
if (mApplicationClient != null) {
//使用当前插件的Context(PluginContext)
mApplicationClient.callAttachBaseContext(mLoader.mPkgContext);
mApplicationClient.callOnCreate();
}
} else {
if (LOGR) {
LogRelease.e(PLUGIN_TAG, "p.cal dm " + mInfo.getName());
}
}
}
PluginServiceServer.java
// 加载插件,获取Service对象,并将其缓存起来
private boolean installServiceLocked(ServiceRecord sr) {
// 通过ServiceInfo创建Service对象
Context plgc = Factory.queryPluginContext(sr.plugin);
if (plgc == null) {
if (LogDebug.LOG) {
Log.e(TAG, "installServiceLocked(): Fetch Context Error! pn=" + sr.plugin);
}
return false;
}
ClassLoader cl = plgc.getClassLoader();
if (cl == null) {
if (LOGR) {
LogRelease.e(PLUGIN_TAG, "psm.is: cl n " + sr.className);
}
return false;
}
// 构建Service对象
Service s;
try {
s = (Service) cl.loadClass(sr.serviceInfo.name).newInstance();
} catch (Throwable e) {
if (LOGR) {
LogRelease.e(TAG, "isl: ni f " + sr.plugin, e);
}
return false;
}
// 只复写Context,别的都不做,使用PluginContext代替
try {
attachBaseContextLocked(s, plgc);
} catch (Throwable e) {
if (LOGR) {
LogRelease.e(PLUGIN_TAG, "psm.is: abc e", e);
}
return false;
}
s.onCreate();
sr.service = s;
// 开启“坑位”服务,防止进程被杀
ComponentName pitCN = getPitComponentName();
sr.pitComponentName = pitCN;
startPitService(pitCN);
return true;
}
// 通过反射调用Service.attachBaseContext方法(Protected的)
private void attachBaseContextLocked(ContextWrapper cw, Context c) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
if (mAttachBaseContextMethod == null) {
mAttachBaseContextMethod = ContextWrapper.class.getDeclaredMethod("attachBaseContext", Context.class);
mAttachBaseContextMethod.setAccessible(true);
}
mAttachBaseContextMethod.invoke(cw, c);
// init Application
Field applicationField = Service.class.getDeclaredField("mApplication");
if (applicationField != null) {
applicationField.setAccessible(true);
applicationField.set(cw, c.getApplicationContext());
}
}
备注:Service,Activity 在初始化之后,第一个调用的函数的attachBaseContext
总之:所有对资源对访问,都是通过使用PluginContext来实现资源的分包