美文网首页Android开发
解决SDK中getIdentifier的痛点

解决SDK中getIdentifier的痛点

作者: Ray206 | 来源:发表于2020-08-08 16:43 被阅读0次

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

  • 解决方式.
Resources.getIdentifier(String name, String defType, String defPackage)

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

  • getIdentifier的痛点
    1. 删除其中一个layout,编译可以通过,使用时因为找不到资源而无法使用
    2. 使用名称去查找,无编译器支持,每次使用时查找资源名称都要硬敲
    3. getIdentifier的原理就是通过反射去查找当前包名下面,名称对应的资源id,反射影响性能,如果有反复查找的需求,性能上就会有影响
  • 我的解决方法
    jar包的资源id(A)->资源名称(B)->新生成的资源ID(C)
    不管怎么编译资源名称是不会改变的,我们导出jar包时,也导出对应的资源的R.java到jar包中。
    如果使用eclipse开发可以直接吧gen目录选中和导出,如果使用android studio开发,library是不生成R文件的。所以需要
//apply plugin: 'com.android.library'
改成
apply plugin: 'com.android.application'

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

  • 步骤
    1. 读取旧jar包中R文件中资源id名称,生成对应的索引
    2. 在外部调用R.xx.xx时通过对应的资源id找到名称,并查找到对面资源的名称
    3. 通过getIdentifier/或者反射的方式,找新包名下对应的新的资源id
    4. 把旧id和新的id做一个映射,然后保存
    5. 工具类对R.xx.xx做包装,当调用R.xx.xx时返回新包名下的新资源id

整个流程就是这样了,相当于我们自己生成了一个资源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的方式就介绍完了,希望你可以喜欢!

相关文章

网友评论

    本文标题:解决SDK中getIdentifier的痛点

    本文链接:https://www.haomeiwen.com/subject/akxtahtx.html