本文同步自 我是一只香脆的大鸡排
嘿,我亲爱的 Android 老司机.
你是否还依稀记得第一次使用 TextView 的 setText 方法设置了一个空数据,得到了这么个玩意.
Caused by: java.lang.NullPointerException
对的,这玩意老烦人了,写习惯了 OC PHP 等语言的程序员在 java 和 android 里估计会疯掉.
要满世界判断是否为空,是否等于双引号【""】.
我们也许是使用过
这种判断.
TextUtils.isEmpty(s)
,
StringUtils.isEmpty(s)
但是无一例外它们都需要在我们设置到 View 之前做一次判断.
不,我受够了!!
Scenes1
我们来搞事情解决一下这种问题.
首先我们知道,大多数情况下控件上的数据都是来自服务端的接口吐出.接口中给出的数据有空的情况是非常常见的.
多数情况下后台给出的数据如:size=1 name=null 转成 json 后
会变成:{size:1,name:null}
或者这样
{size:1,"name":"null"
或者这样:{size:1}
看见没,name 可以是 null,也可以是双引号 "null",甚至是直接不返回.
移动端同学新手上路第一天看完返回的结果:nmpp 啊啊啊啊啊啊!!!.
后端同学:你咬我?就是没有数据呀,反正我是不会改的.
方案一
能难倒我?不改就不改.蛮了不起了吧?
看我来个初值大法.
额,如果我有一万个字段怎么办,这里的双引号写一万个内存会不会有影响啊?
public class SmartZero {
String name = "";
String pwd = "";
int size;
}
有了,这样写.
啊哈哈,这样就只有一个静态引用了.
public class SmartOne {
final static String NULL = "";
String name = NULL;
String pwd = NULL;
int size;
}
提示:
常量字符串被引用时,如果内容在一致的情况下,会在常量池里只有一份,所有的引用将指向该地址.
简单来说,这里根即便写与不写
static String NULL = "";
和前者的写法在运行时内存里的变化并无区别.他们的区别仅是第一种写法会导致寄存器上空字符会被从新设值指向空字符串.所以后则的写法可能会节约细微的时间,几乎可以忽略不计.(详情分析读者可以看 smali 逆向文件)
这样写完后得到的结果就是:
马上就有大兄弟要说了:
你这个好 low 哇.不对吧,你这个只满足了是 null 的情况下.如果 name 服务端返回的是双引号的 "null" 这种玩意,怎么搞?你还不是一样要在 TextView.setText 之前判断一把?赫,大兄弟勿躁,勿躁嘛!且听我把话说完,我们还有方案二的,坚决抵制在 View 层做数据脏检查.
方案二
目前主流 Gson 和 Fastjson 是序列化 json 最好用的方式.大鸡排这里尝试了下 Gson 的方式.我们不能直接使用 Gson 来转换.这里需要用到 Gson 库里的 TypeAdapter 来扩展一下我们自定义的对象.实现方式如下.
还是前面的对象:
不同的是,这里我们没有改动它.而是通过 Adpter 来解析,或者说是串改本身 json 中的键值数据.
public class Smart {
String name;
String pwd;
int size;
}
现在我们运行一下看看效果.
public class SmartTypeAdapter extends TypeAdapter<Smart> {
@Override
public Smart read(JsonReader in) throws IOException {
final Smart smart = new Smart();
smart.name = "";
smart.pwd = "暂无";//初始化值是因为Gson不会遍历在json中没有的字段
in.beginObject();
String s=null;
while (in.hasNext()) {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
in.endObject();
return smart;
}
switch (in.nextName()) {
case "name":
if (in.peek() != JsonToken.NULL){
s = in.nextString();
if (s != null&&!s.equals("null")){
smart.name = s;
break;
}
}
smart.name = "";
break;
case "pwd":
if (in.peek() != JsonToken.NULL){
s = in.nextString();
if (s != null&&!s.equals("null")){
smart.pwd = s;
break;
}
}
smart.pwd = "暂无";
break;
case "size":
smart.size = in.nextInt();
break;
}
}
in.endObject();
return smart;
}
}
当 json 为:{size:18} 的情况下.
当 json 为:
{size:18,name:"null",pwd:"null"
的情况下.
我们可以发现方案二的办法要比方案一好很多,可定制化程度高.完全可以过滤掉字符串 "null" 这种情况的脏数据.
当 json 为:
{"size": 18,"name": "null","pwd": null}
null 没有双引号的情况就不展示了,效果同上.
TypeAdapter 的扩展性其实不仅仅可以用在这里做不同类型的空判断,它还可以做动态的 json 键值或者多类型映射.
总结
大多数情况在 View 层里再做很多脏数据判断并不合适,但实际情况我们还是这样写着.
直到我们学会了偷懒
不再写
if if if 等等等于空错误 错误 清除 清除 记忆清除 归零
来源: https://juejin.im/post/5a55c8896fb9a01caf374e93