Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Android SDK
Code

在Android 中使用Retrofit 2 HTTP Client 发送数据

by
Difficulty:IntermediateLength:LongLanguages:

Chinese (Simplified) (中文(简体)) translation by Shen Yun Court (you can also view the original English article)

Final product image
What You'll Be Creating

什么是Retrofit?

Retrofit 是一个用于Android 和Java 的类型安全的HTTP 客户端. 它通过将REST web service 的API 转换成Java 的接口, 以简化HTTP 连接的处理. 在这里, 我将告诉你怎么使用这个在Android 中最常用并且最被推荐的HTTP 库.

这个强大的库可以很容易地处理JSON 或者XML 数据, 然后转换成POJO. GET, POST, PUT, PATCH, 和DELETE 这些请求都可以执行.

和大多数开源软件一样, Retrofit 也是构建在其他强大的库之上. 在底层, Retrofit 使用OKHttp (来自同一个开发者) 来处理网络请求. 同样, Retrofit 也并没有自己构建JSON 转换器来转换JSON 数据, 相反, 它通过支持下面一些JSON 转换库来将JSON 数据转换成Java 对象:

  • Gson: com.squareup.retrofit:converter-gson
  • Jackson: com.squareup.retrofit:converter-jackson
  • Moshi: com.squareup.retrofit:converter-moshi

对于Protocol buffers, 则可以使用:

  • Protobuf: com.squareup.retrofit2:converter-protobuf
  • Wire: com.squareup.retrofit2:converter-wire

而对于XML, 可以使用:

  • Simple Framework: com.squareup.retrofit2:converter-simpleframework

所以, 为什么使用Retrofit?

要自己开发一个类型安全的HTTP 库来映射REST API 是一个痛点: 你必须处理好很多方面, 比如创建连接, 缓存, 失败请求重试, 多线程, 处理响应, 处理错误, 等等. 而从另一个方面来说, Retrofit 是一个经过良好规划, 拥有优质文档, 经过全面测试的库, 它将节省你大量的宝贵的时间.

在这里, 我将通过创建一个简单的APP 来讲解该怎么使用Retrofit 2 来处理网络请求. 这个APP 会执行POST, PUT (为了更新实体), 和DELETE 请求. 我也会告诉你如何与 RxJava 集成以及如何取消请求. 我们将使用由JSONPlaceholder 提供的REST API, 这是一个用于测试和原型设计的在线模拟 API.

如果不知道要怎么执行GET 请求或者和RxJava 进行集成, 可以看看我的前一篇文章, 开始使用Retrofit 2 HTTP 客户端.

1. 创建一个Android 项目

启动Android Studio, 然后创建一个带有一个空的MainActivity 的项目.

create a new empty activity

2. 声明依赖

创建了项目之后, 在build.gradle 里面添加下面的依赖. 这些依赖包括Retrofit 库, 以及用来转换JSON 的Google 的Gson 库, 另外还包含Retrofit 的Gson 集成库.

添加了依赖之后, 你还必须同步一下你的项目.

3. 添加网络权限

要访问网络, 必须要在AndroidManifest.xml 中添加INTERNET 权限.

4. 自动生成Model

我们将使用一个非常有用的工具: jsonschema2pojo, 来将响应的JSON 格式自动创建成相应的Model. 我们会进行一个POST 请求(创建一个新的资源). 但是在开始之前, 我们需要知道我们即将获取到的JSON 响应是什么样的, 这样Retrofit 才能够解析JSON, 然后转换成Java 对象. 根据API 说明, 如果我们用POST 发送下面的数据:

我们将得到下面的响应:

将JSON 数据映射到Java

复制上面示例的响应数据. 访问jsonschema2pojo 然后将JSON 响应粘贴到输入框里面. 选择源类型为JSON, Gson 的annotation, 去掉Allow additional properties 的复选框, 然后将类名从Example 改为Post.

jsonschema2pojo input

然后点击Preview 按钮来生成Java 对象.

jsonschema2pojo output

你可能会好奇这里面的@SerializedName@Expose 是做什么用的! 别着急, 且听我道来!

@SerializedName 是Gson 用于将JSON 的key 映射到Java 对象的字段的.

比如, 上面这一段, JSON 的key userId 将被映射到类字段userId. 想必你也注意到了, 它们是一样的, 所以这里其实可以不用@SerializedName, Gson 会自动为我们做这样的映射.

而另一方面, @Expose 则是用来声明类成员是否需要进行JSON 的序列化或反序列化.

导入数据Model 到Android Studio

现在让我们回到Android Studio. 在main 包下创建data 包. 在这新建的包下面, 再创建一个model 包. 然后在这个包下面创建Post 类. 然后将jsonschema2pojo 生成的代码复制到你创建的Post 类里面

除了getter 和setter 之外, 我还包含了toString() 方法. (在Intellij, 你可以使用Generate 命令来快速生成: 在Windows 的快捷键是Alt-Insert, 在macOS 是则是Command-N)

5. 创建Retrofit 实例

要使用Retrofit 去请求RESTful 的API, 我们需要先使用Retrofit Builder 来创建一个实例, 并使用一个根URL 来配置它.

data 包下面创建remote 包. 然后, 在新建的包下面, 创建一个RetrofitClient 的Java 类. 这个类将通过getClient(String baseUrl) 方法创建一个Retrofit 的单例, 并返回给调用者.

正如我之前提到的, Retrofit 需要一个根URL 来创建实例, 所以我在调用RetrofitClient.getClient(String baseUrl) 的时候传递给它. 这个URL 将被用于创建实例, 如12 行所示. 另外我们在13 行还设置了JSON 的转换器为Gson.

6. 创建API 接口

在remote 包下面, 创建一个APIService 的接口. 这个接口包含了将要用到的发送POST, PUTDELETE 请求的方法. 让我们从POST 请求开始.

看看APIService 类, 我们定义了一个savePost() 方法. 在这个方法上, 有个@POST 的annotation, 这是用来标识当这个方法执行的时候要发送POST 请求出去. @Post annotation 的参数值, 是请求的地址, 这里是/posts. 所以, 请求的全路径将会是http://jsonplaceholder.typicode.com/posts.

OK, 那么@FormUrlEncoded 是用来做什么的呢? 它是用来标识这个请求的MIME 类型(一个用来标识HTTP 请求或响应的内容格式的HTTP 头) 需要设置成application/x-www-form-urlencoded, 并且请求的字段和字段值需要在进行URL 编码之前先进行UTF-8 编码处理. @Field("key") 里面的参数值需要和API 期望的参数名相匹配. Retrofit 使用String.valueOf(Object) 将值转换成字符串, 然后对这些字符串进行URL 编码处理. null 值则忽略.

例如, 调用APIService.savePost("My Visit To Lagos", "I visited...", 2) 会生成title=My+Visit+To+Lagos&body=I+visited...&userId=2 这样的请求内容.

使用@Body Annotation

我们也可以在请求方法的参数使用@Body Annotation, 而不是像上面那样每个字段都单独指定. 这个对象将使用在创建Retrofit 实例时指定的Converter 进行序列化. 不过这个只能用于在进行POST 或者PUT 请求的时候.

7. 创建API 工具类

我们还需要创建一个工具类. 在data.remote 包下面创建一个ApiUtils 的类. 这个类定义了根URL 的路径, 并且定义静态方法getAPIService(), 以方便应用中其他类对APIService 的调用.

需要注意的是, 根URL 需要以/ 结尾.

8. 创建布局

MainActivity 对应的布局文件是activity_main.xml. 这个布局, 包含了两个文本框, 一个用于输入标题, 另一个则是用于输入内容. 另外还有个用于提交数据的按钮.

9. 提交POST 请求

MainActivityonCreate() 方法里面, 我们进行APIService 接口的初始化(第14 行). 我们也获取了文本框EditText, 并且对提交按钮绑定点击事件, 使得当它被点击时, 调用sendPost() 方法.

MainActivitysendPost(String, String) 方法里面, 我们接收标题和内容参数. 然后在这个方法里面, 调用API service 接口的savePost(String, String) 方法, 将接收到的标题和内容用POST 请求的方式发送给API. 而showResponse(String response) 方法则会将响应数据显示在屏幕上.

对象mAPIService (APIService 接口的实例) 的savePost(String, String) 方法会返回一个Call 对象, 这个对象有个enqueue(Callback callback) 方法.

理解enqueue()

enqueue() 异步地发送请求, 然后当响应回来的时候, 使用回调的方式通知你的APP. 因为这个请求是异步的, Retrofit 使用一个另外的线程去执行它, 这样UI 线程就不会被阻塞了.

要使用enqueue() 方法, 你需要实现两个回调方法: onResponse() 和onFailure(). 对于一个请求, 当响应回来的时候, 只有一个方法会被执行.

  • onResponse(): 在收到HTTP 响应的时候被调用. 这个方法在服务器可以处理请求的情况下调用, 即使服务器返回的是一个错误的信息. 例如你获取到的响应状态是404 或500. 你可以使用response.code() 来获取状态码, 以便进行不同的处理. 当然你也可以直接用isSuccessful() 方法来判断响应的状态码是不是在200-300 之间(在这个范围内标识是成功的).
  • onFailure(): 当和服务器通信出现网络异常时, 或者在处理请求出现不可预测的错误时, 会调用这个方法.

同步请求

要执行同步的请求, 你可以直接使用Call 对象的execute() 方法. 但是要注意, 如果在UI 线程执行同步的请求, 将会阻塞用户的操作. 所以, 不要在Android 的UI 线程去执行同步的方法. 而是应该将它们放在后台线程去运行.

使用RxJava

在Retrofit 1 中, RxJava 是默认集成进去了的, 但是在Retrofit 2 中, 我们还需要添加一些额外的依赖. Retrofit 在执行Call 对象的方法时, 带有一个默认的Adapter. 所以你能够通过引入RxJava (带有RxJava 的CallAdapter), 改变Retrofit 的运行机制. 按照下面的步骤:

步骤1

添加依赖.

步骤2

在创建Retrofit 实例的时候添加新的CallAdapter RxJavaCallAdapterFactory.create() (第5 行).

步骤3

更新APIServicesavePost(String title, String body, String userId) 方法, 将返回值改为Observable

步骤4

当进行请求时, 添加一个匿名的subscriber 去监听observable 的流. 当subscriber 接收到事件时, 将调用onNext 方法, 然后在这个方法里面调用showResponse(String response) 方法

如果想要了解更多关于RxJava 和RxAndroid 的知识, 可以看看Ashraff Hathibelagal 的在Android 开始使用ReactiveX.

10. 测试APP

到这里, 你就可以允许你的APP, 输入标题和内容, 然后点击提交按钮. 从API 返回的结果将显示在提交按钮下面.

final app screenshot

11. 执行PUT 请求.

现在你已经指定怎么执行POST 请求了, 让我们看看该怎么执行PUT 请求来更新实体. 在APIService 类中添加下面的新方法.

要更新一个post, 我们使用/posts/{id} 这样的请求地址, 在这里{id} 是用来填充我们要更新的post 的id 的占位符. 而参数中的@Path 注解是用来填充URL 路径中的{id} 片段的值的. 要注意的是这些值都将使用String.valueOf(Object) 转换成字符串, 然后进行URL 编码. 如果这个值已经是编码过了的, 你也可以用这样的方式禁止再进行URL 编码: @Path(value="name", encoded=true).

12. 执行DELETE 请求.

让我们再来看看怎么执行DELETE 请求. 要使用JSONPlaceholder API 去删除一个post 资源, 要请求的地址和更新是一样的/posts/{id}, 所不同的只是使用的HTTP 方法是DELETE. 回到APIService 接口, 只需要添加下面的deletePost() 方法. 我们传入方法的参数是要删除的post id, 这个id 一样是用来替换URL 中的{id} 片段.

13. 取消请求.

如果你想让你的用户能够取消或放弃一个请求. 在Retrofit 可以很容易做到这一点. Retrofit 的Call 类有个cancel() 方法就是用来做这个的(下面的第30 行). 这个方法将触发onFailure() 回调方法.

如果在没有网络连接, 或者在创建请求或处理响应时出现意外异常, 可以调用这个方法来取消请求. 如果你想要知道你的请求是否被取消了, 可以使用Call 类里面的isCanceled() 方法(第18 行).

总结

在这篇教程, 你学习了Retrofit 一些内容: 为啥要用它, 怎么在项目中集成, 怎么进行POST, PUT, DELETE 请求, 怎么取消请求. 你还了解到该怎么让Retrofit 和RxJava 进行集成. 在下一篇文章中, 我将讲讲该怎么上传文件.

想要了解更多Retrofit 的内容, 请务必去看看官方的文档. 然后看看Envato Tuts+ 里面关于Android 开发的其他教程和课程.

关注我们的公众号
Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.