总所周知,activity的工作主要是由ContextImpl来完成的, 它在activity中是一个叫做mBase的成员变量。注意到Context中有如下两个抽象方法,看起来是和资源有关的,实际上context就是通过它们来获取资源的,这两个抽象方法的真正实现在ContextImpl中。
@Override
public AssetManager getAssets() {
// Ensure we're returning assets with the correct configuration.
return getResourcesInternal().getAssets();
}
@Override
public Resources getResources() {
return getResourcesInternal();
}
而想拿到其它apk的资源文件,关键就在于获取到该apk的resources对象。废话不多说,直接上代码。
private static Context sPkgContext;
private static Resources sPkgResources;
private static PackageInfo sPkgArchiveInfo;
public static void loadPackage(Context context, String packageName) {
String dexPath = null;
try {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(packageName, 0);
//check package
if (applicationInfo == null) {
return;
}
dexPath = applicationInfo.sourceDir;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
//check path
if (TextUtils.isEmpty(dexPath)) {
return;
}
Context pluginContext = null;
try {
pluginContext = context.createPackageContext(packageName, Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
PackageInfo pluginPackageArchiveInfo = context.getPackageManager().getPackageArchiveInfo(dexPath, PackageManager.GET_ACTIVITIES);
Log.d(TAG, "loadApk: dexPath " + dexPath);
Log.d(TAG, "loadApk: pluginPackageArchiveInfo " + pluginPackageArchiveInfo);
//加载apk的资源
AssetManager assets = null;
try {
assets = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.invoke(assets, dexPath);
} catch (Exception e) {
e.printStackTrace();
}
Resources pluginResources = new Resources(assets, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());
Log.d(TAG, "loadApk: pluginResources " + pluginResources);
sPkgContext = pluginContext;
sPkgResources = pluginResources;
sPkgArchiveInfo = pluginPackageArchiveInfo;
}
说明:加载的方法是通过反射,通过调用AssetManager中的隐藏方法addAssetPath,我们可以将一个apk中的资源加载到Resources中,addAssetPat的注释如下
/**
* Add an additional set of assets to the asset manager. This can be
* either a directory or ZIP file. Not for use by applications. Returns
* the cookie of the added asset, or 0 on failure.
* {@hide}
*/
public final int addAssetPath(String path) {
int res = addAssetPathNative(path);
return res;
}
通过 addAssetPath 将指定的apk资源加载到AssetManager中,然后再通过AssetManager来创建一个新的Resources对象,这个对象就是我们可以使用的apk中的资源了,这样我们就可以动态的加载其它apk的资源了。