在很多Android应用上,都有资源动态加载的功能,比如更换主题皮肤,替换聊天界面背景图片等。
我们知道,应用中的资源文件,包括图片,xml文件等,都是在编译的时候打包好的,那怎样才能动态加载资源呢?
其实有一个比较简单的思路,将需要替换的资源文件打包在一个apk文件中,动态下发到本地,然后通过重新构造Resources对象访问apk中的资源,进行本地的动态替换。主要有以下几个步骤:
一、指定资源文件加载路径
Android应用中的资源是通过AssetManager来管理的,其中addAssetPath方法可以指定资源加载路径。
/**
* 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) {
synchronized (this) {
int res = addAssetPathNative(path);
makeStringBlocks(mStringBlocks);
return res;
}
}
很显然这是个隐藏的API,所以需要通过反射来调用。
二、构造Resources对象
有了Resource对象,就可以访问指定路径的资源文件,进行动态替换,示例如下:
public class SkinManager {
private Resources mResources;
/**
* 获取APK资源
* @param context 上下文
* @param apkPath APK路径
*/
public void loadSkinRes(Context context, String skinFilePath) {
if (TextUtils.isEmpty(skinFilePath)) {
return ;
}
try {
AssetManager assetManager = createAssetManager(skinFilePath);
mResources = createResources(context, assetManager);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 反射addAssetPath方法
*/
private AssetManager createAssetManager(String skinFilePath) {
try {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, skinFilePath);
return assetManager;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 构造Resources对象
*/
private Resources createResources(Context context, AssetManager assetManager) {
Resources superRes = context.getResources();
Resources resources = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());
return resources;
}
public Resources getSkinResource() {
return mResources;
}
}
三、在进入Activity的时候进行检查,如果有资源apk文件,则通过新的Resources对象进行资源获取。
public class MainActivity extends Activity {
private Context mContext;
private ImageView mBgView;
private SkinManager mSkinManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBgView = (ImageView) findViewById(R.id.bg);
mContext = this;
mSkinManager = new SkinManager();
checkNewSkin();
}
private void checkNewSkin() {
String skinDir = "/mnt/sdcard/skin";
File file = new File(skinDir);
File[] skinFile = file.listFiles();
if (skinFile == null || skinFile.length == 0) {
return ;
}
mSkinManager.loadSkinRes(mContext, skinFile[0].getAbsolutePath());
if (mSkinManager.getSkinResource() != null) {
mBgView.setBackgroundDrawable(mSkinManager.getSkinResource().getDrawable(R.mipmap.skin));
}
}
}
这里只是简单的处理,将编译好的资源apk文件push到本地sd卡直接加载,正常情况下应该是从网络下载,根据不同的模板名称进行资源的动态替换。
文章评论
Great read Anton. Thanks again for taking the time to give us an invrteiew on the beautiful blue lake. I had a fantastic weekend following you and the other runners around with my camera. The finished film will be on YouTube within the next week.
I do not fully understand it) Youtube promotion of high production value and grass roots seohis/serws would be a major way for youtube to start matching the revenue of other video websites like hulu. Just think of youtube as an independent cable channel that looks for viewable content that will keep on running.