Retrofit 解析 JSON 数据

转载自:Retrofit 解析 JSON 数据 - 简书

Retro是一个类型安全的REST客户端,它可以直接解析JSON数据变成JAVA对象,甚至支持回调操作,处理不同的结果,本文将以IP地址API数据解析为例,讲解如何使用Retrofit

本文适用于2.0以下的版本,目前1.9还是主流,此文章将渐渐成为历史

将要使用的网站

Retrofit

IP地址查询站

JSON数据在线转换

文章目录

JSON数据如何转成JAVA

Retrofit同步获取方法

Retrofit异步回调方法

JSON数据如何转成JAVA

打开了刚刚引用的API查询网页,那个网页给了一串JSON数据的示例

{"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"}}

根据数据的大意,我们可以考虑构建一个JavaIP类

publicclassIP{privateintcode;privateData data;}

这个类中的Date类是十分麻烦的,所以我们考虑用工具直接生成

进入JSON数据在线转换

粘贴JSON代码进去,在右边的Source Type选择JSON,Anotation Style选择Gson,其他的选择自行摸索,我的配置如图

Retrofit

注意服务器发送的区分大小写与带下划线的数据可能无法识别,导致返回为NULL,所以一定要勾选上Gson这个Anotation,这个勾选后,你的POJO数据,以及Gson的jar包都可以完全混淆,是一种超级偷懒的写法。

点击下方的Jar,就会生成源码包,你可以直接扔到工程中或者改名为zip手动折腾,注意代码的有些注解(Annotation)可能在AS中无法通过编译,删除即可

HTTP GET简介

请求指定的页面信息,并返回实体主体,我们可以在http链接中加入path,key-value等参数,从而得到具体的对象。

我们回到API网站,它告诉了我们它的API是通过HTTP GET方法获取的,何为GET?简单的说,就是在请求的url中加入key-value参数,发送给服务器,这里的key是ip,value是你要查询的地址,比如202.202.33.33

http://ip.taobao.com/service/getIpInfo.php?ip=202.202.33.33

服务器根据你的GET请求,返回如下的JSON数据

{"code":0, "data":{    "country":"中国",    "country_id":"CN",    "area":"西南",    "area_id":"500000",    "region":"重庆市",    "region_id":"500000",    "city":"重庆市",    "city_id":"500000",    "county":"",    "county_id":"-1",    "isp":"教育网",    "isp_id":"100027",    "ip":"202.202.33.33"}}

接下来我们如何使用Retrofit获取并解析数据呢,现在开始正式的使用

Retrofit同步获取方法

这里的同步获取方法是指以只获得JAVA对象为目标,而不更新UI线程中的数据,异步是指获取到数据后立刻回调,更新UI线程中的界面,我们先讲简单的同步,建议跟着官方Wiki一起看

打开Android Studio,新建一个工程,添加网络权限,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数据,这步略

写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,接着又创建了一个restAdapter,最后用restAdapter实例化接口,我们要获取IP的时候,直接调用taobaoIPService中的getIp方法了,至于为什么我要写这个接口?官网上有详细的讲解

现在只需要在Activity中调用getIp即可

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

就可以获得所有的IP数据了,注意这个任务是网络任务,所以不要忘记给程序加入网络权限,并且让这个getIp在非UI线程中使用(比如最简单的AsyncTask),之后如何使用IP数据就简单了,操作第二步的UI组件即可

Retrofit异步回调方法

Retrofit的异步回调是指在获取到数据后,立刻进行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.

在我们结束了数据获取后,无论是否成功,都将启动回调,回调将在主线程(UI线程)执行。

在Activity的使用

submitButton.setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View view){        String query = editText.getText().toString();if(!query.isEmpty()) {//该组件能够自动启动Http线程,然后在回调中用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)中,我们利用回调的数据,直接进行UI的更新

这里注意可能出现的内存泄露,如果你是执行耗时任务,当你退出activity后,回调后可能会出现空指针异常。

对错误以及异常的处理

可以看到,在刚刚的代码中,我们仅仅输出了错误的种类,没有个性化的输出,作为客户端我们应该如何处理不同的错误异常呢?我们先列举用户出错的情况,常见的错误种类如下

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:用户没有联网,这个简单,你可以发一个Toast,或者在提交数据前检查网络连接

CONVERSION:用户输入错误的数据,比如1234,导致服务器返回错误的数据,使客户度无法解析(CONVERSION)

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

对于这个错误(CONVERSION),我们可以在本地用正则表达式在提交数据前对数据进行简单的验证,当然如果服务器真的传来了,也没什么,你同样只用发一个Toast,提示“重新填写请求”即可

HTTP:处理这个错误,需要服务端与客户端写好技术文档,或者使用Mock模拟所有的错误,一般一个好的服务器是不会出现这个错误的,真的出现了话,特例处理,比如常见的500,404错误

UNEXPECTED:暂时没见过

最后,我们写出的failure应该是这样的,健壮高效

@Overridepublicvoidfailure(RetrofitError error){switch(error.getKind()) {caseNETWORK:      showToast("网络错误");break;caseCONVERSION:      showToast("重新输入");break;caseHTTP://这里可以用Mockito模拟showToast("错误代码:"+ String.valueOf(error.getResponse().getStatus())      +"错误原因:"+ error.getResponse().getReason());break;caseUNEXPECTED:      showToast("未知错误");//TODO:写入日志break;  }  showToast("failure:"+ error.getKind());}

在用界面看来,用户得到了有效的错误消息,可以与开发作者沟通反馈。

PS1:POST操作

我目前有个开源的项目,图片上传用的就是Retrofit2.0的POST上传,有兴趣去看看吧。

Fork me on gitHub

PS2: Retrofit2.0

我在stackoverflow回答的关于Retrofit2.0的相关问题

使用RxJava与Retrofit2.0使用的实例:Retrofit 2.0 RxJava Sample

JW大神的文章

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

后记

本文全完,谢谢观看!本博客持续更新与搬运国外大神的文章,有兴趣的话不妨点一个收藏,另外我还维护着一个材料设计的专题,欢迎收藏。

推荐拓展阅读

举报文章

著作权归作者所有

89145aa6a54e@火枪辉耀了我也是最近在学习这个 缓存今天看的 你可以看下这篇文章http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0115/3873.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,724评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,104评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,142评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,086评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,076评论 5 370
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,914评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,220评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,871评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,318评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,834评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,951评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,574评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,162评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,162评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,383评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,349评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,652评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,488评论 25 707
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,358评论 0 17
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • 文/一念之间 此故事是“常走夜路必撞鬼”的结局篇 大红和李发急匆匆地穿过荒坟地,此时已月上中天。 今晚的月亮好像半...
    一nian之间阅读 422评论 2 1
  • 前情回顾: 那个嚷嚷着要睡我的男人。(一) 那个嚷嚷着要睡我的男人(二) 就是马小武不说让我走,我也要走。 这个地...
    月德阅读 386评论 0 0