原理:根据activity获取资源的方法getResources(),通过查找其源码,根据其原理,通过生成一个AssetManager并添加一个APK文件的 ,最终生成一个包含所有资源的Resource对象。由于 AssetManager无法直接实例化,所以通过反射方法获取其实例,并调用其添加apk包的addAssetPath()方法。使用DexClassLoader加载目标apk中的资源R类获取需要的资源id的值。使用Resource的get方法(getString(int id)、getDrawable(int id))即可获取到相应的资源。
源码如下:
a.生成一个包含apk路径的AssetManager实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public static AssetManager getAssetManager(String filePath) throws ClassNotFoundException { AssetManager assetManager = null; Class<?> aClass = Class.forName("android.content.res.AssetManager"); Method[] methods = aClass.getDeclaredMethods(); for (Method method : methods) { if (method.getName().equals("addAssetPath")) { try { assetManager = (AssetManager) aClass.newInstance(); method.invoke(assetManager, filePath); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } return assetManager; }
|
b.生成对应的Resource对象:
1 2 3 4 5 6 7 8
| private static PluginResource resource; public static PluginResource getInstance(String filePath) throws ClassNotFoundException { if (resource == null) { resource = new PluginResource(getAssetManager(filePath), new DisplayMetrics(), new Configuration()); } return resource; }
|
c.获取需要对象的id并根据资源Id获取资源:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| try { PluginResource resource = PluginResource.getInstance(filePath); DexClassLoader dexClassLoader = new DexClassLoader(filePath, this.getDir("demo",MODE_PRIVATE).getPath(), null, this.getClassLoader()); Class<?> aClass =dexClassLoader.loadClass("com.linwoain.demo.R$string"); for (Field field : aClass.getDeclaredFields()) { if (field.getName().equals("test")) { try { int targetId = field.getInt(R.string.class); String string = resource.getString(targetId); Log.i(TAG, "onCreate: "+string); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } catch (ClassNotFoundException e) { e.printStackTrace(); }
|