轉(zhuǎn)載自:Retrofit 解析 JSON 數(shù)據(jù) - 簡書
Retro是一個(gè)類型安全的REST客戶端转捕,它可以直接解析JSON數(shù)據(jù)變成JAVA對(duì)象糖儡,甚至支持回調(diào)操作横漏,處理不同的結(jié)果俭尖,本文將以IP地址API數(shù)據(jù)解析為例幽勒,講解如何使用Retrofit
本文適用于2.0以下的版本,目前1.9還是主流焊虏,此文章將漸漸成為歷史
將要使用的網(wǎng)站
文章目錄
JSON數(shù)據(jù)如何轉(zhuǎn)成JAVA
Retrofit同步獲取方法
Retrofit異步回調(diào)方法
JSON數(shù)據(jù)如何轉(zhuǎn)成JAVA
打開了剛剛引用的API查詢網(wǎng)頁淡喜,那個(gè)網(wǎng)頁給了一串JSON數(shù)據(jù)的示例
{"code":0,"data":{"ip":"210.75.225.254","country":"\u4e2d\u56fd","area":"\u534e\u5317",
"region":"\u5317\u4eac\u5e02","city":"\u5317\u4eac\u5e02","county":"","isp":"\u7535\u4fe1",
"country_id":"86","area_id":"100000","region_id":"110000","city_id":"110000",
"county_id":"-1","isp_id":"100017"}}
根據(jù)數(shù)據(jù)的大意,我們可以考慮構(gòu)建一個(gè)JavaIP類
publicclassIP{privateintcode;privateData data;}
這個(gè)類中的Date類是十分麻煩的诵闭,所以我們考慮用工具直接生成
進(jìn)入JSON數(shù)據(jù)在線轉(zhuǎn)換
粘貼JSON代碼進(jìn)去炼团,在右邊的Source Type選擇JSON,Anotation Style選擇Gson涂圆,其他的選擇自行摸索们镜,我的配置如圖
Retrofit
注意服務(wù)器發(fā)送的區(qū)分大小寫與帶下劃線的數(shù)據(jù)可能無法識(shí)別,導(dǎo)致返回為NULL润歉,所以一定要勾選上Gson這個(gè)Anotation模狭,這個(gè)勾選后,你的POJO數(shù)據(jù)踩衩,以及Gson的jar包都可以完全混淆嚼鹉,是一種超級(jí)偷懶的寫法。
點(diǎn)擊下方的Jar驱富,就會(huì)生成源碼包锚赤,你可以直接扔到工程中或者改名為zip手動(dòng)折騰,注意代碼的有些注解(Annotation)可能在AS中無法通過編譯,刪除即可
HTTP GET簡介
請(qǐng)求指定的頁面信息褐鸥,并返回實(shí)體主體线脚,我們可以在http鏈接中加入path,key-value等參數(shù),從而得到具體的對(duì)象叫榕。
我們回到API網(wǎng)站浑侥,它告訴了我們它的API是通過HTTP GET方法獲取的,何為GET晰绎?簡單的說寓落,就是在請(qǐng)求的url中加入key-value參數(shù),發(fā)送給服務(wù)器荞下,這里的key是ip伶选,value是你要查詢的地址,比如202.202.33.33
http://ip.taobao.com/service/getIpInfo.php?ip=202.202.33.33
服務(wù)器根據(jù)你的GET請(qǐng)求尖昏,返回如下的JSON數(shù)據(jù)
{"code":0, "data":{? ? "country":"中國",? ? "country_id":"CN",? ? "area":"西南",? ? "area_id":"500000",? ? "region":"重慶市",? ? "region_id":"500000",? ? "city":"重慶市",? ? "city_id":"500000",? ? "county":"",? ? "county_id":"-1",? ? "isp":"教育網(wǎng)",? ? "isp_id":"100027",? ? "ip":"202.202.33.33"}}
接下來我們?nèi)绾问褂肦etrofit獲取并解析數(shù)據(jù)呢仰税,現(xiàn)在開始正式的使用
Retrofit同步獲取方法
這里的同步獲取方法是指以只獲得JAVA對(duì)象為目標(biāo),而不更新UI線程中的數(shù)據(jù)抽诉,異步是指獲取到數(shù)據(jù)后立刻回調(diào)肖卧,更新UI線程中的界面,我們先講簡單的同步,建議跟著官方Wiki一起看
打開Android Studio掸鹅,新建一個(gè)工程塞帐,添加網(wǎng)絡(luò)權(quán)限,Bulid.gradle添加如下依賴
//自行更新后面的版本號(hào)compile'com.squareup.retrofit:retrofit:1.7.1' compile'com.squareup.okhttp:okhttp-urlconnection:2.0.0' compile'com.squareup.okhttp:okhttp:2.0.0'
修改UI界面巍沙,里面放上一個(gè)EditText,一個(gè)Button,一些Textview葵姥,用于輸入和顯示UI數(shù)據(jù),這步略
寫IP工具類句携,用于封裝獲取獲取IP榔幸,這里的命名IPUtils比較吐槽,各位先忽視
publicclassIPUtils{//eg : http://ip.taobao.com/service/getIpInfo.php?ip=202.202.32.202staticfinalString? ENDPOINT ="http://ip.taobao.com/service";publicinterfaceTaobaoIPService{@GET("/getIpInfo.php")IPgetIp(@Query("ip")String ip);? ? }staticRestAdapter restAdapter =newRestAdapter.Builder()? ? ? ? ? ? .setEndpoint(ENDPOINT)//是否Debug.setLogLevel(RestAdapter.LogLevel.FULL)? ? ? ? ? ? .build();staticpublicTaobaoIPService taobaoIPService = restAdapter.create(TaobaoIPService.class); }
這個(gè)IPUtils首先建立了一個(gè)接口TaobaoIPService矮嫉,接著又創(chuàng)建了一個(gè)restAdapter削咆,最后用restAdapter實(shí)例化接口,我們要獲取IP的時(shí)候蠢笋,直接調(diào)用taobaoIPService中的getIp方法了拨齐,至于為什么我要寫這個(gè)接口?官網(wǎng)上有詳細(xì)的講解
現(xiàn)在只需要在Activity中調(diào)用getIp即可
IP ip = IPUtils.taobaoIPService.getIp("202.202.33.33");
就可以獲得所有的IP數(shù)據(jù)了昨寞,注意這個(gè)任務(wù)是網(wǎng)絡(luò)任務(wù)瞻惋,所以不要忘記給程序加入網(wǎng)絡(luò)權(quán)限,并且讓這個(gè)getIp在非UI線程中使用(比如最簡單的AsyncTask)援岩,之后如何使用IP數(shù)據(jù)就簡單了歼狼,操作第二步的UI組件即可
Retrofit異步回調(diào)方法
Retrofit的異步回調(diào)是指在獲取到數(shù)據(jù)后,立刻進(jìn)行UI的更新享怀,你不需要自己另外寫AsyncTask羽峰,代碼看上去簡潔,如果配合Dagger(一個(gè)Android注解框架添瓷,本文不討論)的使用就更加美了
建立一個(gè)工具類
publicclassIPUtils{//eg : http://ip.taobao.com/service/getIpInfo.php?ip=202.202.32.202staticfinalString? ENDPOINT ="http://ip.taobao.com/service";publicinterfaceTaobaoIPService{@GET("/getIpInfo.php")voidgetIp(@Query("ip")String ip, Callback callback);? ? }staticRestAdapter restAdapter =newRestAdapter.Builder()? ? ? ? ? ? .setEndpoint(ENDPOINT)? ? ? ? ? ? .setLogLevel(RestAdapter.LogLevel.FULL)? ? ? ? ? ? .build();publicstaticTaobaoIPService taobaoIPService =? ? ? ? ? ? restAdapter.create(TaobaoIPService.class); }
從代碼中可以看出TaobaoIPService中的方法getIp沒有返回值了梅屉,反而多了一個(gè)Callback,官方 Wiki是這么說的
On Android, callbacks will be executed on the main thread.
在我們結(jié)束了數(shù)據(jù)獲取后仰坦,無論是否成功履植,都將啟動(dòng)回調(diào),回調(diào)將在主線程(UI線程)執(zhí)行悄晃。
在Activity的使用
submitButton.setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View view){? ? ? ? String query = editText.getText().toString();if(!query.isEmpty()) {//該組件能夠自動(dòng)啟動(dòng)Http線程玫霎,然后在回調(diào)中用main線程修改UI//詳情可以看“SYNCHRONOUS VS. ASYNCHRONOUS VS. OBSERVABLE”IPUtils.taobaoIPService.getIp(query,newCallback() {@Overridepublicvoidsuccess(IP ip, Response response){? ? ? ? ? ? ? ? ? ? textView_code.setText(String.valueOf(ip.getCode()));? ? ? ? ? ? ? ? ? ? textView_ip.setText(ip.getData().getIp());? ? ? ? ? ? ? ? ? ? textView_country.setText(ip.getData().getCountry());? ? ? ? ? ? ? ? ? ? textView_area.setText(ip.getData().getArea());? ? ? ? ? ? ? ? ? ? textView_region.setText(ip.getData().getRegion());? ? ? ? ? ? ? ? ? ? textView_city.setText(ip.getData().getCity());? ? ? ? ? ? ? ? ? ? textView_isp.setText(ip.getData().getIsp());? ? ? ? ? ? ? ? }@Overridepublicvoidfailure(RetrofitError error){? ? ? ? ? ? ? ? ? ? showToast("failure:"+ error.getKind());? ? ? ? ? ? ? ? }? ? ? ? ? ? });? ? ? ? }? ? } });
我們可以看出Callback重寫了2個(gè)方法,一個(gè)是成功妈橄,一個(gè)是失敗庶近。在成功(success)中,我們利用回調(diào)的數(shù)據(jù)眷蚓,直接進(jìn)行UI的更新
這里注意可能出現(xiàn)的內(nèi)存泄露鼻种,如果你是執(zhí)行耗時(shí)任務(wù),當(dāng)你退出activity后沙热,回調(diào)后可能會(huì)出現(xiàn)空指針異常叉钥。
對(duì)錯(cuò)誤以及異常的處理
可以看到罢缸,在剛剛的代碼中,我們僅僅輸出了錯(cuò)誤的種類投队,沒有個(gè)性化的輸出枫疆,作為客戶端我們應(yīng)該如何處理不同的錯(cuò)誤異常呢?我們先列舉用戶出錯(cuò)的情況敷鸦,常見的錯(cuò)誤種類如下
publicenumKind {/** An {@link IOException} occurred while communicating to
the server. */NETWORK,/** An exception was thrown while (de)serializing a body. */CONVERSION,/** A non-200 HTTP status code was received from the server. */HTTP,/**
* An internal error occurred while attempting to execute a
request. It is best practice to
* re-throw this exception so your application crashes.
*/UNEXPECTED? ? }
NETWORK:用戶沒有聯(lián)網(wǎng)息楔,這個(gè)簡單,你可以發(fā)一個(gè)Toast扒披,或者在提交數(shù)據(jù)前檢查網(wǎng)絡(luò)連接
CONVERSION:用戶輸入錯(cuò)誤的數(shù)據(jù)值依,比如1234,導(dǎo)致服務(wù)器返回錯(cuò)誤的數(shù)據(jù),使客戶度無法解析(CONVERSION)
{"code":1,"data":"invaild ip."}
對(duì)于這個(gè)錯(cuò)誤(CONVERSION),我們可以在本地用正則表達(dá)式在提交數(shù)據(jù)前對(duì)數(shù)據(jù)進(jìn)行簡單的驗(yàn)證,當(dāng)然如果服務(wù)器真的傳來了碟案,也沒什么愿险,你同樣只用發(fā)一個(gè)Toast,提示“重新填寫請(qǐng)求”即可
HTTP:處理這個(gè)錯(cuò)誤蟆淀,需要服務(wù)端與客戶端寫好技術(shù)文檔拯啦,或者使用Mock模擬所有的錯(cuò)誤,一般一個(gè)好的服務(wù)器是不會(huì)出現(xiàn)這個(gè)錯(cuò)誤的熔任,真的出現(xiàn)了話褒链,特例處理,比如常見的500,404錯(cuò)誤
UNEXPECTED:暫時(shí)沒見過
最后疑苔,我們寫出的failure應(yīng)該是這樣的甫匹,健壯高效
@Overridepublicvoidfailure(RetrofitError error){switch(error.getKind()) {caseNETWORK:? ? ? showToast("網(wǎng)絡(luò)錯(cuò)誤");break;caseCONVERSION:? ? ? showToast("重新輸入");break;caseHTTP://這里可以用Mockito模擬showToast("錯(cuò)誤代碼:"+ String.valueOf(error.getResponse().getStatus())? ? ? +"錯(cuò)誤原因:"+ error.getResponse().getReason());break;caseUNEXPECTED:? ? ? showToast("未知錯(cuò)誤");//TODO:寫入日志break;? }? showToast("failure:"+ error.getKind());}
在用界面看來,用戶得到了有效的錯(cuò)誤消息惦费,可以與開發(fā)作者溝通反饋兵迅。
PS1:POST操作
我目前有個(gè)開源的項(xiàng)目,圖片上傳用的就是Retrofit2.0的POST上傳薪贫,有興趣去看看吧恍箭。
PS2: Retrofit2.0
我在stackoverflow回答的關(guān)于Retrofit2.0的相關(guān)問題
使用RxJava與Retrofit2.0使用的實(shí)例:Retrofit 2.0 RxJava Sample
JW大神的文章
http://wuxiaolong.me/2016/01/15/retrofit/
后記
本文全完,謝謝觀看瞧省!本博客持續(xù)更新與搬運(yùn)國外大神的文章扯夭,有興趣的話不妨點(diǎn)一個(gè)收藏,另外我還維護(hù)著一個(gè)材料設(shè)計(jì)的專題鞍匾,歡迎收藏交洗。
89145aa6a54e:@火槍輝耀了我也是最近在學(xué)習(xí)這個(gè) 緩存今天看的 你可以看下這篇文章http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0115/3873.html