Android拼接实现动态对象方法详解
流浪汉kylin 人气:01. 前言
我们往往有些配置文件,当项目大的时候,一些配置文件或者一些判断逻辑就会变得复杂,会出现很多判断语句,我在想,能不能通过前缀拼接动态参数并且借助反射等方式去消除一些判断,让这些判断的地方去实现动态。
当前只是有个想法,但是这个操作又没有风险,对性能影响大不大,会不会在使用中出现什么问题,还不得而知,下面就用一些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,就其实多走了两步,但是这个对性能的影响有多少,这就需要具体去取舍了。因为我是一想到就写出来,所以可能有些地方没考虑周全。
加载全部内容