SDK开发时优雅引用资源文件

SDK开发时优雅引用资源文件

Android小彩虹2021-07-15 17:04:45100A+A-

背景

我们平时开发安卓时候使用资源都是使用R文件,用类似R.id.xxx的形式获取资源id


public class SdkActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sdk);
        ImageView avatarIv = findViewById(R.id.fatty_avatar);
        TextView helloTv = findViewById(R.id.fatty_hello);

        avatarIv.setImageResource(R.drawable.ic_baseline_emoji_emotions_24);
        helloTv.setText(R.string.hello);
    }
}

但是,在SDK开发中,我们常会遇到这个问题:在R文件中找不到指定ID。

因为代码编译完之后就用R文件里面的id覆盖了原本的R.xx.xx引用, 而有些时候因为一些外界原因, (比如对接方使用eclipse等古老的工具去接入,或者我们反编译后二次打包重新用aapt生成了R文件而原本的代码中的引用的id却没有修改) 可能导致R文件ID查找失败。

要解决这个问题可以从以下几个方面入手

  • 使用aar与android studio,aar带有自己的包名与r文件,不会出现r文件的错乱
  • 从细节层面修复R的id问题,即保证每个id的引用都正确
  • 使用代码动态获取id

实际的sdk开发中,更多使用第三种方法,于是我们使用如下方案


public class Util_Resource {

	public static int getLayoutIdentifier(Context context, String name) {
		return getResourceId(context, name, "layout");
	}

	public static int getIdIdentifier(Context context, String name) {
		return getResourceId(context, name, "id");
	}

	public static int getDrawableIdentifer(Context context, String name) {
		return getResourceId(context, name, "drawable");
	}

	public static int getMipmapIdentifer(Context context, String name) {
		return getResourceId(context, name, "mipmap");
	}

	public static int getStyleIdentifer(Context context, String name) {
		return getResourceId(context, name, "style");
	}

	public static int getStringIdentifer(Context context, String name) {
		return getResourceId(context, name, "string");
	}

	public static int getAttrIdentifer(Context context, String name) {
		return getResourceId(context, name, "attr");
	}

	public static int getAnimIdentifer(Context context, String name) {
		return getResourceId(context, name, "anim");
	}

	public static int getColorIdentifer(Context context, String name) {
		return getResourceId(context, name, "color");
	}

	public static int getDimenIdentifer(Context context, String name) {
		return getResourceId(context, name, "dimen");
	}


	private static int getResourceId(Context context, String name, String defType) {
		return context.getResources().getIdentifier(name, defType, context.getPackageName());
	}
}

public class SdkActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(Util_Resource.getLayoutIdentifier(this, "activity_sdk"));
        ImageView avatarIv = findViewById(Util_Resource.getIdIdentifier(this, "fatty_avatar"));
        TextView helloTv = findViewById(Util_Resource.getIdIdentifier(this, "fatty_hello"));

        avatarIv.setImageResource(Util_Resource.getDrawableIdentifer(this, "ic_baseline_emoji_emotions_24"));
        helloTv.setText(Util_Resource.getStringIdentifer(this, "hello"));
    }
}

以上代码是我接触过的很多sdk使用的,这种方式是比较安全的,但是不难看出,代码层次存在太多弊端了

  • 代码太长,每次使用都要写一堆重复代码
  • 太多字符串魔法值,完全不利于资源的管理,也容易因为写错字符导致没必要的错误
  • id 或者res 文件改名时候要更新必须全局搜索,太不规范
  • 同样的,代码可读性也是大受影响

优化

然后就是本文的重点了,我们要怎么去优化这些代码才能使其看起来优雅一些呢?

消灭魔法值

首先就是id的引用,应该是用常量而不是字符串,建立一个文件专门存放

public final class ResConst {

        public static String activity_sdk = "activity_sdk";
        public static String fatty_avatar = "fatty_avatar";
        public static String fatty_hello = "fatty_hello";
        public static String hello_id = "fatty_hello";

}

这个文件看着没什么意义,但我们将所有与R相关的引用都跟这个文件关联起来, 那我们查询某个id的使用情况或者修改id,只要编辑这个文件就行了,对维护来说简直是事半功倍。

自制R文件

既然我们把跟R操作相关的常量都放到了一个文件,那么, Util_Resource.getLayoutIdentifier(context,resId)这样的寻值操作为什么不也交给这个文件去处理呢, 然后我们只需要使用类似ResConst.activity_sdk这样的代码就可以获取id的目的。
机智的朋友可能发现了,我们这个操作跟R文件有点异曲同工。 于是,我想,为什么不保留我们R的使用习惯。同样的,用R.id.xxx这种方式调用。

我们看看R文件的代码


public final class R {

    public static final class anim {
        public static final int abc_fade_in = 2130771968;
        ...
        
        private anim() {
        }
    }

    public static final class attr {
        public static final int actionBarDivider = 2130837504;
        ...
        private attr() {
        }
    }

    public static final class bool {
        public static final int abc_action_bar_embed_tabs = 2130903040;
        ...

        private bool() {
        }
    }

    public static final class color {
        public static final int abc_background_cache_hint_selector_material_dark = 2130968576;
        ...

        private color() {
        }
    }

    public static final class dimen {
        public static final int abc_action_bar_content_inset_material = 2131034112;
        ...

        private dimen() {
        }
    }

    public static final class drawable {
        ...
        /* added by JADX */
        public static final int $ic_launcher_foreground__0 = 2131099648;

        private drawable() {
        }
    }

    public static final class id {

        public static final int fatty_avatar = 2131165241;
        public static final int fatty_hello = 2131165242;
        public static final int fill = 2131165243;
        ...
        private id() {
        }
    }

    public static final class integer {

        ...

        private integer() {
        }
    }

    public static final class layout {

        public static final int activity_main = 2131296284;
        public static final int activity_sdk = 2131296285;
        ...

        private layout() {
        }
    }

    public static final class mipmap {
        public static final int ic_launcher = 2131361792;
        public static final int ic_launcher_round = 2131361793;

        private mipmap() {
        }
    }

    public static final class string {
      
        ...
        public static final int app_name = 2131427367;
        public static final int fatty_hello = 2131427368;

        private string() {
        }
    }

    public static final class style {
        ...

        private style() {
        }
    }

    public static final class styleable {
        
        ...

        private styleable() {
        }
    }

    private R() {
    }
}

这里我用LR来命名我们的文件,然后在里面建立一个id的静态内部类。(我们需要一个context,可以设置全局的applicationContext)

public final class LR {
 public final static class id {

        public static int fatty_avatar = getId("fatty_avatar");
        public static int fatty_hello = getId("fatty_hello");


        private final static int getId(String str) {
            return Util_Resource.getIdIdentifier(MainApplication.getAppContext(), str);
        }
    }

}

然后就可以实现类似R的引用效果:LR.id.fatty_avatar

对于string,可以把给getString()方法也封装进来

 public final static class string {

        public static int hello_id = getStringId("fatty_hello");
        public static String hello = getString(hello_id);

        private static int getStringId(String str) {
            return Util_Resource.getStringIdentifer(MainApplication.getAppContext(), str);
        }

        private static String getString(int id) {
            return MainApplication.getAppContext().getResources().getString(id);
        }
    }

最终的LR文件

public final class LR {

    /*********************************dimen***********************************************************/
    public final static class dimen {

        private static int getDimen(String str) {
            return Util_Resource.getDimenIdentifer(MainApplication.getAppContext(), str);
        }
    }

    /**********************************layout********************************************************************/
    public final static class layout {
        public static int activity_sdk = getLayout("activity_sdk");

        private static int getLayout(String str) {
            return Util_Resource.getLayoutIdentifier(MainApplication.getAppContext(), str);
        }
    }

    /**********************************id********************************************************************/
    public final static class id {

        /***************** activity_sdk ****************/
        public static int fatty_avatar = getId("fatty_avatar");
        public static int fatty_hello = getId("fatty_hello");


        private final static int getId(String str) {
            return Util_Resource.getIdIdentifier(MainApplication.getAppContext(), str);
        }


    }

    /**********************************style********************************************************************/
    public final static class style {

        private static int getStyle(String str) {
            return Util_Resource.getStyleIdentifer(MainApplication.getAppContext(), str);
        }
    }
    /**********************************style********************************************************************/
    public final static class color {
        private static int getColor(String str) {
            return Util_Resource.getColorIdentifer(MainApplication.getAppContext(), str);
        }
    }


    /**********************************anim********************************************************************/
    public final static class anim {
        private static int getAnim(String str) {
            return Util_Resource.getAnimIdentifer(MainApplication.getAppContext(), str);
        }
    }

    /**********************************mipmap********************************************************************/
    public final static class mipmap {

        private static int getMipmap(String str) {
            return Util_Resource.getMipmapIdentifer(MainApplication.getAppContext(), str);
        }
    }

    /**********************************drawable********************************************************************/
    public final static class drawable {


        public static int ic_baseline_emoji_emotions_24 = getDrawable("ic_baseline_emoji_emotions_24");

        private static int getDrawable(String str) {
            return Util_Resource.getDrawableIdentifer(MainApplication.getAppContext(), str);
        }
    }

    public final static class string {

        public static int hello_id = getStringId("fatty_hello");
        public static String hello = getString(hello_id);

        private static int getStringId(String str) {
            return Util_Resource.getStringIdentifer(MainApplication.getAppContext(), str);
        }

        private static String getString(int id) {
            return MainApplication.getAppContext().getResources().getString(id);
        }
    }
}

效果

最终可以用以下方式调用

public class SdkActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(LR.layout.activity_sdk);
        ImageView avatarIv = findViewById(LR.id.fatty_avatar);
        TextView helloTv = findViewById(LR.id.fatty_hello);

        avatarIv.setImageResource(LR.drawable.ic_baseline_emoji_emotions_24);
        helloTv.setText(LR.string.hello);
    }
}

可以看到,代码变得跟一开始R文件调用的一样简洁,我们甚至可以将调用R文件的代码可以轻松切换到LR

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1

联系我们