4、 资源如何加载
在获取资源时,有三个重要的类Resources, AssetManager, Theme,我们通过Context获取它们的对象
@Override
public AssetManager getAssets() {
return getResources().getAssets();
}
@Override
public Resources getResources() {
return mResources;
}
@Override
public Resources.Theme getTheme() {
synchronized (mSync) {
if (mTheme != null) {
return mTheme;
}
mThemeResource = Resources.selectDefaultTheme(mThemeResource,
getOuterContext().getApplicationInfo().targetSdkVersion);
initializeTheme();
return mTheme;
}
}
我们要做的就是依葫芦画瓢重写这几个方法,构造自己对象。对于插件Activity,我们需要给它设置一个Resources,让其包含插件中的资源。但是,这里不能替换掉宿主应用中的Resources,不然宿主就出问题了。Resources的构造函数如下:
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
this(null);
mResourcesImpl = new ResourcesImpl(assets, metrics, config, new DisplayAdjustments());
}
它需要三个对象,这里的关键是AssetManager,它可以通过addAssetPath制定资源所在apk的路径,而另外两个我们就用宿主的即可。这里我们需要自己创建一个AssetManager,由于addAssetPath是hide的,所以只能通过反射调用。
mDexAM = AssetManager.class.newInstance();
Method method = mDexAM.getClass().getDeclaredMethod("addAssetPath", String.class);
method.setAccessible(true);
method.invoke(mDexAM, LoadDexUtils.getPlugApkPath());
接下来就可以构造一个Resources对象了
Resources res = getResources();
mDexResources = new Resources(mDexAM, res.getDisplayMetrics(), res.getConfiguration());
上面的Theme获取要复杂些,不过我们也可以仿照获取
Class resources = Class.forName("android.content.res.Resources");
Method selectDefaultTheme = resources.getDeclaredMethod("selectDefaultTheme", int.class, int.class);
selectDefaultTheme.setAccessible(true);
int defaultTheme = (Integer) selectDefaultTheme.invoke(mDexResources, 0, getApplicationInfo().targetSdkVersion);
mDexTheme = mDexResources.newTheme();
mDexTheme.applyStyle(defaultTheme, true);
我们重写了Application的getResources、getTheme、getAssets方法,返回以上新创建的对象,而对应的插件Activity也重写以上三个方法:
@Override
public Resources getResources() {
return getApplication() == null ? super.getResources() : getApplication().getResources();
}
@Override
public Resources.Theme getTheme() {
return getApplication() == null ? super.getTheme() : getApplication().getTheme();
}
@Override
public AssetManager getAssets() {
return getApplication() == null ? super.getAssets() : getApplication().getAssets();
}
到这里,插件Activity就可以正常启动了,最终实现无需安装APK,就可以实现插件化了。