--摘自《android插件化开发指南》
fork了强哥的github代码到自己的github下 https://github.com/king1039/Dynamic3
将代码跑起来,Plugin1和Plugin2打成apk放到HostApp的assets下
贴下主要代码
public class BaseActivity extends Activity {
private AssetManager mAssetManager;
private Resources mResources;
private Resources.Theme mTheme;
protected HashMap<String, PluginInfo> plugins = new HashMap<String, PluginInfo>();
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
Utils.extractAssets(newBase, "plugin1.apk");
Utils.extractAssets(newBase, "plugin2.apk");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
genegatePluginInfo("plugin1.apk");
genegatePluginInfo("plugin2.apk");
}
protected void genegatePluginInfo(String pluginName) {
File extractFile = this.getFileStreamPath(pluginName);
File fileRelease = getDir("dex", 0);
String dexpath = extractFile.getPath();
DexClassLoader classLoader = new DexClassLoader(dexpath, fileRelease.getAbsolutePath(), null, getClassLoader());
plugins.put(pluginName, new PluginInfo(dexpath, classLoader));
}
protected void loadResources(String dexPath) {
try {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, dexPath);
mAssetManager = assetManager;
} catch (Exception e) {
e.printStackTrace();
}
Resources superRes = super.getResources();
mResources = new Resources(mAssetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());
mTheme = mResources.newTheme();
mTheme.setTo(super.getTheme());
}
@Override
public AssetManager getAssets() {
return mAssetManager == null ? super.getAssets() : mAssetManager;
}
@Override
public Resources getResources() {
return mResources == null ? super.getResources() : mResources;
}
@Override
public Resources.Theme getTheme() {
return mTheme == null ? super.getTheme() : mTheme;
}
}
public class ResourceActivity extends BaseActivity {
/**
* 需要替换主题的控件
* 这里就列举三个:TextView,ImageView,LinearLayout
*/
private TextView textV;
private ImageView imgV;
private LinearLayout layout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_resource);
textV = (TextView) findViewById(R.id.text);
imgV = (ImageView) findViewById(R.id.imageview);
layout = (LinearLayout) findViewById(R.id.layout);
findViewById(R.id.btn1).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
PluginInfo pluginInfo = plugins.get("plugin1.apk");
loadResources(pluginInfo.getDexPath());
doSomething(pluginInfo.getClassLoader());
}
});
findViewById(R.id.btn2).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
PluginInfo pluginInfo = plugins.get("plugin2.apk");
loadResources(pluginInfo.getDexPath());
doSomething(pluginInfo.getClassLoader());
}
});
}
private void doSomething(ClassLoader cl) {
try {
Class clazz = cl.loadClass("jianqiang.com.plugin1.UIUtil");
String str = (String) RefInvoke.invokeStaticMethod(clazz, "getTextString", Context.class, this);
textV.setText(str);
Drawable drawable = (Drawable) RefInvoke.invokeStaticMethod(clazz, "getImageDrawable", Context.class, this);
imgV.setBackground(drawable);
layout.removeAllViews();
View view = (View) RefInvoke.invokeStaticMethod(clazz, "getLayout", Context.class, this);
layout.addView(view);
} catch (Exception e) {
Log.e("DEMO", "msg:" + e.getMessage());
}
}
}
简单来说,就是针对不同的apk生成不同的ClassLoader,然后通过反射框架取出相应的资源,最终加载显示
doSomething还有另外一个种写法,直接访问R.java的内部类drawable/string/layout中的相应字段对应的十六进制值(这个好屌)
private void doSomething(ClassLoader cl) {
try {
Class stringClass = cl.loadClass("jianqiang.com.plugin1.R$string");
int resId1 = (int) RefInvoke.getStaticFieldObject(stringClass, "hello_message");
textV.setText(getResources().getString(resId1));
Class drawableClass = cl.loadClass("jianqiang.com.plugin1.R$drawable");
int resId2 = (int) RefInvoke.getStaticFieldObject(drawableClass, "robert");
imgV.setBackground(getResources().getDrawable(resId2));
Class layoutClazz = cl.loadClass("jianqiang.com.plugin1.R$layout");
int resId3 = (int) RefInvoke.getStaticFieldObject(layoutClazz, "main_activity");
View view = (View) LayoutInflater.from(this).inflate(resId3, null);
layout.removeAllViews();
layout.addView(view);
} catch (Exception e) {
Log.e("DEMO", "msg:" + e.getMessage());
}
}
总的来说,资源插件化就是通过反射AssetManager和addAssetPath来加载插件资源
欢迎关注我的微信公众号:安卓圈