【工程實踐】服務器數據解析

本文來自網易雲社區html

做者:孫建順java


在客戶端開發過程當中一個重點內容就是解析服務器數據,關於這個話題也許你們首先會去思考的問題是用哪一個json解析庫。是的,目前經過json格式進行數據傳輸是主流的方式,確實不一樣的json解析庫在性能方面也有一些差別。以上問題當然重要,可是在開發過程當中常常遇到的每每並非性能相關的問題,本文從數據使用的角度給你們分享一些經驗。json

  • 數據成員設爲private,默認不提供set方法後端

解析獲得的數據在產品中被修改的概率極低,數據的修改很容易致使bug的產生。另外,數據對象解析的過程當中每每是經過反射直接構建對象,基於以上兩點思考,咱們提倡對數據的修改遵循關閉原則,即默認不提供set方法,只有極少數狀況容許對數據進行修改。數組

  • 提供友好的數據訪問接口服務器

讀取數據時每每是帶着具體的場景使用的目的而來,最原始的數據一般並不可以直接知足使用,常常須要作一些簡單處理。所以,經過get方法提供更友好的數據獲取方式。在get方法中不僅是返回最原始的數據,而是疊加上一些簡單的處理或者數據的拼裝。
例如:數據結構

public class LearnInfo {    // 原始數據
    private int learnStatus;    // 簡單封裝後的方法
    public boolean isLearned() {        return learnStatus == 2;
    }
}public class Example {    private void doSomething1(LearnInfo learnInfo) {        // 經過獲取原始數據作業務處理
        if (learnInfo.getLearnStatus() == 2) {            // dosomething
        }
    }    private void doSomething2(LearnInfo learnInfo) {        // 經過簡單封裝過的接口獲取狀態信息
        if (learnInfo.isLearned()) {            // do something
        }
    }
}
  • 隔離json字符串與代碼調用之間的耦合框架

數據解析庫每每經過反射調用成員字段,致使成員變量的命名與json串中的字符存在嚴格的對應關係,經過get方法能夠屏蔽掉這種強耦合依賴。
例如:版本1.0ide

public class Course {    private description;    public String getDescription() {        if (description == null) {            return "";
        }        return description;
    }
}public class Example {    private void doSomething(Course course) {
        mTextView.setText(course.getDescription());
    }
}

版本1.1,因爲某些緣由在版本迭代過程當中後端字段可能會發生更改性能

public class Course {    // 此處被修改了
    private desc;    // 接口不變
    public String getDescription() {        if (desc == null) {            return "";
        }        return desc;
    }
}public class Example {    // 調用方無需修改
    private void doSomething(Course course) {
        mTextView.setText(course.getDescription());
    }
}
  • 調試與日誌

Android Studio中針對成員變量能夠經過Field Watchpoint進行斷點調試,而引入get方法以後,增長了常規斷點調試的方式。在有必要的狀況下,還能夠在get方法中增長日誌。

  • 引入數據合法性檢查機制

一旦非法數據進入到系統之後,會帶來不少異常的狀況,在代碼設計時異常邏輯分支處理不夠充分的話,很容易致使系統異常,甚至應用程序崩潰。所以,比較建議儘可能在源頭堵截住異常數據的進入,越早處理對後期的影響越小。通常來講從服務器獲取數據在將其解析爲系統中真正有含義的對象時,視爲檢查數據合法性的第一時間比較恰當。常見的非法數據。


  1. 不容許空的字段返回了空值

java語言對空指針的處理並不友好,NullPointerException是致使崩潰的主要緣由之一。在系統迭代過程當中常常出現數據結構中該返回值的地方卻返回了空值。針對這一問題有多種解決方案,有一種作法是作系統全局的異常捕獲,這種作法簡單粗暴,沒有從根源上解決問題,不夠優雅。另外一種作法在用到數據的地方所有加上判空,這種作法繁瑣,容易遺漏,開發人員比較痛苦。筆者在項目開發過程當中嘗試了從框架層面解決這一問題的技術方案。引入Nullable與NotNull註解的支持,在定義字段時經過註解註明,在解析時校驗字段值是否符合註解描述。註解方式通常來講只適合解決與後端約定不容許空的值的校驗。事實上爲了知足更好的用戶體驗,更多的字段在設計時會傾向於容許空值。所以在容許空值進入的狀況下,在設計get方法時要求不能返回空值,作第二層防禦。
例如:

public class Course {    @NotNull
    private long id; // id 不容許空

    private List<Unit> units; // 列表能夠爲空
    private String description; // 描述信息能夠爲空

    // 不容許直接返回null值,下降調用方的使用成本
    public List<Unit> getUnits() {        if (units == null) {            return new ArrayList();
        }        return units;
    }    // 不容許返回null值,下降調用方的使用成本
    public String getDescription() {        if (description == null) {            return "";
        }        return description;
    }
}


  1. 返回的數據不符合邏輯

在某些狀況下,解析獲得的數據不符合邏輯,例如:

public class PageInfo {
    private totalCount;    private currentPage;
}totalCount : 0, currentPage : 11

因爲服務器上異常處理不當,客戶端解析獲得totalCount爲0,currentPage爲11,顯然不符合邏輯。若將此數據繼續往下傳遞頗有可能引起數組越界的異常,從而致使應用程序崩潰。 所以,咱們還引入了第三層防禦機制,定義LegalModel接口,代碼以下:

public interface LegalModel {    boolean check();
}public class PageInfo implements LeagalModel {    private totalCount;    private currentPage;    @Override
    public boolean check() {        return totalCount > currentPage;
    }
}public class Example {    private void doSomething(PageInfo pageInfo) {        if (!pageinfo.check()) {            // 判斷數據不合法
        }
    }
}
  • 關於混淆

當json解析庫經過反射構建數據對象時,數據對象類不能參與混淆,不然就沒法找到對應的字段名。避免混淆一般的方法是將數據模型類集中放置在相同的包名下面,在混淆配置中經過配置路徑來避免混淆。這種方式有一個比較明顯的侷限性,即當文件換一個路徑之後,則要在配置文件中追加相應的路徑。這種限制對開發人員操做過程當中很是不友好,比較繁瑣,並且特別容易遺漏。所以,推薦你們用一種經過空接口的方式,來解決避免混淆問題。
代碼以下:

// 定義防止混淆空接口public interface NoProguard {

}// 不須要混淆的類去實現空接口public class Course implements NoProguard {    private String description;    // 內部類一樣適用
    public static class LearnInfo implements NoProguard {        private int learnStatus;    
    }
}// 配置代碼-keep interface com.example.NoProguard {*;}
-keep class * implements com.example.NoProguard {*;}
  • 建議使用基本類型取代包裝類

包裝類的默認值爲null,很是容易引發空指針異常。而基本類型自帶默認值,省去了大量的判空代碼。以int與Integer爲例,某些狀況下對於int默認值0會有必定的含義,此種場景的機率不高,通常來講在約定時能夠儘可能避免使用0值來簡單規避。


網易雲免費體驗館,0成本體驗20+款雲產品!

更多網易研發、產品、運營經驗分享請訪問網易雲社區


相關文章:
【推薦】 有些驗證碼看起來很容易可是沒人作自動識別的緣由分析

相關文章
相關標籤/搜索