【工程实践】服务器数据解析

本文来自网易云社区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+款云产品!

更多网易研发、产品、运营经验分享请访问网易云社区


相关文章:
【推荐】 有些验证码看起来很容易可是没人作自动识别的缘由分析

相关文章
相关标签/搜索