亲宝软件园·资讯

展开

Android拼接实现动态对象方法详解

流浪汉kylin 人气:0

1. 前言

我们往往有些配置文件,当项目大的时候,一些配置文件或者一些判断逻辑就会变得复杂,会出现很多判断语句,我在想,能不能通过前缀拼接动态参数并且借助反射等方式去消除一些判断,让这些判断的地方去实现动态。

当前只是有个想法,但是这个操作又没有风险,对性能影响大不大,会不会在使用中出现什么问题,还不得而知,下面就用一些Demo来描述一下这个方案。

这个思路的规则就是:默认前缀拼接动态参数获取对象,动态参数可以是从后台获取,可以是用文件中获取,可以是从系统参数获取,等等,任何你能想到的地方,这个根据自己的场景去涉及从何获取。

2. 动态选密钥

举例的场景不一定好,但是应该能看出这个方案的使用方式。

假如我们有做跨端的对称加密,然后希望密钥有几套,不固定只有一套,然后要动态去选择密钥,当然这个动态的条件要简单,不然也只能if-else去写了。假如我有10套密钥,我根据当前的时间戳的最后一位,去选择使用哪套

假设这些公钥

public class KeyLibs {
    public static final String KEY0 = "0000000000000000";
    public static final String KEY1 = "1111111111111111";
    public static final String KEY2 = "2222222222222222";
    public static final String KEY3 = "3333333333333333";
    public static final String KEY4 = "4444444444444444";
    public static final String KEY5 = "5555555555555555";
    public static final String KEY6 = "6666666666666666";
    public static final String KEY7 = "7777777777777777";
    public static final String KEY8 = "8888888888888888";
    public static final String KEY9 = "9999999999999999";
}

如果我要用if-else去写

String key;
int type = (int) ((System.currentTimeMillis()/1000) % 10);
if (type == 0){
    key =  KeyLibs.KEY0;
}else if (){......}
......
else if (type == 9){
    key =  KeyLibs.KEY9;
}

这样写就很让人不舒服,但是如果我们用反射

try {
    long time = System.currentTimeMillis()/1000;
    Log.v("mmp", "获取到的时间:" + time);
    Class cls = Class.forName("com.kylin.demo.KeyLibs");
    Field fields = cls.getDeclaredField("KEY" + (time % 10));
    fields.setAccessible(true);
    String result = (String) fields.get(null);
    Log.v("mmp", "获取到的key:" + result);
} catch (Exception e) {
    e.printStackTrace();
}

可以看看结果

这里的"KEY" + (time % 10)就是拼接操作

这样据这个例子好像又感觉不要太好,在这个基础上我们变一变。假如我们有很多套域名,根据一个参数的值去判断去用什么域名。

根据一个参数的值去判断去用什么域名,这个参数可以是后台返回一个string字符串,可以是写在文件中(比如利用v1签名的漏洞的参数),也可以是其它方式。总之需要只根据这个参数的值去判断使用哪个域名 ,那我们可以这样做

先写下域名常量

public class KeyLibs {
    public static final String URL_SHUAI = "www.shuai.com";
    public static final String URL_ZHENDESHUAI = "www.zhendeshuai.com";
    public static final String URL_SHIFENSHUAI = "www.shifenshuai.com";
    public static final String URL_QUESHISHUAI = "www.queshishuai.com";
}

然后通过反射去获取

try {
    String type = .......
    Class cls = Class.forName("com.kylin.demo.KeyLibs");
    // toUpperCase 是转大写
    Field fields = cls.getDeclaredField("URL_" + toUpperCase(type));
    fields.setAccessible(true);
    String result = (String) fields.get(null);
} catch (Exception e) {
    e.printStackTrace();
}

假如你的type拿到的是shifenshuai,那这里拿到的域名就是URL_SHIFENSHUAI。那有个朋友就说了,为什么不动态返回这个域名呢,我这不是举例嘛,没想到什么比较好的例子,大概能看懂这个意思就行。

这里的"URL_" + toUpperCase(type)就是拼接操作

3. 换肤上的使用

假如我要使用换肤,我可以这样定规则:我的皮肤资源ID的名称是原资源的名称加上下划线加上皮肤名

比如我的这一套皮肤的皮肤名是"plugin",我的原皮肤中有个图片kylin_close,那我的这个图片在这套皮肤中的名字就叫kylin_close_plugin

PS:我这里只是举个例子,一般皮肤资源不会直接这样和原资源放在一起,要么动态皮肤放在插件中,要么静态皮肤放在单独一个文件用gradle去控制资源合并

那我要做的就是当我从任何地方接收到这个皮肤名之后,我把原皮肤换成新的皮肤。Resources的getIdentifier能根据名称找到皮肤,但我不想通过名称,我想通过资源,这样也方便我查看资源,那我可以这样写 (代码直接在这写,没经过验证,如果拿去用有问题,可自行调整)

public int getPluginId(int id){
    String name = "plugin"; // todo 动态获取的参数
    String rName = getResources().getResourceName(id);
    rName = rName + "_" + name;
    // todo 这里我写死是drawable,显示可以根据TypeId去判断是什么
    int result = getResources().getIdentifier(rName, "drawable", getPackageName());;
    if (result == -1){
        result = id; //找不到资源的情况下用会原资源
    }
    return result;
}

在调用的地方

imageView.setImageResource(getPluginId(R.drawable.kylin_close));

这里的rName = rName + "_" + name就是拼接操作。

先解读一下这段代码,因为也算是不完整的代码。name就是我们获取的动态参数,上面也说了,这个参数是可以从后台反,可以从本地文件拿,可以从你自己设计的任何一个地方拿到。然后getResources().getResourceName就是根据传进来的资源ID拿到资源名称,然后rName = rName + "_" + name拼接操作,就是得到我们皮肤的资源名称,再用getIdentifier方法通过资源名称拿到资源ID,最后判断如果拿不到资源ID的话,就返回原资源ID。 然后这里有个地方我没写,就是你可以通过资源ID去判断资源的类型(我这里写死drawable),其实这个可以根据id去判断,我们都知道ID的组成是有规则的,分为PackageId、TypeId、EntryId这些,我们可以进行拆解判断TypeId就知道是什么资源了,这里我就不演示了,相信大家能理解。

好,那这么做的好处是什么,这么做其实有个好处就是如果你的某个资源要新皮肤,你直接给新皮肤的资源按约定格式去命名就行,这样就不用去改代码。

4. 总结

我这里其实感觉例子列举得不是很好,但是主要是想表达,通过自己去约定一套规则,通过前缀拼接动态参数的方式去消除一些非必要的判断语句,并且在一定程度上能提高扩展性。但相对的也有缺点,比如换肤那个例子,我先通过ID拿到名称,再通过名称拿到目标ID,就其实多走了两步,但是这个对性能的影响有多少,这就需要具体去取舍了。因为我是一想到就写出来,所以可能有些地方没考虑周全。

加载全部内容

相关教程
猜你喜欢
用户评论