--摘自《android插件化开发指南》
1.android资源文件分为两类:
第一类是res目录下存放的可编译资源文件,编译时,系统会自动在R.java中生成资源文件的十六进制值
Resources resources = getResources();
String appName = resources.getString(R.string.app_name);
第二类是assets目录下存放的原始资源文件,apk在编译时不会编译assets下的资源文件
Resources resources = getResources(); AssetManager am = getResources().getAssets(); InputStream is = getResources().getAssets().open("filename");
2.Resources内部各种方法其实都是间接调用AssetManager的内部方法。AssetManager的addAssetPath方法会在app启动的时候把当前apk的路径传进去,就能访问apk的所有资源了。在这里可以把插件apk的资源塞进去
3.apk打包时会生成一个resource.arsc文件,它就是一个Hash表,存放着每个十六进制值和资源的对应关系
***资源的插件化解决方案***
public class Dynamic implements IDynamic { @Override public String getStringForResId(Context context) { return context.getResources().getString(R.string.myplugin1_hello_world); } }
<resources> <string name="app_name">Plugin1</string> <string name="myplugin1_hello_world">myplugin1_hello_world</string> </resources>
public class BaseActivity extends Activity { private AssetManager mAssetManager; private Resources mResources; private Resources.Theme mTheme; private String dexpath = null; //apk文件地址 private File fileRelease = null; //释放目录 protected DexClassLoader classLoader = null; private String pluginName = "plugin1.apk"; TextView tv; @Override protected void attachBaseContext(Context newBase) { super.attachBaseContext(newBase); Utils.extractAssets(newBase, pluginName); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //第三步:加载外部的插件,生成插件对应的ClassLoader File extractFile = this.getFileStreamPath(pluginName); dexpath = extractFile.getPath(); fileRelease = getDir("dex", 0); classLoader = new DexClassLoader(dexpath, fileRelease.getAbsolutePath(), null, getClassLoader()); } /** * 第一步:通过反射,创建AssetManager对象,调用addAssetPath方法,把插件Plugin的路径添加到这个AssetManager对象中 **/ protected void loadResources() { 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(); superRes.getDisplayMetrics(); superRes.getConfiguration(); mResources = new Resources(mAssetManager, superRes.getDisplayMetrics(), superRes.getConfiguration()); mTheme = mResources.newTheme(); mTheme.setTo(super.getTheme()); } /** * 第二步:重写Acitivity的getAsset,getResources和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 MainActivity extends BaseActivity { TextView tv; @SuppressLint("NewApi") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btn_6 = (Button) findViewById(R.id.btn_6); tv = (TextView)findViewById(R.id.tv); //带资源文件的调用 btn_6.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { loadResources(); Class mLoadClassDynamic = null; try { //第四步:通过反射,获取插件中的类,构造出插件类的对象dynamicObject,然后就可以让插件中的类读取插件中的资源了 mLoadClassDynamic = classLoader.loadClass("jianqiang.com.plugin1.Dynamic"); Object dynamicObject = mLoadClassDynamic.newInstance(); IDynamic dynamic = (IDynamic) dynamicObject; String content = dynamic.getStringForResId(MainActivity.this); tv.setText(content); Toast.makeText(getApplicationContext(), content + "", Toast.LENGTH_LONG).show(); } catch (Exception e) { Log.e("DEMO", "msg:" + e.getMessage()); } } }); } }
还有换肤