android动态加载资源的一个典型的例子就是app的换肤功能。在应用中不可能将所有的皮肤内置到app中,特别是在一些节日里都会有新的皮肤上线,而且为了更新皮肤而更新整个应用也是不可能的。那么以apk插件的形式提供皮肤包,应用动态的加载的这些皮肤包提供的图片才是一种可取的方式。那么问题来了,要怎么动态加载这么皮肤包呢,需要处理两个方面:

  1. 获取插件包的resource
  2. 获取插件包的resource id

下面就从这两个方面说说怎么处理皮肤包。


1.获取插件包中的resource

一般我们获取resource实例的方法是直接使用context.getResource()获取的,现在要获取插件中的resource实例该方法就不适用了,我们来看看构造方法

 public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {      this(null);      mResourcesImpl = new ResourcesImpl(assets, metrics, config, new DisplayAdjustments());    }

我们需要一个AssetManager的实例,如果直接实例话一个AssetManager类的话是不行的,它没有经过安装过程的处理,不能引用到一个应用中的资源。还好在AssetManager中有一个方法可以帮助我们做到这一点

 /**     * 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) {        return  addAssetPathInternal(path, false);    }

这是一个hide方法,这就需要调用反射的方式来调用这个方法。

 AssetManager manager = AssetManager.class.newInstance(); Method addAssetMethod = manager.getClass().getMethod("addAssetPath", String.class); addAssetMethod.invoke(manager, apkPath);

2.获取插件包中的resource id

获取resource id就需要获取插件中的R类,如何获取插件中的R类呢?可以使用DexClassLoader这个类加载器,使用这个类就可以直接加载插件中的R类,并且都是static变量,可以非常方便的获取到resource id

 DexClassLoader loader = new DexClassLoader(apkPath, codePath, null, this.getClass().getClassLoader());

3.实例

1.将插件apk放置到sdcard中

首先将一个apk作为插件放置到sdcard目录中作为插件供应用调用,apk中只放置了一张图片在drawable目录中。

2.应用中获取资源替换图片

package com.motiongear.simplecc.dynamicapk;import android.Manifest;import android.content.res.AssetManager;import android.content.res.Resources;import android.os.Bundle;import android.os.Environment;import android.support.v4.app.ActivityCompat;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.Button;import android.widget.ImageView;import java.io.File;import java.lang.reflect.Field;import java.lang.reflect.Method;import dalvik.system.DexClassLoader;public class MainActivity extends AppCompatActivity implements View.OnClickListener {    private static final String APK_NAME = "app-debug.apk";    private ImageView mImageView;    private Button mButton;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mImageView = (ImageView) this.findViewById(R.id.imageview);        mButton = (Button) this.findViewById(R.id.btn);        mButton.setOnClickListener(this);        //获取动态权利,简单处理下        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0x001);    }    @Override    public void onClick(View v) {        String apkPath = Environment.getExternalStorageDirectory() + File.separator + APK_NAME;        String codePath = getCodeCacheDir().getAbsolutePath();        DexClassLoader loader = new DexClassLoader(apkPath, codePath, null, this.getClass().getClassLoader());        try {            //1. 获取resource id            //加载插件中的R.drawable类            Class clz = loader.loadClass("com.motiongear.simplecc.pluginsapk.R$drawable");            //获取图片id            Field field = clz.getDeclaredField("dog");            int resId = (int) field.get(clz);            //2. 获取resource            //实例化AssetManager            AssetManager manager = AssetManager.class.newInstance();            Method addAssetMethod = manager.getClass().getMethod("addAssetPath", String.class);            addAssetMethod.invoke(manager, apkPath);            Resources superResource = getResources();            //获取插件的resource            Resources pluginRes = new Resources(manager, superResource.getDisplayMetrics(), superResource.getConfiguration());            //替换图片            mImageView.setImageDrawable(pluginRes.getDrawable(resId));        } catch (Exception e) {            e.printStackTrace();        }    }}

显示结果如下:

更多相关文章

  1. Android(安卓)进阶——高级UI必知必会之常用的自定义ViewGroup进
  2. Android(安卓)换肤之旅——主题切换
  3. android 扫码设备获取扫码回调内容实践
  4. android获取设备分辨率的新方法
  5. 高德地图Android错误码1008、32和7;错误提示invalid_user_scode;返
  6. Android几种常见的多渠道(批量)打包方式介绍
  7. Android(安卓)6.0权限动态申请适配
  8. TTF字体库系列文章1 —— Android使用ttf字体库替代替图片(iconf
  9. Android插件技术——(二)加载已安装apk

随机推荐

  1. android 图片双缓存,开源框架 universali
  2. 绝对精品—史上最全最权威的Android(安卓
  3. Android 中常用的五种布局
  4. Android NDK开发动态加载so示例源码
  5. [置顶] android IPC通信(下)-AIDL
  6. TextView的跑马灯效果,还有焦点问题
  7. SuperMap iMobile for Android定位实现
  8. 关于Android的极光推送的消息栏
  9. RecyclerView不显示问题
  10. Android中的Grid间距(Grid Spacing on And