本文同步自我是一隻香脆的大雞排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程序員。