--摘自《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来加载插件资源