淺談Android客戶端與服務(wù)器的數(shù)據(jù)交互總結(jié)
前言:
本文總結(jié)了Android客戶端與服務(wù)器進(jìn)行交互時(shí),采用RESTful API +Json的交互方式,針對(duì)不同的數(shù)據(jù)形式以及不同的解析方法,如有不足之處,歡迎指正。
溫馨提示:本文適合有一定Android開發(fā)經(jīng)驗(yàn)的人閱讀,如有疑問,歡迎留言討論。
先了解一下相關(guān)的基本概念。
1. Android客戶端與服務(wù)器端通信方式
通信方式主要有HTTP和Socket。
- HTTP通信:即使用HTTP協(xié)議進(jìn)行通信,工作原理是客戶端向服務(wù)器端發(fā)送一條HTTP請(qǐng)求,服務(wù)器收到之后先解析客戶端的請(qǐng)求,之后會(huì)返回?cái)?shù)據(jù)給客戶端,然后客戶端再對(duì)這些數(shù)據(jù)進(jìn)行解析和處理。HTTP連接采取的是“請(qǐng)求—響應(yīng)”方式,即在請(qǐng)求時(shí)建立連接通道,當(dāng)客戶端像服務(wù)器端發(fā)送請(qǐng)求時(shí),服務(wù)器端才能向客戶端發(fā)送數(shù)據(jù)。
- Socket通信:Socket又稱套接字,在程序內(nèi)部提供了與外界通信的端口,即端口通信。通過建立socket連接,可為通信雙方的數(shù)據(jù)傳輸傳提供通道。Socket的主要特點(diǎn)有數(shù)據(jù)丟失率低,使用簡單且易于移植。Socket類似于peer to peer的連接,一方可隨時(shí)向另一方喊話。
小結(jié):HTTP和Socket都是基于TCP協(xié)議的。使用兩種通信方式的情況是:
1.使用HTTP的情況:雙方不需要時(shí)刻保持連接在線,比如客戶端資源的獲取、文件上傳等。
2.使用UDP的情況:大部分即時(shí)通訊應(yīng)用(QQ、微信)、聊天室、蘋果APNs等。
2. Android客戶端與服務(wù)器的數(shù)據(jù)交互方式
主要有三種:
- 數(shù)據(jù)流
從web服務(wù)器響應(yīng)到手機(jī)終端的數(shù)據(jù) 一般打包在一個(gè)字節(jié)數(shù)組中,這個(gè)字節(jié)數(shù)據(jù)中包含了不同的數(shù)據(jù)類型,客端端采取Java數(shù)據(jù)流和過慮流的方式從字節(jié)數(shù)組中取出各種類型的數(shù)據(jù)。
這種交互方式我在學(xué)習(xí)Android之初用過,實(shí)際項(xiàng)目中并沒有發(fā)現(xiàn)哪家公司在用。這種方式了擴(kuò)展了Android平臺(tái)在訪問Web服務(wù)器進(jìn)行交互時(shí)的解析數(shù)據(jù)能力,僅供研究學(xué)習(xí)。
- XML
Webservice的標(biāo)準(zhǔn)數(shù)據(jù)格式。
- Protocol Buffers
Protocol Buffers 是一種輕便高效的結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)格式,支持跨平臺(tái)。它很適合做數(shù)據(jù)存儲(chǔ)或 RPC 數(shù)據(jù)交換格式。比 JSON 最大的優(yōu)點(diǎn)就是傳輸?shù)臅r(shí)候數(shù)據(jù)體積可以壓縮很小,傳輸效率比較高。本人在這個(gè)在項(xiàng)目中沒有用到過。
- JSON
JSON(JavaScript Object Notation) 是一種輕量級(jí)的數(shù)據(jù)交換格式。 易于人閱讀和編寫。同時(shí)也易于機(jī)器解析和生成。毫無疑問,大家最常用。
本文重點(diǎn)會(huì)介紹關(guān)于json數(shù)據(jù)格式 的常用格式。
json數(shù)據(jù)格式 的采用,根據(jù)業(yè)務(wù)情況,一般是團(tuán)隊(duì)中的共識(shí)。技術(shù)的迭代更新,到后期基本都會(huì)考慮多個(gè)平臺(tái)的通用性、可移植性和可讀性。比如 我們開發(fā)團(tuán)隊(duì),有移動(dòng)端開發(fā)(Android、iOS)、前端開發(fā)(H5開發(fā))和后臺(tái)開發(fā)(golang開發(fā))。
關(guān)于服務(wù)器的開發(fā)規(guī)范,我們先來了解一下。
服務(wù)器開發(fā)規(guī)范 我們采用的是 RESTful,RESTful是目前最流流行的 API設(shè)計(jì)規(guī)范,用于web數(shù)據(jù)接口的設(shè)計(jì)。
3. 為什么要使⽤RESTful API
- ⾯面向資源(URI),具有解釋性;
- 行為(GET / POST / PUT / PATCH / DELETE)與資源(URI)分離,更更加輕量量;
- 數(shù)據(jù)描述簡單,使⽤用JSON、XML、Protocol Buffers即可全覆蓋,主要使用JSON;
它的核心原則是定義用少量方法就能操作的命名資源。資源和方法可視為API的名詞和動(dòng)詞。
4. http請(qǐng)求方式
- GET :讀?。≧ead)
- POST :新建(Create)
- PUT :更新(Update),通常是全部更更新
- PATCH :更新(Update),通常是部分更更新
- DELETE :刪除(Delete)
項(xiàng)目搭建之始,客戶端和服務(wù)器一般用 Get 和Post的方式來交互,隨著業(yè)務(wù)的演進(jìn)和技術(shù)的規(guī)范迭代,到后期我們都得按規(guī)范來。于是 我們采用了上述幾種方式來設(shè)計(jì)服務(wù)器接口,相應(yīng)地,移動(dòng)端的請(qǐng)求方式也得與之對(duì)應(yīng)。
至此,不在贅述RESTful API的設(shè)計(jì)規(guī)范,可自行百度了解更多。
5. Json交互數(shù)據(jù)類型實(shí)際中的運(yùn)用
接口的數(shù)據(jù)一般都采用JSON格式進(jìn)行傳輸,不過,需要注意的是,JSON的值只有六種數(shù)據(jù)類型:
- Number:整數(shù)或浮點(diǎn)數(shù)
- String:字符串
- Boolean:true 或 false
- Array:數(shù)組包含在方括號(hào)[]中
- Object:對(duì)象包含在大括號(hào){}中
- Null:空類型
傳輸?shù)臄?shù)據(jù)類型不能超過這六種數(shù)據(jù)類型,不能用Date數(shù)據(jù)類型,不同的解析庫解析方式不同,可能會(huì)導(dǎo)致異常,如果遇到日期的數(shù)據(jù),最好的方式就是使用毫秒數(shù)表示日期。
5.1 String的數(shù)據(jù)類型
使用場景:如用戶退出登錄時(shí),只需要得到返回狀態(tài)和提示信息即可,不需要返回任何數(shù)據(jù)。
{
"code": 1000,
"message": "成功"
}
數(shù)據(jù)解析工具類:
abstract class BaseStringCallback: BaseCallback() {
override fun onSuccess(data: String) {
val responseData = JSONObject(data)
val code = responseData.getInt("code")
val message = responseData.getString("message")
if (code == 1000) {
success(message)
} else {
//其他狀態(tài)
}
}
abstract fun success(msg: String)
}
調(diào)用時(shí)(偽代碼):
LogoutApi.execute(object : BaseStringCallback() {
override fun success(msg: String) {
//處理數(shù)據(jù)
})
5.2 Object數(shù)據(jù)類型
識(shí)別標(biāo)示為:{}
使用場景:如獲取當(dāng)前用戶信息,返回owner實(shí)體類,這個(gè)類我們可以直接用Gson的工具類轉(zhuǎn)換為owner實(shí)體類。
{
"code": 1000,
"message": "成功",
"resp": {
"owner": {
"id": 58180,
"name": "張三",
"idCert": "",
"certType": 1,
"modifier": "jun5753",
"updateTime": 1567127656436
},
}
}
Json數(shù)據(jù)轉(zhuǎn)換為實(shí)體類工具類:
abstract class BaseObjectCallback<T>(private val clazz: Class<T>) : BaseCallback() {
override fun onSuccess(data: String) {
val responseData = JSONObject(data)
val code = responseData.getInt("code")
val message = responseData.getString("message")
if (code == 1000) {
val disposable = Observable.just(responseData)
.map { it.getJSONObject("resp").toString() }
.map { JsonUtil.parseObject(it, clazz)!! }
.applyScheduler()
.subscribe(
{
success(it)
},
{
//異常時(shí)處理
})
} else {
//其他狀態(tài)時(shí)處理
}
}
abstract fun success(data: T)
}
調(diào)用時(shí)(偽代碼):
LaunchApi.getOwerInfo.execute(object : BaseObjectCallback<OwnerEntity>(OwnerEntity::class.java) {
override fun success(data: OwnerEntity) {
//處理數(shù)據(jù)
})
5.3. Array數(shù)據(jù)類型
識(shí)別標(biāo)示為:[]
使用場景:如獲取聯(lián)系人列表,返回的數(shù)據(jù)是contact列表,如 ArrayList<contact >。
{
"code": 1000,
"message": "成功",
"resp": {
"contact": [
{
"id": 5819,
"name": "來啦",
"phone": "",
"address": "哈哈哈",
"province": "湖南省",
"city": "長沙市",
"area": "芙蓉區(qū)",
"modifier": "jun5753",
"isOwner": 0,
"updateTime": 1566461377761
},
{
"id": 5835,
"name": "小六",
"phone": "13908258239",
"address": "天安門",
"province": "北京市",
"city": "北京市",
"area": "東城區(qū)",
"modifier": "jun5753",
"isOwner": 0,
"updateTime": 1567150580553
}
]
}
}
Json數(shù)據(jù)轉(zhuǎn)換為實(shí)體類列表工具類:
abstract class BaseArrayCallback<T>(private val clazz: Class<T>) :BaseCallback() {
override fun onSuccess(data: String) {
val responseData = JSONObject(data)
val code = responseData.getInt("code")
val message = responseData.getString("message")
if (code == 1000) {
val disposable = Observable.just(responseData)
.map { it.getJSONArray("resp").toString() }
.map { JsonUtil.parseArray(it, clazz)!! }
.applyScheduler()
.subscribe(
{
success(it)
},
{
//異常時(shí)處理
})
} else {
//其他狀態(tài)時(shí)處理
}
}
abstract fun success(data: ArrayList<T>)
}
調(diào)用時(shí)(偽代碼):
LaunchApi.getContactList.execute(object : BaseArrayCallback<ContactEntity>(ContactEntity::class.java) {
override fun success(data: ArrayList<ContactEntity>) {
//處理數(shù)據(jù)
})
5.4 復(fù)雜數(shù)據(jù)格式
使用場景:如用戶的篩選數(shù)據(jù)需要上傳到服務(wù)器,每次進(jìn)入篩選界面時(shí)先從服務(wù)器獲取最新數(shù)據(jù)信息。
返回的篩選json數(shù)據(jù)如下所示:
{
"code": 1000,
"message": "成功",
"resp": {
"filterdata": [
321,
671
],
}
此時(shí)的數(shù)據(jù) 不同于上面提到的幾種Json數(shù)據(jù)類型,返回的列表中 數(shù)據(jù)沒有key,只有value值 。并不是以鍵值對(duì)(key-value)返回的。
解析方法:
聲明實(shí)體類
class FilterEntity {
/** 篩選的數(shù)據(jù):解析數(shù)組對(duì)象 為Int 型數(shù)據(jù) ArrayList<Int> */
var filterdata = ArrayList<Int>()
}
調(diào)用方法(偽代碼):
HomeApi.getFilterData()
.execute(object : CJJObjectCallback<FilterEntity>(FilterEntity::class.java) {
override fun success(data: FilterEntity) {
//處理數(shù)據(jù)
}
})
當(dāng)用戶選擇篩選數(shù)據(jù)后,需要上傳到服務(wù)器,偽代碼如下:
//上傳json示例為:[0,1,2,3,4]
val filterList = ArrayList<Int>()
//添加int數(shù)據(jù)
filterList.add(321)
filterList.add(671)
val jsonData = JsonUtil.toJson(filterList)
//上傳服務(wù)器
HttpTool.put(FILTER_DATA).param("data", jsonData)
//Gson轉(zhuǎn)換方法
fun toJson(object: Any): String {
var str = ""
try {
str = gson.toJson(object)
} catch (e: Exception) {
}
return str
}
更多地,如果想要 上傳多種數(shù)據(jù)類型,如key-value形式的數(shù)據(jù)到服務(wù)器,偽代碼如下:
//json數(shù)據(jù)示例:{"group":[22,23,24],"brand":[1,2,3,4]}
// 客戶分組篩選
val customerGroupJsonArray = ArrayList<Int>()
val map = ArrayMap<String, ArrayList<Int>>()
customerGroupList.forEach {
customerGroupJsonArray.add(it.id)
}
map["group"] = customerGroupJsonArray
// 品牌篩選
val vehicleBrandJsonArray = ArrayList<Int>()
vehicleBrandList.forEach {
vehicleBrandJsonArray.add(it.brandId)
}
map["brand"] = vehicleBrandJsonArray
//將map類型的數(shù)據(jù)轉(zhuǎn)換為json數(shù)據(jù)
val jsonData = JsonUtil.toJson(map)
//上傳服務(wù)器
HttpTool.put(FILTER_DATA).param("data", jsonData)
6.總結(jié)
本文總結(jié)了Android與服務(wù)器的交互方式和數(shù)據(jù)類型,并總結(jié)了在實(shí)際項(xiàng)目的簡單運(yùn)用,數(shù)據(jù)格式的運(yùn)用場景遠(yuǎn)不止上面提到的幾種場景,后期會(huì)持續(xù)完善,如有不足之處,歡迎指出。
參考資料:
1.Android手機(jī)訪問服務(wù)器的一種數(shù)據(jù)交互方法
2.App架構(gòu)設(shè)計(jì)經(jīng)驗(yàn)談:接口的設(shè)計(jì)
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android使用MediaRecorder類實(shí)現(xiàn)視頻和音頻錄制功能
Android提供了MediaRecorder這一個(gè)類來實(shí)現(xiàn)視頻和音頻的錄制功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2018-07-07
Android音視頻開發(fā)之MediaExtactor使用教程
MediaExtactor在Android音視頻開發(fā)中負(fù)責(zé)提取音視頻信息和數(shù)據(jù)流的功能,可以通過該類實(shí)現(xiàn)從多媒體文件中剝離得到音頻和視頻的能力。本文將詳細(xì)為大家介紹一下它的使用,感興趣的可以了解一下2022-04-04
flutter自定義InheritedProvider實(shí)現(xiàn)狀態(tài)管理詳解
這篇文章主要為大家介紹了flutter自定義InheritedProvider實(shí)現(xiàn)狀態(tài)管理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
Android自定義滾動(dòng)選擇器實(shí)例代碼
本篇文章主要介紹了Android自定義滾動(dòng)選擇器實(shí)例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01
基于Android在布局中動(dòng)態(tài)添加view的兩種方法(總結(jié))
下面小編就為大家?guī)硪黄贏ndroid在布局中動(dòng)態(tài)添加view的兩種方法(總結(jié))。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10
android開發(fā) eclipse alt+”/”自動(dòng)提示失效的解決方法
最近在學(xué)習(xí)android開發(fā)布局這塊。第一次學(xué)習(xí),很多代碼不熟悉。所以自動(dòng)提示對(duì)我來說很重要。但悲催的就是這個(gè)自動(dòng)提示失效。今天在網(wǎng)上搜索了一下解決辦法,主要有一下幾種方法2014-05-05
Android 手機(jī)瀏覽器調(diào)試使用Chrome進(jìn)行調(diào)試實(shí)例詳解
這篇文章主要介紹了Android 手機(jī)瀏覽器調(diào)試使用Chrome進(jìn)行調(diào)試實(shí)例詳解的相關(guān)資料,這里提供了實(shí)例,需要的朋友可以參考下2016-12-12
kotlin Standard中的內(nèi)聯(lián)函數(shù)示例詳解
這篇文章主要給大家介紹了關(guān)于kotlin Standard中內(nèi)聯(lián)函數(shù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用kotlin具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Android同步屏障機(jī)制sync barrier實(shí)例應(yīng)用詳解
這篇文章主要介紹了Android同步屏障機(jī)制sync barrier實(shí)例應(yīng)用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-02-02

