最近在研究Android 工程的插件化,也可是说是热更新,让项目插件化,功能模块放到插件工程中,编译为插件apk。已实现在不重新安装开发包的基础上,达到更新项目功能模块的效果。当然也可以用于换肤
了很多网上的资料和视频,感觉有些地方还是很误导读者。所以在这里整理一下,关于java反射使Android 项目插件化的原理。
实际上是利用了java反射的原理和DexClassLoader 这个类,载入apk中的class类和资源。
dexClassLoader = new DexClassLoader(dexpath,dexOutputPath, null, mContext.getClassLoader());
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, dexpath);
resources = new Resources(assetManager,
mContext.getResources().getDisplayMetrics(),
mContext.getResources().getConfiguration());
执行了上面的代码,就可以得到两个类 一个是 Resources类型,一个是DexClassLoader类型
而关于很多文章中提到的,上下文注入,这里就误导了读者。实际上并不是把代理Activity的上下文注入到了插件apk中加载进来
Activity ,而只是代理Activity调用了插件apk中的接口。所以插件包中的apk实际上可以直接写成一个接口而不需继承Activity。这样也避免了代码的冗余。而且也免于覆盖很多没有必要处理的抽象类。写成下面的形式是完全没有问题的。实际上它只是一个接口而并不是一个Activity。所以其实很多开源的,在插件中继承Actitity是不对的。因为代码只是给代理Activity调用而已。只需要实现代理Activity中调用的接口就可以了。下面的代码中也把代理Activity的源代码放进来(BaseContainerActivity)。并没有完全覆盖公共接口中的所有方法,只是一个简单的示例。实际上还应该在代理的相应方法中调用公共接口的方法,以保证插件类中实现的方法被调用。关于公共接口,在插件工程和子工程中使用一模一样的包名和类名就可以了。
service 等等 BroadcastReceiver等其他的类型原理也是相同的就不累述了。实际上可以用于实现任何类中的代码的动态更改(可以随时从服务器是下载相应的apk,不需要重新安装)。个人觉得非常的灵活方便。
public class OpenActivity implements OpenInterface {
protected Activity activity;
@Override
public void onOpenStart() {
}
@Override
public void onOpenResume() {
}
@Override
public void onOpenPause() {
}
@Override
public void onOpenStop() {
}
@Override
public void onOpenDestroy() {
}
@Override
public void onOpenCreate(Bundle savedInstanceState) {
activity.setContentView(R.layout.activity_open);
activity.findViewById(R.id.click).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(activity,"djfkjd",Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void setProxy(Activity proxyActivity) {
this.activity=proxyActivity;
}
}
public class BaseContainerActivity extends Activity {
private static String classname;
public static void setClassname(String classname) {
BaseContainerActivity.classname = classname;
}
public static final String CLASSNAME="classname";
private OpenInterface openInterface;
LoaderPluginManager loaderPluginManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e("classname",classname);
loaderPluginManager = LoaderPluginManager.create(this, classname);
try {
DexClassLoader loader = loaderPluginManager.getClassLoader();
Class clazz = loader.loadClass(classname);
Constructor<?> localConstructor = clazz.getConstructor(new Class[]{});
openInterface = (OpenInterface) localConstructor.newInstance(new Object[]{});
openInterface.setProxy(this);
openInterface.onOpenCreate(savedInstanceState);
} catch (Exception e) {
Log.e("载入class出错",e.getMessage());
e.printStackTrace();
}
}
@Override
public ClassLoader getClassLoader() {
if (loaderPluginManager == null) {
return super.getClassLoader();
} else {
return loaderPluginManager.getClassLoader();
}
}
@Override
public Resources getResources() {
if(loaderPluginManager==null){
return super.getResources();
}else{
return loaderPluginManager.getResource();
}
}
}