Android 使用Retrofit请求API数据
概览
Retrofit 是一个Square开发的类型安全的REST安卓客户端请求库。这个库为网络认证、API请求以及用OkHttp发送网络请求提供了强大的框架 。理解OkHttp 的工作流程见 这个指南 。
注意本文是基于Retrofit2.0讲解的 - 译者注。
Retrofit 库让从web api下载JSON 或者xml数据变的非常简单直接。一旦数据下载完成即将其解析成普通java类(POJO)。
设置
首先确保在AndroidManifest.xml中请求了网络权限 :
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- <uses-permission android:name="android.permission.INTERNET" />
- </manifest>
在app/build.gradle文件中添加如下代码:
- dependencies {
- compile 'com.google.code.gson:gson:2.3'
- compile 'com.squareup.retrofit:retrofit:2.0.0-beta1'
- compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
- compile 'com.squareup.okhttp:okhttp:2.4.0'
- }
过去,Retrofit 依赖于Gson 库来解析JSON数据。 Retrofit 2 现在支持许多种解析方式来解析响应数据,包括 Moshi,一个由Square 创建的高效JSON解析库。
但是,它也存在一些 局限,,因此如果你不确定该使用哪种,就暂时使用Gson converter 吧。
有如下的解析库可以选择:
Converter | Library |
---|---|
Gson | com.squareup.retrofit:converter-gson:2.0.0-beta2 |
Jackson | com.squareup.retrofit:converter-jackson:2.0.0-beta1 |
Moshi | com.squareup.retrofit:converter-moshi:2.0.0-beta1 |
Protobuf | com.squareup.retrofit:converter-protobuf:2.0.0-beta1 |
Wire | com.squareup.retrofit:converter-wire:2.0.0-beta1 |
Simple XML | com.squareup.retrofit:converter-simplexml:2.0.0-beta1 |
要能把json解析成数据对象,需要响应的Java对象(准确的讲是类),因此我们需要先自己构造Java对象。
从资源中创建Java对象
本文讨论了两种方法。第一种是手动创建,这要求你学会如何使用 Gson 库。第二种是自动生成,使用jsonschema2pojo。我们建议你使用手动的方式,因为这样你能更好的理解自动生成代码是如何工作的。
手动方式创建Java对象
这篇指南 讲述了关于如何利用Gson库创建自己的数据对象来供Retrofit使用。它演示了如何使用Gson 接收来自Rotten Tomatoes API的数据,但是对于其他RESTful web service方法是相同的。
自动生成Java对象
假设你已经有了JSON的响应结果,访问jsonschema2pojo。
你可以选择注解方式为Gson,但这并不是完全必须的,因为java没有它们也能工作。目前,我们暂且选择None作为注解方式。(Retrofit1.9默认使用的是Gson解析,如果要和Retrofit一起使用,注解方式请选择Gson。不然生成的驼峰命名方式会导致解析失败)
接下来,把JSON 输出粘贴到文字输入框中:
点击Preview 按钮。你可以看到前面部分和下图类似:
把生成的类粘贴到项目的诸如models一类的子包之下,把类名Example重命名为反映数据模型的名称。以这里为例,我们命名为User。
注意:生成的代码中存在@Generated注解。安卓默认并没有javax.annotation library里面的许多东西。如果你希望保留@Generated注解,你需要添加如下的依赖。更多信息参见Stack Overflow上的讨论。或者,你可以直接删除这个注解,完全没有问题。
- dependencies {
- provided 'org.glassfish:javax.annotation:10.0-b28'
- }
创建Retrofit 实例
要向一个api发送我们的网络请求 ,我们需要使用 Retrofit builder 类并指定service的base URL (通常情况下就是域名)。
- public static final String BASE_URL = "http://api.myservice.com";
- Retrofit retrofit = new Retrofit.Builder()
- .baseUrl(BASE_URL)
- .addConverterFactory(GsonConverterFactory.create())
- .build();
还需注意我们要指定一个factory 来对响应进行反序列化,使用的是 Gson library。就如这个视频演讲中所说的,converters 被添加的顺序将是它们被Retrofit尝试的顺序。如果我们希望传入一个自定义的Gson 解析实例,也是可以指定的:
- Gson gson = new GsonBuilder()
- .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
- .create();
- Retrofit retrofit = new Retrofit.Builder()
- .baseUrl(BASE_URL)
- .addConverterFactory(GsonConverterFactory.create(gson))
- .build();
添加多个converters - 译者注。
定义 Endpoints
在Retrofit 2中,endpoints是定义在一个interface里面的(其实1.9版本也是,只是用法略有不同),使用特殊的retrofit 注解来映射参数以及请求方法这些细节。另外,返回值始终是一个参数化了的Call<T>对象,比如Call<User>。如果你不需要任何类型安全的响应,你可以把返回值指定为Call<Response>。
比如,这个interface 用下面的方式定义了每个endpoint :
- public interface MyApiEndpointInterface {
- // Request method and URL specified in the annotation
- // Callback for the parsed response is the last parameter
- @GET("/users/{username}")
- Call<User> getUser(@Path("username") String username);
- @GET("/group/{id}/users")
- Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
- @POST("/users/new")
- Call<User> createUser(@Body User user);
- }
注意到每个endpoint 都指定了一个关于HTTP(GET, POST, 等等。) 方法的注解以及用于分发网络调用的方法。而且这些方法的参数也可以有特殊的注解。
注解 | 描述 |
---|---|
@Path | variable substitution for the API endpoint (i.e. username will be swapped for {username} in the URL endpoint). |
@Query | specifies the query key name with the value corresponding to the value of that annotated parameter. |
@Body | payload for the POST call |
修改 base URL
一般地,base URL是在实例化 Retrofit instance的时候定义的。Retrofit 2 允许你在注解里面重写base URL 。
- @POST("https://api.github.com/api/v3")
就如 这篇文章(中文地址: Retrofit 2.0)所讨论的,还有一些允许你使用相对路径(不是完整的URL)修改 base URL的方法。
Multipart forms
如果你要提交多参数表单数据(multi-part form data),可以使用@Multipart与@Part注解:
- @Multipart
- @POST("/some/endpoint")
- Call<SomeResponse> someEndpoint(@Part("name1") String name1, @Part("name2") String name2)
Form URL encoding
如果我们希望提交 form-encoded name/value ,我们可以使用@FormUrlEncoded 与 @FieldMap注解:
- @FormUrlEncoded
- @POST("/some/endpoint")
- Call<SomeResponse> someEndpoint(@FieldMap Map<String, String> names);
Upgrading from Retrofit 1
如果你是想从 Retrofit 1升级过来,你应该记得在1.x版本中,如果你想定义一个异步的API请求而非同步请求,最后一个参数必须是Callback类型:
- public interface MyApiEndpointInterface {
- // Request method and URL specified in the annotation
- // Callback for the parsed response is the last parameter
- @GET("/users/{username}")
- void getUser(@Path("username") String username, Callback<User> cb);
- @GET("/group/{id}/users")
- void groupList(@Path("id") int groupId, @Query("sort") String sort, Callback<List<User>> cb);
- @POST("/users/new")
- void createUser(@Body User user, Callback<User> cb);
- }
Retrofit 1 依赖于这个Callback类型作为最后一个参数,决定api请求是异步的。为了避免出现两种不同的调用模式,这个接口在Retrofit 2被统一了。现在你可以直接定义返回值为一个参数化了的Call<T>,如前面小节所讲。
Accessing the API
我们现在可以把这些东西放在一起:
- MyApiEndpointInterface apiService =
- retrofit.create(MyApiEndpointInterface.class);
如果我们想异步请求这个API,我们如下调用这个service (注意取名为service 是Retrofit 的惯例,并不是语法的一部分):
- String username = "sarahjean";
- Call<User> call = apiService.getUser(username);
- call.enqueue(new Callback<User>() {
- @Override
- public void onResponse(Response<User> response) {
- int statusCode = response.code();
- User user = response.body();
- }
- @Override
- public void onFailure(Throwable t) {
- // Log error here since request failed
- }
- });
如上所示,Retrofit 将在后台线程下载与解析API数据,然后通过onResponse或者 onFailure方法把结果发送回UI线程。
同步请求呢?更简单,是这样的:
- // Synchronous Call in Retrofit 2.0
- String username = "sarahjean";
- Call<User> call = apiService.getUser(username);
- User user = call.execute();
Retrofit与Authentication
使用Authentication Headers
可以使用一个Interceptor来为请求添加Headers。要发送请求到一个authenticated API,如下:
- // Define the interceptor, add authentication headers
- Interceptor interceptor = new Interceptor() {
- @Override
- public Response intercept(Chain chain) throws IOException {
- Request newRequest = chain.request().newBuilder().addHeader("User-Agent", "Retrofit-Sample-App").build();
- return chain.proceed(newRequest);
- }
- };
- // Add the interceptor to OkHttpClient
- OkHttpClient client = new OkHttpClient();
- client.interceptors().add(interceptor);
- // Set the custom client when building adapter
- Retrofit retrofit = new Retrofit.Builder()
- .baseUrl("https://api.github.com")
- .addConverterFactory(GsonConverterFactory.create())
- .client(client)
- .build();
注意在Retrofit 2中,interceptor 必须添加到一个自定义的OkHttpClient。而在Retrofit 1,它可以直接被builder 类设置。
使用 OAuth
为了用OAuth来认证,我们需要为每个网络请求注册一个特殊的header ,这个header 嵌入了在认证过程中为用户获取的access token。实际的认证过程需要使用第三方的library(比如signpost )完成,然后使用一个request interceptor把 access token添加到header中。Retrofit与authentication的相关链接列举如下:
使用signpost认证的资料:
其它替代signpost的Android OAuth 库:
参考
http://engineering.meetme.com/2014/03/best-practices-for-consuming-apis-on-android/
http://themakeinfo.com/2015/04/android-retrofit-images-tutorial/
https://speakerdeck.com/jakewharton/simple-http-with-retrofit-2-droidcon-nyc-2015
Android 使用Retrofit请求API数据的更多相关文章
- Android - 使用Volley请求网络数据
Android - 使用Volley请求网络数据 Android L : Android Studio 14 个人使用volley的小记,简述使用方法,不涉及volley源码 准备工作 导入Volle ...
- 关于ajax跨域请求API数据的一些问题
一般来说我们使用jquery的ajax来跨域请求API数据的时候每次请求,就只能请求一组数据,而且当我们再次点击发送ajax请求的时候,新请求的数据会覆盖掉原来的数据,那么如何每次在请求的数据的时候, ...
- android 获取http请求json数据
package com.my.gethttpjsondata; import java.io.BufferedReader;import java.io.ByteArrayOutputStream;i ...
- Android 使用Retrofit获取JSON数据
在大家使用网络请求的时候,往往会出现一种情况:需要拿到服务器返回来的JSON字符串,而Retrofit会默认将Json解析,而又没有直接暴露出拿到Json字符串的方法: 今天测接口的时候,发现当数据正 ...
- APICloud ajax请求api数据问题
云编译开启全局加密的情况下,请务必使用api.ajax,避免使用JQ等框架的ajax,否则将引起请求失败.官网API说明链接 还要就是要注意用$.ajax请求数据时会出现的同源策略问题.
- Android okHttp网络请求之Retrofit+Okhttp+RxJava组合
前言: 通过上面的学习,我们不难发现单纯使用okHttp来作为网络库还是多多少少有那么一点点不太方便,而且还需自己来管理接口,对于接口的使用的是哪种请求方式也不能一目了然,出于这个目的接下来学习一下R ...
- Android之三种网络请求解析数据(最佳案例)
AsyncTask解析数据 AsyncTask主要用来更新UI线程,比较耗时的操作可以在AsyncTask中使用. AsyncTask是个抽象类,使用时需要继承这个类,然后调用execute()方法. ...
- 基于Retrofit+RxJava的Android分层网络请求框架
目前已经有不少Android客户端在使用Retrofit+RxJava实现网络请求了,相比于xUtils,Volley等网络访问框架,其具有网络访问效率高(基于OkHttp).内存占用少.代码量小以及 ...
- Android网络请求与数据解析,使用Gson和GsonFormat解析复杂Json数据
版权声明:未经博主允许不得转载 一:简介 [达叔有道]软件技术人员,时代作者,从 Android 到全栈之路,我相信你也可以!阅读他的文章,会上瘾!You and me, we are family ...
随机推荐
- [HAOI2015]树上染色(树形dp)
[HAOI2015]树上染色 题目描述 有一棵点数为 N 的树,树边有边权.给你一个在 0~ N 之内的正整数 K ,你要在这棵树中选择 K个点,将其染成黑色,并将其他 的N-K个点染成白色 . 将所 ...
- 【codeforces 370C】Mittens
[题目链接]:http://codeforces.com/problemset/problem/370/C [题意] 给你n个人,每个人都有一双相同颜色的手套; 然允许在所有人之间交换手套; (每个人 ...
- js 判断是否滚动到底部
$(window).scroll(function(){ var scrollTop = $(this).scrollTop(); //scrollTop() 方法返回或设置匹配元素的滚动条的垂直位置 ...
- [Transducer] Lazyness in Transduer
Transducers remove the requirement of being lazy to optimize for things like take(10). However, it c ...
- hdu4405--Aeroplane chess(概率dp第七弹:飞行棋游戏--2012年网络赛)
Aeroplane chess Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- mysql-组合查询
一.组合查询 mysql允许执行多个查询(多条select语句),并将结果作为单个查询结果集返回.这些组合查询通常称为并(union)或复合查询(compound query). 有两种情况需要使用组 ...
- MyBatis+mysql查询和添加数据
项目结构: Menu package com.mstf.dao; import java.util.Scanner; import org.apache.ibatis.session.SqlSessi ...
- 2018年湘潭大学程序设计竞赛 Fibonacci进制
Fibonacci数是非常有名的一个数列,它的公式为 f(n)=f(n-1)+f(n-2),f(0)=1,f(1)=2. 我们可以把任意一个数x表示成若干不相同的Fibonacci数的和, 比如说1 ...
- JAVA-截取字符串两边指定字符
工具类: /** * 工具类 */ public class Tool { /** * 截取两边指定的字符 * @param character * @param symbol * @return * ...
- NodeJS学习笔记 (19)进阶调试-debugger(ok)
写在前面 谈到node断点调试,目前主要有三种方式,通过node内置调试工具.通过IDE(如vscode).通过node-inspector,三者本质上差不多.本文着重点在于介绍 如何在本地通过nod ...