反射獲取drawable等資源

做android項目打包apk時,爲了儘量減少apk的大小,加入了替換資源路徑功能,類似於微信混淆壓縮,把資源路徑和資源的名稱用較短的字符串替換,這樣做的好處是apk確實變小了,壞處就是一些通過資源的名字,用反射的方式來獲取資源失敗了,因爲名字變了,所以這時候就需要添加個資源白名單的功能,裏面可以填寫資源的名字。我再網上找了一些通過名稱反射的方式來獲取 drawable 資源的方法,先看看方法一,比較繁瑣


    private LoadedResource mLoadedResource;
    
    public Drawable getDrawable(String packageName, String fieldName) {
        Drawable drawable = null;
        int resourceID = getResourceID(packageName, "drawable", fieldName);
        LoadedResource installedResource = getInstalledResource(packageName);
        if (installedResource != null) {
            drawable = installedResource.resources.getDrawable(resourceID);
        }
        return drawable;
    }

    public int getResourceID(String packageName, String type, String fieldName) {

        int resID = 0;
        LoadedResource installedResource = getInstalledResource(packageName);    // 獲取已安裝APK的資源
        if (installedResource != null) {
            String rClassName = packageName + ".R$" + type;    // 根據匿名內部類的命名, 拼寫出R文件的包名+類名
            try {
                Class cls = installedResource.classLoader.loadClass(rClassName);    //  加載R文件
                resID = (Integer) cls.getField(fieldName).get(null);    //  反射獲取R文件對應資源名的ID
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
        }
        return resID;
    }

    public LoadedResource getInstalledResource(String packageName) {

        LoadedResource resource = mLoadedResource;    // 先從緩存中取, 沒有就去加載

        if (resource == null) {
            try {
                Context context = mContext.createPackageContext(packageName,
                        Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
                resource = new LoadedResource();
                resource.packageName = packageName;
                resource.resources = context.getResources();
                resource.classLoader = context.getClassLoader();
                mLoadedResource = resource;    // 得到結果緩存起來
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return resource;
    }


    public static class LoadedResource {
        public Resources resources;
        public String packageName;
        public ClassLoader classLoader;
    }

說明,"com.test.cn" 是自己項目的包名,user_pic.png 是放在 drawable 文件裏面的圖片,調用下面的方法,給 ImageView 設置圖片:

    imageView.setImageDrawable(getDrawable("com.test.cn", "user_pic"));

方法二,比較簡單粗暴,沒那麼多步驟:

    private int getResidByReflect(String packageName, String imageName){
        try {
            Field field = Class.forName(packageName + ".R$drawable").getField(imageName);
            int resid = field.getInt(field);
            return resid;
        } catch (Exception e) {
            return -1;
        }
    }

    imageView.setBackgroundResource(getResidByReflect("com.test.cn","user_pic"));

方法一獲取的是 Drawable 類型,方法二獲取的是資源id,傳入的參數是一樣的。

還有中獲取圖片的方式,借用阿里百川sdk中的代碼,示例如下 

public class ResourceUtils {

    public ResourceUtils() {
    }

    public static String getString(Context var0, String var1) {
        return var0.getResources().getString(getIdentifier(var0, "string", var1));
    }

    public static int getRLayout(Context var0, String var1) {
        return getIdentifier(var0, "layout", var1);
    }

   public static int getRDrawable(Context var0, String var1) {
        return getIdentifier(var0, "drawable", var1);
    }

    public static int getRId(Context var0, String var1) {
        return getIdentifier(var0, "id", var1);
    }

    public static float getDimen(Context var0, String var1) {
        return getIdentifier(var0, "dimen", var1); 
    }
    
    public static int getIdentifier(Context var0, String var1, String var2) {
        return var0.getResources().getIdentifier(var2, var1, var0.getPackageName());
    }
}

爲什麼介紹這個呢?是因爲以前項目中接入了阿里百川sdk,sdk中包含有淘寶登錄等頁面,它裏面用到的都是反射,這樣就造成了一些有趣的現象。項目繼承sdk後,debug模式下使用的好好的,release模式也沒問題;一旦切換到混淆路徑release模式,剛跳轉到淘寶頁面就崩潰了,原因是資源圖片找不到,原因就是資源路徑被混淆,所以找不到了。阿里百川官方文檔也沒資源免混淆的說明,只能動手去翻sdk裏面的代碼,然後去添加相應的資源白名單,廢了好一番力氣。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章