最近在研究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();
}
}
}