本文同步自我是一只香脆的大鸡排html
嘿,我亲爱的Android老司机。java
你是否还依稀记得第一次使用TextView的setText方法设置了一个空数据,获得了这么个玩意。android
(文中错误,见底部解释)程序员Caused by: java.lang.NullPointerException
对的,这玩意老烦人了,写习惯了OC PHP等语言的程序员在java和android里估计会疯掉。json
要满世界判断是否为空,是否等于双引号【“”】。后端
咱们也许是使用过TextUtils.isEmpty(s)
,StringUtils.isEmpty(s)
这种判断。ide
可是无一例外它们都须要在咱们设置到View以前作一次判断。性能
不,我受够了!!spa
咱们来搞事情解决一下这种问题。插件
首先咱们知道,大多数状况下控件上的数据都是来自服务端的接口吐出。接口中给出的数据有空的状况是很是常见的。
多数状况下后台给出的数据如: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来扩展一下咱们自定义的对象。实现方式以下。
仍是前面的对象:
public class Smart {
String name;
String pwd;
int size;
}
复制代码
不一样的是,这里咱们没有改动它。而是经过Adpter来解析,或者说是串改自己json中的键值数据。
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
等等等于空
错误 错误 清除 清除 记忆清除 归零
1: 这里并不会空指针,是我之前记错了。该打脸,该打!昨天晚上从2.2的源码翻到7.0全部的setText方法都不会引发空指针异常。是个人疏忽。近凭借之前的记忆去认为会空指针。给你们添麻烦了。
private void setText(CharSequence text, BufferType type, boolean notifyBefore, int oldlen) {
if (text == null) {
text = "";
}
.....
}
复制代码
可是若是服务端返回空数据,咱们就无论了,这个仍是不够体面的。要么服务端改,要么咱们在中间层过滤掉。再不行就是平常if else。 可我总觉着在View层作这些事情是不够明智的。View应该只关注本身要展现什么东西,而不该该出现异常数据还放到了V层处理。应该在M层就要被过滤掉或处理。
一些不成熟的想法:
1.通用形DataAdpter在反序列化时进行过滤,但若是作到通用。就必定再也不知足个性化的需求。
2.我有想过Hook TextView的方法来实现脏数据偷懒行为,这不外乎两种作法。要么反射再注入Hook。要么编译时对代码埋点。作法都不是很完美。
3.参考lombok的实现,或许也能作到在对象Get的时候生成一些校验。
4.依赖as作成插件Gradle Pulgin,在编译时像lomBok同样。他们都不会产生后期运行时和或像反射带来的性能影响。
拼死挣扎的Android程序员。