Replugin相关

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来实现资源的分包

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值