Retrofit 解析 JSON 數(shù)據(jù)

轉(zhuǎn)載自:Retrofit 解析 JSON 數(shù)據(jù) - 簡(jiǎn)書(shū)

Retro是一個(gè)類(lèi)型安全的REST客戶(hù)端,它可以直接解析JSON數(shù)據(jù)變成JAVA對(duì)象,甚至支持回調(diào)操作,處理不同的結(jié)果,本文將以IP地址API數(shù)據(jù)解析為例,講解如何使用Retrofit

本文適用于2.0以下的版本,目前1.9還是主流,此文章將漸漸成為歷史

將要使用的網(wǎng)站

Retrofit

IP地址查詢(xún)站

JSON數(shù)據(jù)在線轉(zhuǎn)換

文章目錄

JSON數(shù)據(jù)如何轉(zhuǎn)成JAVA

Retrofit同步獲取方法

Retrofit異步回調(diào)方法

JSON數(shù)據(jù)如何轉(zhuǎn)成JAVA

打開(kāi)了剛剛引用的API查詢(xún)網(wǎng)頁(yè),那個(gè)網(wǎng)頁(yè)給了一串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類(lèi)

publicclassIP{privateintcode;privateData data;}

這個(gè)類(lèi)中的Date類(lèi)是十分麻煩的,所以我們考慮用工具直接生成

進(jìn)入JSON數(shù)據(jù)在線轉(zhuǎn)換

粘貼JSON代碼進(jìn)去,在右邊的Source Type選擇JSON,Anotation Style選擇Gson,其他的選擇自行摸索,我的配置如圖

Retrofit

注意服務(wù)器發(fā)送的區(qū)分大小寫(xiě)與帶下劃線的數(shù)據(jù)可能無(wú)法識(shí)別,導(dǎo)致返回為NULL,所以一定要勾選上Gson這個(gè)Anotation,這個(gè)勾選后,你的POJO數(shù)據(jù),以及Gson的jar包都可以完全混淆,是一種超級(jí)偷懶的寫(xiě)法。

點(diǎn)擊下方的Jar,就會(huì)生成源碼包,你可以直接扔到工程中或者改名為zip手動(dòng)折騰,注意代碼的有些注解(Annotation)可能在AS中無(wú)法通過(guò)編譯,刪除即可

HTTP GET簡(jiǎn)介

請(qǐng)求指定的頁(yè)面信息,并返回實(shí)體主體,我們可以在http鏈接中加入path,key-value等參數(shù),從而得到具體的對(duì)象。

我們回到API網(wǎng)站,它告訴了我們它的API是通過(guò)HTTP GET方法獲取的,何為GET?簡(jiǎn)單的說(shuō),就是在請(qǐng)求的url中加入key-value參數(shù),發(fā)送給服務(wù)器,這里的key是ip,value是你要查詢(xún)的地址,比如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":"中國(guó)",? ? "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"}}

接下來(lái)我們?nèi)绾问褂肦etrofit獲取并解析數(shù)據(jù)呢,現(xiàn)在開(kāi)始正式的使用

Retrofit同步獲取方法

這里的同步獲取方法是指以只獲得JAVA對(duì)象為目標(biāo),而不更新UI線程中的數(shù)據(jù),異步是指獲取到數(shù)據(jù)后立刻回調(diào),更新UI線程中的界面,我們先講簡(jiǎn)單的同步,建議跟著官方Wiki一起看

打開(kāi)Android Studio,新建一個(gè)工程,添加網(wǎng)絡(luò)權(quán)限,Bulid.gradle添加如下依賴(lài)

//自行更新后面的版本號(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ù),這步略

寫(xiě)IP工具類(lèi),用于封裝獲取獲取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方法了,至于為什么我要寫(xiě)這個(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線程中使用(比如最簡(jiǎn)單的AsyncTask),之后如何使用IP數(shù)據(jù)就簡(jiǎn)單了,操作第二步的UI組件即可

Retrofit異步回調(diào)方法

Retrofit的異步回調(diào)是指在獲取到數(shù)據(jù)后,立刻進(jìn)行UI的更新,你不需要自己另外寫(xiě)AsyncTask,代碼看上去簡(jiǎn)潔,如果配合Dagger(一個(gè)Android注解框架,本文不討論)的使用就更加美了

建立一個(gè)工具類(lèi)

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沒(méi)有返回值了,反而多了一個(gè)Callback,官方 Wiki是這么說(shuō)的

On Android, callbacks will be executed on the main thread.

在我們結(jié)束了數(shù)據(jù)獲取后,無(wú)論是否成功,都將啟動(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重寫(xiě)了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ò)誤的種類(lèi),沒(méi)有個(gè)性化的輸出,作為客戶(hù)端我們應(yīng)該如何處理不同的錯(cuò)誤異常呢?我們先列舉用戶(hù)出錯(cuò)的情況,常見(jiàn)的錯(cuò)誤種類(lèi)如下

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:用戶(hù)沒(méi)有聯(lián)網(wǎng),這個(gè)簡(jiǎn)單,你可以發(fā)一個(gè)Toast,或者在提交數(shù)據(jù)前檢查網(wǎng)絡(luò)連接

CONVERSION:用戶(hù)輸入錯(cuò)誤的數(shù)據(jù),比如1234,導(dǎo)致服務(wù)器返回錯(cuò)誤的數(shù)據(jù),使客戶(hù)度無(wú)法解析(CONVERSION)

{"code":1,"data":"invaild ip."}

對(duì)于這個(gè)錯(cuò)誤(CONVERSION),我們可以在本地用正則表達(dá)式在提交數(shù)據(jù)前對(duì)數(shù)據(jù)進(jìn)行簡(jiǎn)單的驗(yàn)證,當(dāng)然如果服務(wù)器真的傳來(lái)了,也沒(méi)什么,你同樣只用發(fā)一個(gè)Toast,提示“重新填寫(xiě)請(qǐng)求”即可

HTTP:處理這個(gè)錯(cuò)誤,需要服務(wù)端與客戶(hù)端寫(xiě)好技術(shù)文檔,或者使用Mock模擬所有的錯(cuò)誤,一般一個(gè)好的服務(wù)器是不會(huì)出現(xiàn)這個(gè)錯(cuò)誤的,真的出現(xiàn)了話,特例處理,比如常見(jiàn)的500,404錯(cuò)誤

UNEXPECTED:暫時(shí)沒(méi)見(jiàn)過(guò)

最后,我們寫(xiě)出的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:寫(xiě)入日志break;? }? showToast("failure:"+ error.getKind());}

在用界面看來(lái),用戶(hù)得到了有效的錯(cuò)誤消息,可以與開(kāi)發(fā)作者溝通反饋。

PS1:POST操作

我目前有個(gè)開(kāi)源的項(xiàng)目,圖片上傳用的就是Retrofit2.0的POST上傳,有興趣去看看吧。

Fork me on gitHub

PS2: Retrofit2.0

我在stackoverflow回答的關(guān)于Retrofit2.0的相關(guān)問(wèn)題

使用RxJava與Retrofit2.0使用的實(shí)例:Retrofit 2.0 RxJava Sample

JW大神的文章

http://wuxiaolong.me/2016/01/15/retrofit/

后記

本文全完,謝謝觀看!本博客持續(xù)更新與搬運(yùn)國(guó)外大神的文章,有興趣的話不妨點(diǎn)一個(gè)收藏,另外我還維護(hù)著一個(gè)材料設(shè)計(jì)的專(zhuān)題,歡迎收藏。

推薦拓展閱讀

舉報(bào)文章

著作權(quán)歸作者所有

89145aa6a54e@火槍輝耀了我也是最近在學(xué)習(xí)這個(gè) 緩存今天看的 你可以看下這篇文章http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0115/3873.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,057評(píng)論 25 708
  • ¥開(kāi)啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開(kāi)一個(gè)線程,因...
    小菜c閱讀 6,509評(píng)論 0 17
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,868評(píng)論 18 139
  • 文/一念之間 此故事是“常走夜路必撞鬼”的結(jié)局篇 大紅和李發(fā)急匆匆地穿過(guò)荒墳地,此時(shí)已月上中天。 今晚的月亮好像半...
    一nian之間閱讀 441評(píng)論 2 1
  • 前情回顧: 那個(gè)嚷嚷著要睡我的男人。(一) 那個(gè)嚷嚷著要睡我的男人(二) 就是馬小武不說(shuō)讓我走,我也要走。 這個(gè)地...
    月德閱讀 408評(píng)論 0 0