浅谈Android客户端与服务器的数据交互总结
前言:
本文总结了Android客户端与服务器进行交互时,采用RESTfulAPI+Json的交互方式,针对不同的数据形式以及不同的解析方法,如有不足之处,欢迎指正。
温馨提示:本文适合有一定Android开发经验的人阅读,如有疑问,欢迎留言讨论。
先了解一下相关的基本概念。
1.Android客户端与服务器端通信方式
通信方式主要有HTTP和Socket。
- HTTP通信:即使用HTTP协议进行通信,工作原理是客户端向服务器端发送一条HTTP请求,服务器收到之后先解析客户端的请求,之后会返回数据给客户端,然后客户端再对这些数据进行解析和处理。HTTP连接采取的是“请求—响应”方式,即在请求时建立连接通道,当客户端像服务器端发送请求时,服务器端才能向客户端发送数据。
- Socket通信:Socket又称套接字,在程序内部提供了与外界通信的端口,即端口通信。通过建立socket连接,可为通信双方的数据传输传提供通道。Socket的主要特点有数据丢失率低,使用简单且易于移植。Socket类似于peertopeer的连接,一方可随时向另一方喊话。
小结:HTTP和Socket都是基于TCP协议的。使用两种通信方式的情况是:
1.使用HTTP的情况:双方不需要时刻保持连接在线,比如客户端资源的获取、文件上传等。
2.使用UDP的情况:大部分即时通讯应用(QQ、微信)、聊天室、苹果APNs等。
2.Android客户端与服务器的数据交互方式
主要有三种:
- 数据流
从web服务器响应到手机终端的数据一般打包在一个字节数组中,这个字节数据中包含了不同的数据类型,客端端采取Java数据流和过虑流的方式从字节数组中取出各种类型的数据。
这种交互方式我在学习Android之初用过,实际项目中并没有发现哪家公司在用。这种方式了扩展了Android平台在访问Web服务器进行交互时的解析数据能力,仅供研究学习。
- XML
Webservice的标准数据格式。
- ProtocolBuffers
ProtocolBuffers是一种轻便高效的结构化数据存储格式,支持跨平台。它很适合做数据存储或RPC数据交换格式。比JSON最大的优点就是传输的时候数据体积可以压缩很小,传输效率比较高。本人在这个在项目中没有用到过。
- JSON
JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。毫无疑问,大家最常用。
本文重点会介绍关于json数据格式的常用格式。
json数据格式的采用,根据业务情况,一般是团队中的共识。技术的迭代更新,到后期基本都会考虑多个平台的通用性、可移植性和可读性。比如我们开发团队,有移动端开发(Android、iOS)、前端开发(H5开发)和后台开发(golang开发)。
关于服务器的开发规范,我们先来了解一下。
服务器开发规范我们采用的是RESTful,RESTful是目前最流流行的API设计规范,用于web数据接口的设计。
3.为什么要使⽤RESTfulAPI
- ⾯面向资源(URI),具有解释性;
- 行为(GET/POST/PUT/PATCH/DELETE)与资源(URI)分离,更更加轻量量;
- 数据描述简单,使⽤用JSON、XML、ProtocolBuffers即可全覆盖,主要使用JSON;
它的核心原则是定义用少量方法就能操作的命名资源。资源和方法可视为API的名词和动词。
4.http请求方式
- GET:读取(Read)
- POST:新建(Create)
- PUT:更新(Update),通常是全部更更新
- PATCH:更新(Update),通常是部分更更新
- DELETE:删除(Delete)
项目搭建之始,客户端和服务器一般用Get和Post的方式来交互,随着业务的演进和技术的规范迭代,到后期我们都得按规范来。于是我们采用了上述几种方式来设计服务器接口,相应地,移动端的请求方式也得与之对应。
至此,不在赘述RESTfulAPI的设计规范,可自行百度了解更多。
5.Json交互数据类型实际中的运用
接口的数据一般都采用JSON格式进行传输,不过,需要注意的是,JSON的值只有六种数据类型:
- Number:整数或浮点数
- String:字符串
- Boolean:true或false
- Array:数组包含在方括号[]中
- Object:对象包含在大括号{}中
- Null:空类型
传输的数据类型不能超过这六种数据类型,不能用Date数据类型,不同的解析库解析方式不同,可能会导致异常,如果遇到日期的数据,最好的方式就是使用毫秒数表示日期。
5.1String的数据类型
使用场景:如用户退出登录时,只需要得到返回状态和提示信息即可,不需要返回任何数据。
{ "code":1000, "message":"成功" }
数据解析工具类:
abstractclassBaseStringCallback:BaseCallback(){ overridefunonSuccess(data:String){ valresponseData=JSONObject(data) valcode=responseData.getInt("code") valmessage=responseData.getString("message") if(code==1000){ success(message) }else{ //其他状态 } } abstractfunsuccess(msg:String) }
调用时(伪代码):
LogoutApi.execute(object:BaseStringCallback(){ overridefunsuccess(msg:String){ //处理数据 })
5.2Object数据类型
识别标示为:{}
使用场景:如获取当前用户信息,返回owner实体类,这个类我们可以直接用Gson的工具类转换为owner实体类。
{ "code":1000, "message":"成功", "resp":{ "owner":{ "id":58180, "name":"张三", "idCert":"", "certType":1, "modifier":"jun5753", "updateTime":1567127656436 }, } }
Json数据转换为实体类工具类:
abstractclassBaseObjectCallback(privatevalclazz:Class ):BaseCallback(){ overridefunonSuccess(data:String){ valresponseData=JSONObject(data) valcode=responseData.getInt("code") valmessage=responseData.getString("message") if(code==1000){ valdisposable=Observable.just(responseData) .map{it.getJSONObject("resp").toString()} .map{JsonUtil.parseObject(it,clazz)!!} .applyScheduler() .subscribe( { success(it) }, { //异常时处理 }) }else{ //其他状态时处理 } } abstractfunsuccess(data:T) }
调用时(伪代码):
LaunchApi.getOwerInfo.execute(object:BaseObjectCallback(OwnerEntity::class.java){ overridefunsuccess(data:OwnerEntity){ //处理数据 })
5.3.Array数据类型
识别标示为:[]
使用场景:如获取联系人列表,返回的数据是contact列表,如ArrayList
{ "code":1000, "message":"成功", "resp":{ "contact":[ { "id":5819, "name":"来啦", "phone":"", "address":"哈哈哈", "province":"湖南省", "city":"长沙市", "area":"芙蓉区", "modifier":"jun5753", "isOwner":0, "updateTime":1566461377761 }, { "id":5835, "name":"小六", "phone":"13908258239", "address":"天安门", "province":"北京市", "city":"北京市", "area":"东城区", "modifier":"jun5753", "isOwner":0, "updateTime":1567150580553 } ] } }
Json数据转换为实体类列表工具类:
abstractclassBaseArrayCallback(privatevalclazz:Class ):BaseCallback(){ overridefunonSuccess(data:String){ valresponseData=JSONObject(data) valcode=responseData.getInt("code") valmessage=responseData.getString("message") if(code==1000){ valdisposable=Observable.just(responseData) .map{it.getJSONArray("resp").toString()} .map{JsonUtil.parseArray(it,clazz)!!} .applyScheduler() .subscribe( { success(it) }, { //异常时处理 }) }else{ //其他状态时处理 } } abstractfunsuccess(data:ArrayList ) }
调用时(伪代码):
LaunchApi.getContactList.execute(object:BaseArrayCallback(ContactEntity::class.java){ overridefunsuccess(data:ArrayList ){ //处理数据 })
5.4复杂数据格式
使用场景:如用户的筛选数据需要上传到服务器,每次进入筛选界面时先从服务器获取最新数据信息。
返回的筛选json数据如下所示:
{ "code":1000, "message":"成功", "resp":{ "filterdata":[ 321, 671 ], }
此时的数据不同于上面提到的几种Json数据类型,返回的列表中数据没有key,只有value值。并不是以键值对(key-value)返回的。
解析方法:
声明实体类
classFilterEntity{ /**筛选的数据:解析数组对象为Int型数据ArrayList*/ varfilterdata=ArrayList () }
调用方法(伪代码):
HomeApi.getFilterData() .execute(object:CJJObjectCallback(FilterEntity::class.java){ overridefunsuccess(data:FilterEntity){ //处理数据 } })
当用户选择筛选数据后,需要上传到服务器,伪代码如下:
//上传json示例为:[0,1,2,3,4] valfilterList=ArrayList() //添加int数据 filterList.add(321) filterList.add(671) valjsonData=JsonUtil.toJson(filterList) //上传服务器 HttpTool.put(FILTER_DATA).param("data",jsonData) //Gson转换方法 funtoJson(object:Any):String{ varstr="" try{ str=gson.toJson(object) }catch(e:Exception){ } returnstr }
更多地,如果想要上传多种数据类型,如key-value形式的数据到服务器,伪代码如下:
//json数据示例:{"group":[22,23,24],"brand":[1,2,3,4]} //客户分组筛选 valcustomerGroupJsonArray=ArrayList() valmap=ArrayMap >() customerGroupList.forEach{ customerGroupJsonArray.add(it.id) } map["group"]=customerGroupJsonArray //品牌筛选 valvehicleBrandJsonArray=ArrayList () vehicleBrandList.forEach{ vehicleBrandJsonArray.add(it.brandId) } map["brand"]=vehicleBrandJsonArray //将map类型的数据转换为json数据 valjsonData=JsonUtil.toJson(map) //上传服务器 HttpTool.put(FILTER_DATA).param("data",jsonData)
6.总结
本文总结了Android与服务器的交互方式和数据类型,并总结了在实际项目的简单运用,数据格式的运用场景远不止上面提到的几种场景,后期会持续完善,如有不足之处,欢迎指出。
参考资料:
1.Android手机访问服务器的一种数据交互方法
2.App架构设计经验谈:接口的设计
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。