目录
简介
市面上实现插件化的方式大体可分为两种,一种是hook方式,一种是插桩式。其中hook方式,因为需要hook系统API,随着系统API的变化需要不断做适配。因此插件化方案未来趋势,我更看好代理方式实现的方案
大概步骤
- 设计标准
- 开发插件时遵循这个标准
- 宿主使用自定义的ClassLoader,Resources准备加载插件的环境
- 在宿主的清单文件用一个空的Activity插桩,加载插件Activity
实现案例
设计标准(可作为一个独立的module,因为宿主和插件需要同一套标准)
public interface IActivityInterface {
public void setAppContext(Activity activity);
public void onCreate(Bundle bundle);
public void setContentView(int layoutId);
}
开发插件遵循这套标准(注意,以下只截取了代码片段)
public class BaseActivity implements IActivityInterface {
private Activity mActivity;
@Override
public void setAppContext(Activity activity) {
Log.i("我是插件", "setAppContext");
mActivity = activity;
}
@Override
public void onCreate(Bundle bundle) {
Log.i("我是插件", "onCreate");
}
@Override
public void setContentView(int layoutId) {
Log.i("我是插件", "setContentView");
mActivity.setContentView(layoutId);
}
}
public class PluMainActivity extends BaseActivity {
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_plu);
}
}
宿主使用自定义的ClassLoader,Resources准备加载插件的环境
- 1)ClassLoader的处理
Android中的ClassLoader类加载器派生出的有DexClassLoader和PathClassLoader。这两者的区别是
DexClassLoader: 能够加载未安装的jar/apk/dex
PathClassLoader: 只能加载系统中已经安装的apk
同时,由于虚拟机在安装期间会为类打上CLASS_ISPREVERIFIED标志,当满足以下条件时:
在类加载时,由于ClassLoader的双亲委托机制,加载时如果加载了插件中的类了,那么宿主的类便不会再加载而会使用插件的,反之对插件也是一样。这就很容易触发上述所说的verify的问题,从而报出异常“java.lang.IllegalAccessError: Class ref in pre-verified class...”
如何避免?
可以通过自定义ClassLoader修改类加载逻辑,使得插件和宿主中的类隔离,各自加载。
各自加载的好处:插件和宿主依赖的通用模块无需特殊处理。
package com.sq.a37syplu10.plugin.loader;
import android.os.Build;
import dalvik.system.DexClassLoader;
public class ApkClassLoader extends DexClassLoader {
private ClassLoader mGrandParent;
private final String[] mInterfacePackageNames;
public ApkClassLoader(String dexPath,
String optimizedDirectory,
String librarySearchPath,
ClassLoader parent,
String[] interfacePackageNames) {
super(dexPath, optimizedDirectory, librarySearchPath, parent);
ClassLoader grand = parent;
mGrandParent = grand.getParent();
this.mInterfacePackageNames = interfacePackageNames;
}
@Override
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
String packageName;
int dot