Android开发

解决SDK中getIdentifier的痛点

2020-08-08  本文已影响0人  Ray206

SDK做出来后,需要给其他公司使用。SDK中包含资源和jar包,其它公司使用SDK的时候会重新编译资源并生成ID(aar)除外。遇到第三方打包时(quick、快接)等都会生成新的资源ID。如果使用R.xx.xx的方式会报找不到资源的问题。

Resources.getIdentifier(String name, String defType, String defPackage)

这么做确实可以解决资源找不到的问题,但是对于编码来说太生硬。

//apply plugin: 'com.android.library'
改成
apply plugin: 'com.android.application'

这样配置后就可以生成对应的R文件了。

整个流程就是这样了,相当于我们自己生成了一个资源id,做绑定,只不过现在用了编译生成的id。我们对新生成的资源id用SparseArray做了缓存,提高查找效率(只调用一次getIdentifier)。如果有反复查找提高命中的需求,可以使用LinkedHashMap做缓存容器(Lru算法)
附工具类:

/**
 * Time: 2020/8/5 0005
 * Author: zoulong
 */
public class Res {
    //资源类型
    private String[] resTypes = new String[]{"color", "dimen", "drawable", "id", "string", "style", "layout"};
    //生成R时的包名
    private final String SDK_PACKAGE_NAME = "com.xiyou.sdk.p";
    //全局上下文
    private static Application mApplication;
    //缓存表
    private static SparseArray<ResId> ids = new SparseArray<>();
    private static Res mRes = null;
    public Res(){};

    public static Res get(){
        if(mRes == null){
            synchronized(Res.class){
                if(mRes == null){
                    mRes = new Res();
                }
            }
        }
        return mRes;
    }

    public void init(){
        mApplication = getApplicationByReflect();
        for(String resType : resTypes){
            try {
                Class resTypeClass = Class.forName(SDK_PACKAGE_NAME +  ".R$" + resType);
                for(Field resField : resTypeClass.getFields()){
                    int oldResId = resField.getInt(null);
                    ids.put(oldResId, new ResId(resType, resField.getName()));
                }
            } catch (Exception e) {
            }
        }
    }

    public static int R(int resourcesId){
        if(resourcesId == -1) return -1;
        ResId resId = ids.get(resourcesId);
        if(resId.newId != -1) return resId.newId;
        String packageName = mApplication.getPackageName();
        int newId = mApplication.getResources().getIdentifier(resId.name, resId.Type, packageName);
        resId.newId = newId;
        return newId;
    }

    private Application getApplicationByReflect() {
        try {
            @SuppressLint("PrivateApi")
            Class<?> activityThread = Class.forName("android.app.ActivityThread");
            Object thread = activityThread.getMethod("currentActivityThread").invoke(null);
            Object app = activityThread.getMethod("getApplication").invoke(thread);
            if (app == null) {
                throw new NullPointerException("you should init first");
            }
            return (Application) app;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        throw new NullPointerException("you should init first");
    }

    private static class ResId{
        private String Type;
        private String name;
        private int newId = -1;
        public ResId(String type, String name) {
            Type = type;
            this.name = name;
        }
    }
}

使用:

引入:import static com.xiyou.sdk.p.sdk.utlis.Res.R;

初始化:Res.get().init();

layout调用:R(R.layout.xy_sdk_main_activity)
id调用:R(R.id.test)

到这里SDK中查找资源使用R.xx.xx的方式就介绍完了,希望你可以喜欢!

上一篇下一篇

猜你喜欢

热点阅读