Android APP开发实战:从规划到上线全程详解
上QQ阅读APP看书,第一时间看更新

第3章 APP端和服务器端的协作

3.1 接口设计注意事项

在接口设计中要注意以下事项。

(1)首先需要确定APP和服务器间用什么格式传输数据,常用的有两种:XML和JSON。下面使用了XML格式和JSON格式表示同样的信息进行比较:

        <? xml version="1.0" encoding="utf-8" ? >
        <country>
          <name>中国</name>
          <province>
            <name>广东</name>
            <citys>
              <city>广州</city>
              <city>深圳</city>
            </citys>
          </province>
          <province>
            <name>广西</name>
            <citys>
              <city>南宁</city>
              <city>桂林</city>
            </citys>
          </province>
        </country>

以上是XML格式文件的数据。

        {
          "name": "中国",
          "quantity": 2,
          "provinces": [
            {
              "name": "广东",
              "quantity": 2,
              "citys": {
                "city": [
                  "广州",
                  "深圳"
                ]
              }
            },
            {
              "name": "广西",
              "quantity": 2,
              "citys": {
                "city": [
                  "南宁",
                  "桂林"
                ]
              }
            }
          ]
        }

以上是JSON格式文件的数据。

从上述示例可看出,XML文件中存在大量的描述信息,大大增加了网络传输的数据量;同样的内容用JSON格式传输的数据量比较少,相应的网络传输速度和数据解析速度也都快,所以首选JSON格式。

JSON格式的字段类型值常用的有以下几种。

· Number:整数或浮点数。

· String:字符串。

· Boolean:true或false。

上述三种属于基本类型。

· Array:数组,包含在方括号[]中。

· Object:对象,包含在大括号{}中。

上述两种属于复合类型,其中可以包含各基本类型字段。

示例如下:

        {//对象
          "name": "中国", //字符串
          "quantity": 2, //整数
          "isAsia": true, //布尔类型
          "provinces": [//数组
            {
              "name": "广东",
              "quantity": 2,
              "citys": {
                "city": [
                  "广州",
                  "深圳"
                ]
              }
            },
            {
              "name": "广西",
              "quantity": 2,
              "citys": {
                "city": [
                  "南宁",
                  "桂林"
                ]
              }
            }
          ]
        }

(2)需要设计JSON数据的具体格式。

APP发送请求(有非数组格式的具体参数),示例如下:

        {
              "params":{
                  "username":"aaa",
                  "password":"123456"
              }
        }

APP发送请求(有数组格式的具体参数),示例如下:

        {
          "params": {
            "products": [
              {
                "name": "可乐",
                "quantity ": 1
              },
              {
                "name": "雪碧",
                "quantity ": 2
              }
            ]
          }
        }

APP发送请求(无具体参数),示例如下:

        {
          "params": {
          }
        }

服务器端处理成功后,返回给APP的数据(只返回操作状态,不返回数据),示例如下:

        {
          "code": 800
        }

服务器端处理成功后,返回给APP的数据(返回操作状态和非数组数据),示例如下:

        {
            "code": 800
            "result":{
                  "message":"订单提交成功"
            }
        }

服务器端处理成功后,返回给APP的数据(返回操作状态和数组数据),示例如下:

        {
            "code": 800
            "result":{
              "products": [
                {
                  "name": "可乐",
                  "quantity ": 1
                },
                {
                  "name": "雪碧",
                  "quantity ": 2
                }
              ]
            }
        }

服务器端处理失败后,返回给APP的数据(只返回操作状态和出错提示),示例如下:

        {
            "code": 801
            "result":{
                  "message":"密码错误,请重新输入"
            }
        }

在定义JSON中的字段名称时,要尽量短小,以减少网络传输的数据量。

(3)服务器端采用的语言有Java这样的强类型语言,也有PHP这样的弱类型语言,弱类型语言对变量类型没有强类型语言那么严格,但Android和iOS开发使用的语言都是强类型的,导致APP端常会遇到变量类型出错的问题。如需要整型数据,结果服务器传的数字有小数;需要非字符串类型的数据,结果服务器传的数据是字符串等。

为解决这类问题,在和服务器端定义字段的数据类型时,建议使用以下方案。

· 在APP端涉及数学的加、减、乘、除或比较大小运算的字段,统一使用double类型。int和float类型可以算是double类型的子集,这样只要APP端使用double类型,无论服务器端返回的是int类型,还是float类型,都不会解析出错。

· 布尔型的字段也使用double类型代替,服务器端返回1表示true,返回0表示false。

· 不涉及数学的加、减、乘、除或比较大小运算且非布尔型的字段,统一使用字符串类型。字符串类型的适应性比较强,无论哪种类型的数据,都可以当字符串处理,解析的时候不容易出错。

这样APP和服务器端交互,只使用了两种基本数据类型,大大减少了由于各种数据类型不兼容导致APP端数据解析出错的问题。

(4)APP从服务器读取数据的时候,会遇到数据为空的情况,此时服务器端返回给APP的数据类型应该和数据不为空时的类型一致。

如下所示:

        {
            "code": 800
            "result":{
                  "nikeName":""
            }
        }

nikeName字段的类型是字符串,当其值为空时,应返回空字符串"",而不应返回null。

        {
            "code": 800
            "result":{
                  "products":[]
            }
        }

products字段的类型是数组,当其值为空时,应返回空数组[],而不应返回null或其他类型的数据。

(5)因为服务器端的接口代码可能会发生变化,所以在APP向服务器端发送请求时,最好把接口的版本号也带上,如下所示:

        {
            "version":1.0,
            "params":{
                  "username":"aaa",
                  "password":"123456"
            }
        }

以上JSON数据中,version字段的值表示当前使用接口的版本号为1.0。

如果已经上线的旧APP中使用的接口版本是1.0,在上线后接口更新到1.1版本,而且不兼容1.0版本,用户有可能不更新APP,还是使用旧版本APP。服务器端接收到请求后,发现APP使用的接口版本是1.0,就可以调用旧接口处理APP请求。如果请求中不带版本号,遇到这种状况,就很难处理了。

(6)APP常需要从服务器获取图片,但服务器存储的图片尺寸往往不完全符合APP需要,需要将图片放大或缩小,因为服务器的性能比手机高,所以最好是在服务器端按APP的需求处理图片,然后把处理过的图片发给APP。APP在发送获取图片的请求时,把所需图片的宽度和高度发给服务器,如采用GET方法,可以按以下方式。

http://www.hello.com/getimage/2/width/100/hight/100

服务器收到请求,就可先按APP要求的尺寸处理图片,再发给APP。

当然也可以用POST方法实现,用JSON格式传递参数,示例如下:

        {
            "version":1.0,
            "params":{
                  "imageId":2,
                  "width":100,
                  "hight":100
            }
        }

(7)大多数APP和服务器交互时用HTTP协议,每向服务器发送一个请求都要先建立连接,传输数据后再断开连接。即使服务器端有连接池设计,连接池中容纳的连接个数也是有限制的。

在设计接口时,APP每执行一个动作尽量做到只向服务器发送一次请求,减少APP发送请求的次数,从而减少APP和服务器建立连接和断开连接消耗的时间及资源,提高程序响应速度。

(8)对于向APP返回数组数据的接口应设计支持分页操作,并提供参数以方便APP设置获取元素的起始位置和获取的个数。

例如,数组中有100个元素,APP第一次从第1个元素开始只获取10个元素,第二次从第11个元素开始只获取5个元素。在电商APP中读取商品列表和订单列表可以这样设计。

获取商品列表的JSON数据如下所示:

        {
            "version":1.0,
            "params":{
                  "categoryId":1,
                  "offset":0,
                  "limit":10
            }
        }

其中categoryId表示读取哪一类别的商品列表,offset表示从商品列表中的第一个商品开始读取商品数据,limit表示读取10个商品数据。limit的数值也可以在服务器端设置,此时以服务器端的数值为准,APP传递的数值不起作用。

(9)对于可能会变动的功能逻辑,尽量放在服务器端实现,而不是APP本地实现,这样后续的功能变更时修改服务器端的代码就可以了,不需要用户升级APP。例如,电商APP中商品的默认排序功能,在服务器端可以把商品按价格或销量排序后,再把数据传递给APP, APP端只负责显示就可以了。

(10)APP端在使用服务器接口的时候,常会遇到从服务器传来的JSON数据类型和约定的不一致,导致APP解析出错的问题。APP遇到此类问题时往往会Crash,需要对此问题做特别处理,如下所示:

        try {
              // parseJson为解析从服务器返回的JSON数据的方法
              T model = parseJson(jsonData);
              onSuccess(model);
        }catch (Exception e){
              message = "数据解析出错";
              onError(message);
        }

开发Android APP时,利用try…catch…机制可以有效防止APP Crash,并提示用户出现了什么问题。

在开发阶段,APP应明确提示“数据解析出错”,这样有利于发现和解决问题。上线后,用户在使用APP的时候遇到这类问题,用户不一定理解具体含义,可换种方式提示。

如图3-1所示,明确告知用户服务器端出现问题了,需要联系客服解决。

图3-1

开发阶段通常使用Debug版本,而线上版本是Release版本,利用编译选项可以实现不同版本显示不同的提示信息。

(11)服务器端设计接口的时候,需要考虑到APP重复提交数据的情况。例如,APP和服务器的响应超时时间是10秒,服务器收到APP的请求后,在11秒内完成了处理,但此时APP会提示用户连接超时,用户往往会再次操作,APP就向服务器发起重复请求。