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

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

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

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

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

Retrofit

IP地址查詢站

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

文章目錄

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

Retrofit同步獲取方法

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

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

打開了剛剛引用的API查詢網(wǎng)頁,那個網(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)建一個JavaIP類

publicclassIP{privateintcode;privateData data;}

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

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

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

Retrofit

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

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

HTTP GET簡介

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

我們回到API網(wǎng)站,它告訴了我們它的API是通過HTTP GET方法獲取的,何為GET?簡單的說,就是在請求的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請求,返回如下的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對象為目標,而不更新UI線程中的數(shù)據(jù),異步是指獲取到數(shù)據(jù)后立刻回調(diào),更新UI線程中的界面,我們先講簡單的同步,建議跟著官方Wiki一起看

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

//自行更新后面的版本號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界面,里面放上一個EditText,一個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); }

這個IPUtils首先建立了一個接口TaobaoIPService,接著又創(chuàng)建了一個restAdapter,最后用restAdapter實例化接口,我們要獲取IP的時候,直接調(diào)用taobaoIPService中的getIp方法了,至于為什么我要寫這個接口?官網(wǎng)上有詳細的講解

現(xiàn)在只需要在Activity中調(diào)用getIp即可

IP ip = IPUtils.taobaoIPService.getIp("202.202.33.33");

就可以獲得所有的IP數(shù)據(jù)了,注意這個任務(wù)是網(wǎng)絡(luò)任務(wù),所以不要忘記給程序加入網(wǎng)絡(luò)權(quán)限,并且讓這個getIp在非UI線程中使用(比如最簡單的AsyncTask),之后如何使用IP數(shù)據(jù)就簡單了,操作第二步的UI組件即可

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

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

建立一個工具類

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沒有返回值了,反而多了一個Callback,官方 Wiki是這么說的

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

在我們結(jié)束了數(shù)據(jù)獲取后,無論是否成功,都將啟動回調(diào),回調(diào)將在主線程(UI線程)執(zhí)行。

在Activity的使用

submitButton.setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View view){? ? ? ? String query = editText.getText().toString();if(!query.isEmpty()) {//該組件能夠自動啟動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個方法,一個是成功,一個是失敗。在成功(success)中,我們利用回調(diào)的數(shù)據(jù),直接進行UI的更新

這里注意可能出現(xiàn)的內(nèi)存泄露,如果你是執(zhí)行耗時任務(wù),當你退出activity后,回調(diào)后可能會出現(xiàn)空指針異常。

對錯誤以及異常的處理

可以看到,在剛剛的代碼中,我們僅僅輸出了錯誤的種類,沒有個性化的輸出,作為客戶端我們應(yīng)該如何處理不同的錯誤異常呢?我們先列舉用戶出錯的情況,常見的錯誤種類如下

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),這個簡單,你可以發(fā)一個Toast,或者在提交數(shù)據(jù)前檢查網(wǎng)絡(luò)連接

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

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

對于這個錯誤(CONVERSION),我們可以在本地用正則表達式在提交數(shù)據(jù)前對數(shù)據(jù)進行簡單的驗證,當然如果服務(wù)器真的傳來了,也沒什么,你同樣只用發(fā)一個Toast,提示“重新填寫請求”即可

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

UNEXPECTED:暫時沒見過

最后,我們寫出的failure應(yīng)該是這樣的,健壯高效

@Overridepublicvoidfailure(RetrofitError error){switch(error.getKind()) {caseNETWORK:? ? ? showToast("網(wǎng)絡(luò)錯誤");break;caseCONVERSION:? ? ? showToast("重新輸入");break;caseHTTP://這里可以用Mockito模擬showToast("錯誤代碼:"+ String.valueOf(error.getResponse().getStatus())? ? ? +"錯誤原因:"+ error.getResponse().getReason());break;caseUNEXPECTED:? ? ? showToast("未知錯誤");//TODO:寫入日志break;? }? showToast("failure:"+ error.getKind());}

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

PS1:POST操作

我目前有個開源的項目,圖片上傳用的就是Retrofit2.0的POST上傳,有興趣去看看吧。

Fork me on gitHub

PS2: Retrofit2.0

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

使用RxJava與Retrofit2.0使用的實例:Retrofit 2.0 RxJava Sample

JW大神的文章

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

后記

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

推薦拓展閱讀

舉報文章

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容