开始使用Retrofit 2 HTTP 客户端
来源:互联网 发布:linux修改dns 编辑:程序博客网 时间:2024/05/22 01:39
什么是 Retrofit?
Retrofit 是一种用于Android和Java的类型安全的HTTP客户端。 Retrofit通过将API转换为Java接口来轻松连接到REST Web服务。 在本教程中,我将向您介绍如何使用Android上最流行和经常推荐的HTTP库之一。
这个强大的库可以方便地使用JSON或XML数据,然后将其解析为普通Java对象(POJO)。 GET,POST,PUT,PATCH和DELETE请求都可以执行。
像大多数开源软件一样,Retrofit基于一些其他强大的库和工具。 在幕后,Retrofit使用OkHttp(来自同一个开发人员)处理网络请求。 此外,Retrofit没有内置的任何JSON转换器来解析从JSON到Java对象。 相反,它支持以下JSON转换器库来处理:- Gson:
com.squareup.retrofit:converter-gson
- Jackson:
com.squareup.retrofit:converter-jackson
- Moshi:
com.squareup.retrofit:converter-moshi
For Protocol Buffers, Retrofit 支持:
- Protobuf:
com.squareup.retrofit2:converter-protobuf
- Wire:
com.squareup.retrofit2:converter-wire
And for XML, Retrofit 支持:
- Simple Framework:
com.squareup.retrofit2:converter-simpleframework
为什么要使用 Retrofit?
开发自己的类型安全HTTP库以与REST API交互可能是一个真正的痛苦:你必须处理许多功能,如进行连接,缓存,重试失败的请求,线程,响应解析,错误处理等等。 改造,另一方面,是非常好的计划,文件和测试 - 一个经过考验的图书馆,将为您节省大量宝贵的时间和头痛。在本教程中,我将解释如何使用Retrofit 2来处理网络请求,方法是构建一个简单的应用程序,以查询Stack Exchange API的最新解答。 我们将通过指定一个endpoint- / answers,附加到基本URL https://api.stackexchange.com/2.2,然后获取结果并在回收站视图中显示它们来执行GET请求。 我也将告诉你如何做到这一点与RxJava易于管理的状态和数据的流。
1. 创建Android Studio项目
启动Android Studio并创建一个名为MainActivity的空活动的新项目。
2. 声明依赖关系
创建新项目后,在build.gradle中声明以下依赖项。 依赖关系包括回收器视图,Retrofit库,以及Google的Gson库,用于将JSON转换为POJO(普通Java对象)以及Retrofit的Gson集成。
// Retrofit
compile
'com.squareup.retrofit2:retrofit:2.1.0'
// JSON Parsing
compile
'com.google.code.gson:gson:2.6.1'
compile
'com.squareup.retrofit2:converter-gson:2.1.0'
// recyclerview
compile
'com.android.support:recyclerview-v7:25.0.1'
不要忘记同步项目以下载这些库。
3. 添加Internet权限
要执行网络操作,我们需要在应用程序清单中包含INTERNET权限:AndroidManifest.xml。
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
manifest
xmlns:android
=
"http://schemas.android.com/apk/res/android"
package
=
"com.chikeandroid.retrofittutorial"
>
<
uses-permission
android:name
=
"android.permission.INTERNET"
/>
<
application
android:allowBackup
=
"true"
android:icon
=
"@mipmap/ic_launcher"
android:label
=
"@string/app_name"
android:supportsRtl
=
"true"
android:theme
=
"@style/AppTheme"
>
<
activity
android:name
=
".MainActivity"
>
<
intent-filter
>
<
action
android:name
=
"android.intent.action.MAIN"
/>
<
category
android:name
=
"android.intent.category.LAUNCHER"
/>
</
intent-filter
>
</
activity
>
</
application
>
</
manifest
>
4. 自动生成模型
我们将通过利用一个非常有用的工具,从JSON响应数据中自动创建模型: jsonschema2pojo.
获取示例JSON数据
在浏览器的地址栏中复制并粘贴https://api.stackexchange.com/2.2/answers?order=desc&sort=activity&site=stackoverflow(如果您熟悉该工具,则可以使用Postman)。 然后按Enter键,这将在给定端点上执行GET请求。 你会看到响应是一个JSON对象数组。 下面的截图是使用Postman的JSON响应。
{
"items"
: [
{
"owner"
: {
"reputation"
: 1,
"user_id"
: 6540831,
"user_type"
:
"registered"
,
"profile_image"
:
"https://www.gravatar.com/avatar/6a468ce8a8ff42c17923a6009ab77723?s=128&d=identicon&r=PG&f=1"
,
"display_name"
:
"bobolafrite"
,
"link"
:
"http://stackoverflow.com/users/6540831/bobolafrite"
},
"is_accepted"
:
false
,
"score"
: 0,
"last_activity_date"
: 1480862271,
"creation_date"
: 1480862271,
"answer_id"
: 40959732,
"question_id"
: 35931342
},
{
"owner"
: {
"reputation"
: 629,
"user_id"
: 3054722,
"user_type"
:
"registered"
,
"profile_image"
:
"https://www.gravatar.com/avatar/0cf65651ae9a3ba2858ef0d0a7dbf900?s=128&d=identicon&r=PG&f=1"
,
"display_name"
:
"jeremy-denis"
,
"link"
:
"http://stackoverflow.com/users/3054722/jeremy-denis"
},
"is_accepted"
:
false
,
"score"
: 0,
"last_activity_date"
: 1480862260,
"creation_date"
: 1480862260,
"answer_id"
: 40959731,
"question_id"
: 40959661
},
...
],
"has_more"
:
true
,
"backoff"
: 10,
"quota_max"
: 300,
"quota_remaining"
: 241
}
从浏览器或Postman复制此JSON响应。
将JSON数据映射到Java
现在访问jsonschema2pojo并将JSON响应粘贴到输入框中。选择JSON的源类型,Gson的注释样式,并取消选中允许其他属性。
然后单击“预览”按钮以生成Java对象。
你可能想知道@SerializedName和@Expose注释在这个生成的代码中做什么。 别担心,我会解释一切!Gson使用@SerializedName注释来将JSON键映射到我们的字段。 为了遵守Java的关于类成员属性的camelCase命名约定,不建议使用下划线来分隔变量中的单词。 @SerializedName有助于在两者之间进行转换。
@SerializedName
(
"quota_remaining"
)
@Expose
private
Integer quotaRemaining;
@Expose注释表示该成员应该公开用于JSON序列化或反序列化。
将数据模型导入Android Studio
现在让我们回到Android Studio。 在主包中创建一个新的子包并将其命名为数据。 在新创建的数据包内,创建另一个包并将其命名为model。 在模型包内,创建一个新的Java类并将其命名为Owner。 现在复制由jsonschema2pojo生成的Owner类,并将其粘贴到您创建的Owner类中。
import
com.google.gson.annotations.Expose;
import
com.google.gson.annotations.SerializedName;
public
class
Owner {
@SerializedName
(
"reputation"
)
@Expose
private
Integer reputation;
@SerializedName
(
"user_id"
)
@Expose
private
Integer userId;
@SerializedName
(
"user_type"
)
@Expose
private
String userType;
@SerializedName
(
"profile_image"
)
@Expose
private
String profileImage;
@SerializedName
(
"display_name"
)
@Expose
private
String displayName;
@SerializedName
(
"link"
)
@Expose
private
String link;
@SerializedName
(
"accept_rate"
)
@Expose
private
Integer acceptRate;
public
Integer getReputation() {
return
reputation;
}
public
void
setReputation(Integer reputation) {
this
.reputation = reputation;
}
public
Integer getUserId() {
return
userId;
}
public
void
setUserId(Integer userId) {
this
.userId = userId;
}
public
String getUserType() {
return
userType;
}
public
void
setUserType(String userType) {
this
.userType = userType;
}
public
String getProfileImage() {
return
profileImage;
}
public
void
setProfileImage(String profileImage) {
this
.profileImage = profileImage;
}
public
String getDisplayName() {
return
displayName;
}
public
void
setDisplayName(String displayName) {
this
.displayName = displayName;
}
public
String getLink() {
return
link;
}
public
void
setLink(String link) {
this
.link = link;
}
public
Integer getAcceptRate() {
return
acceptRate;
}
public
void
setAcceptRate(Integer acceptRate) {
this
.acceptRate = acceptRate;
}
}
对新的Item类执行相同的操作,从jsonschema2pojo复制。
import
com.google.gson.annotations.Expose;
import
com.google.gson.annotations.SerializedName;
public
class
Item {
@SerializedName
(
"owner"
)
@Expose
private
Owner owner;
@SerializedName
(
"is_accepted"
)
@Expose
private
Boolean isAccepted;
@SerializedName
(
"score"
)
@Expose
private
Integer score;
@SerializedName
(
"last_activity_date"
)
@Expose
private
Integer lastActivityDate;
@SerializedName
(
"creation_date"
)
@Expose
private
Integer creationDate;
@SerializedName
(
"answer_id"
)
@Expose
private
Integer answerId;
@SerializedName
(
"question_id"
)
@Expose
private
Integer questionId;
@SerializedName
(
"last_edit_date"
)
@Expose
private
Integer lastEditDate;
public
Owner getOwner() {
return
owner;
}
public
void
setOwner(Owner owner) {
this
.owner = owner;
}
public
Boolean getIsAccepted() {
return
isAccepted;
}
public
void
setIsAccepted(Boolean isAccepted) {
this
.isAccepted = isAccepted;
}
public
Integer getScore() {
return
score;
}
public
void
setScore(Integer score) {
this
.score = score;
}
public
Integer getLastActivityDate() {
return
lastActivityDate;
}
public
void
setLastActivityDate(Integer lastActivityDate) {
this
.lastActivityDate = lastActivityDate;
}
public
Integer getCreationDate() {
return
creationDate;
}
public
void
setCreationDate(Integer creationDate) {
this
.creationDate = creationDate;
}
public
Integer getAnswerId() {
return
answerId;
}
public
void
setAnswerId(Integer answerId) {
this
.answerId = answerId;
}
public
Integer getQuestionId() {
return
questionId;
}
public
void
setQuestionId(Integer questionId) {
this
.questionId = questionId;
}
public
Integer getLastEditDate() {
return
lastEditDate;
}
public
void
setLastEditDate(Integer lastEditDate) {
this
.lastEditDate = lastEditDate;
}
}
最后,为返回的StackOverflow答案创建一个名为SOAnswersResponse的类。 您将在jsonschema2pojo中找到此类的代码作为示例。 确保您将类名称更新为SOAnswersResponse,无论它发生在哪里。
import
com.google.gson.annotations.Expose;
import
com.google.gson.annotations.SerializedName;
import
java.util.List;
public
class
SOAnswersResponse {
@SerializedName
(
"items"
)
@Expose
private
List<Item> items =
null
;
@SerializedName
(
"has_more"
)
@Expose
private
Boolean hasMore;
@SerializedName
(
"backoff"
)
@Expose
private
Integer backoff;
@SerializedName
(
"quota_max"
)
@Expose
private
Integer quotaMax;
@SerializedName
(
"quota_remaining"
)
@Expose
private
Integer quotaRemaining;
public
List<Item> getItems() {
return
items;
}
public
void
setItems(List<Item> items) {
this
.items = items;
}
public
Boolean getHasMore() {
return
hasMore;
}
public
void
setHasMore(Boolean hasMore) {
this
.hasMore = hasMore;
}
public
Integer getBackoff() {
return
backoff;
}
public
void
setBackoff(Integer backoff) {
this
.backoff = backoff;
}
public
Integer getQuotaMax() {
return
quotaMax;
}
public
void
setQuotaMax(Integer quotaMax) {
this
.quotaMax = quotaMax;
}
public
Integer getQuotaRemaining() {
return
quotaRemaining;
}
public
void
setQuotaRemaining(Integer quotaRemaining) {
this
.quotaRemaining = quotaRemaining;
}
}
5. 创建改进实例
要使用Retrofit向REST API发出网络请求,我们需要使用Retrofit.Builder类创建一个实例,并使用基本URL配置它。在数据包内创建一个新的子包包,并将它命名为remote。 现在在远程,创建一个Java类,并命名为RetrofitClient。 这个类将创建一个单一的Retrofit。 Retrofit需要一个基本URL来构建其实例,因此当调用RetrofitClient.getClient(String baseUrl)时,我们将传递一个URL。 这个URL将用于在第13行中构建实例。我们还在第14行中指定了我们需要的JSON转换器(Gson)。
import
retrofit2.Retrofit;
import
retrofit2.converter.gson.GsonConverterFactory;
public
class
RetrofitClient {
private
static
Retrofit retrofit =
null
;
public
static
Retrofit getClient(String baseUrl) {
if
(retrofit==
null
) {
retrofit =
new
Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return
retrofit;
}
}
6. 创建API接口
在远程包中,创建一个接口并调用它SOService。 此接口包含我们将用于执行HTTP请求(如GET,POST,PUT,PATCH和DELETE)的方法。 对于本教程,我们将执行GET请求。
import
com.chikeandroid.retrofittutorial.data.model.SOAnswersResponse;
import
java.util.List;
import
retrofit2.Call;
import
retrofit2.http.GET;
public
interface
SOService {
@GET
(
"/answers?order=desc&sort=activity&site=stackoverflow"
)
Call<SOAnswersResponse> getAnswers();
@GET
(
"/answers?order=desc&sort=activity&site=stackoverflow"
)
Call<SOAnswersResponse> getAnswers(
@Query
(
"tagged"
) String tags);
}
在第二个方法定义中,我们添加了一个查询参数,用于从服务器过滤数据。 Retrofit使用@Query(“key”)注释来代替在端点中对其进行硬编码。 键值表示URL中的参数名称。 它将被Retrofit添加到URL。 例如,如果我们将值“android”作为参数传递给getAnswers(String tags)方法,则完整的URL将是:
https://api.stackexchange.com/2.2/answers?order=desc&sort=activity&site=stackoverflow&tagged=android
接口方法的参数可以有以下注释:
@Path
API端点的变量替换@Query
使用带注释参数的值指定查询键名称@Body
POST调用的有效内容@Header
指定带有注释参数值的标题7. 创建API实用程序
现在要创建一个实用程序类。 我们将其命名为ApiUtils。 此类将基本URL作为静态变量,并通过getSOService()静态方法为我们的应用程序提供SOService接口。
public
class
ApiUtils {
public
static
final
String BASE_URL =
"https://api.stackexchange.com/2.2/"
;
public
static
SOService getSOService() {
return
RetrofitClient.getClient(BASE_URL).create(SOService.
class
);
}
}
8. 显示到RecyclerView
由于结果将显示在回收站视图(recycler view)中,因此我们需要一个适配器。 下面的代码片段显示了AnswersAdapter类。
public
class
AnswersAdapter
extends
RecyclerView.Adapter<AnswersAdapter.ViewHolder> {
private
List<Item> mItems;
private
Context mContext;
private
PostItemListener mItemListener;
public
class
ViewHolder
extends
RecyclerView.ViewHolder
implements
View.OnClickListener{
public
TextView titleTv;
PostItemListener mItemListener;
public
ViewHolder(View itemView, PostItemListener postItemListener) {
super
(itemView);
titleTv = (TextView) itemView.findViewById(android.R.id.text1);
this
.mItemListener = postItemListener;
itemView.setOnClickListener(
this
);
}
@Override
public
void
onClick(View view) {
Item item = getItem(getAdapterPosition());
this
.mItemListener.onPostClick(item.getAnswerId());
notifyDataSetChanged();
}
}
public
AnswersAdapter(Context context, List<Item> posts, PostItemListener itemListener) {
mItems = posts;
mContext = context;
mItemListener = itemListener;
}
@Override
public
AnswersAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int
viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View postView = inflater.inflate(android.R.layout.simple_list_item_1, parent,
false
);
ViewHolder viewHolder =
new
ViewHolder(postView,
this
.mItemListener);
return
viewHolder;
}
@Override
public
void
onBindViewHolder(AnswersAdapter.ViewHolder holder,
int
position) {
Item item = mItems.get(position);
TextView textView = holder.titleTv;
textView.setText(item.getOwner().getDisplayName());
}
@Override
public
int
getItemCount() {
return
mItems.size();
}
public
void
updateAnswers(List<Item> items) {
mItems = items;
notifyDataSetChanged();
}
private
Item getItem(
int
adapterPosition) {
return
mItems.get(adapterPosition);
}
public
interface
PostItemListener {
void
onPostClick(
long
id);
}
}
9. 执行请求
在MainActivity的onCreate()方法中,我们初始化SOService接口(行9)的实例,回收器视图以及适配器。 最后,我们调用loadAnswers()方法。
private
AnswersAdapter mAdapter;
private
RecyclerView mRecyclerView;
private
SOService mService;
@Override
protected
void
onCreate (Bundle savedInstanceState) {
super
.onCreate( savedInstanceState );
setContentView(R.layout.activity_main );
mService = ApiUtils.getSOService();
mRecyclerView = (RecyclerView) findViewById(R.id.rv_answers);
mAdapter =
new
AnswersAdapter(
this
,
new
ArrayList<Item>(
0
),
new
AnswersAdapter.PostItemListener() {
@Override
public
void
onPostClick(
long
id) {
Toast.makeText(MainActivity.
this
,
"Post id is"
+ id, Toast.LENGTH_SHORT).show();
}
});
RecyclerView.LayoutManager layoutManager =
new
LinearLayoutManager(
this
);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setHasFixedSize(
true
);
RecyclerView.ItemDecoration itemDecoration =
new
DividerItemDecoration(
this
, DividerItemDecoration.VERTICAL_LIST);
mRecyclerView.addItemDecoration(itemDecoration);
loadAnswers();
}
loadAnswers()方法通过调用enqueue()发出网络请求。 当响应返回时,Retrofit帮助我们将JSON响应解析为Java对象列表。(这可以通过使用GsonConverter。)
public
void
loadAnswers() {
mService.getAnswers().enqueue(
new
Callback<SOAnswersResponse>() {
@Override
public
void
onResponse(Call<SOAnswersResponse> call, Response<SOAnswersResponse> response) {
if
(response.isSuccessful()) {
mAdapter.updateAnswers(response.body().getItems());
Log.d(
"MainActivity"
,
"posts loaded from API"
);
}
else
{
int
statusCode = response.code();
// handle request errors depending on status code
}
}
@Override
public
void
onFailure(Call<SOAnswersResponse> call, Throwable t) {
showErrorMessage();
Log.d(
"MainActivity"
,
"error loading from API"
);
}
});
}
10. 了解 enqueue()
enqueue()异步发送请求,并在响应返回时通知应用程序回调。 因为这个请求是异步的,Retrofit在后台线程上处理它,所以主UI线程不会被阻塞或干扰。
要使用enqueue(),你必须实现两个回调方法:onResponse()
onFailure()
仅响应给定请求,才会调用其中一个方法。
- onResponse():为接收到的HTTP响应调用。 调用此方法可以正确处理响应,即使服务器返回错误消息。 因此,如果您获得404或500的状态代码,此方法仍将被调用。 要获取状态代码,以便您处理基于它们的情况,您可以使用方法response.code()。 您还可以使用isSuccessful()方法来确定状态代码是否在200-300范围内,表示成功。
onFailure()
: 在发生与服务器通信的网络异常时或在处理请求或处理响应时发生意外异常时调用。
要执行同步请求,可以使用execute()方法。 请注意,主/ UI线程上的同步方法将阻止任何用户操作。 所以不要在Android的主/ UI线程上执行同步方法! 相反,在后台线程上运行它们。
11. 测试应用程序
您现在可以运行该应用程序。
12. RxJava 集成
如果你是RxJava的粉丝,你可以很容易地用RxJava实现Retrofit。 在Retrofit 1中,它是默认集成的,但在Retrofit 2中,你需要包括一些额外的依赖。 Retrofit附带了用于执行调用实例的默认适配器。 因此,您可以通过包括RxJava CallAdapter来更改Retrofit的执行机制以包括RxJava。
步骤 1
添加依赖关系。
compile
'io.reactivex:rxjava:1.1.6'
compile
'io.reactivex:rxandroid:1.2.1'
compile
'com.squareup.retrofit2:adapter-rxjava:2.1.0'
步骤 2
在构建Retrofit实例时添加新的CallAdapter RxJavaCallAdapterFactory.create()。
public
static
Retrofit getClient(String baseUrl) {
if
(retrofit==
null
) {
retrofit =
new
Retrofit.Builder()
.baseUrl(baseUrl)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return
retrofit;
}
步骤 3
当发出请求时,我们的匿名用户响应observable的流发出事件,在我们的例子SOAnswersResponse。 当我们的订阅者接收到任何发出的事件然后传递给我们的适配器时,onNext方法被调用。
@Override
public void loadAnswers() {
mService.getAnswers().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<SOAnswersResponse>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(SOAnswersResponse soAnswersResponse) {
mAdapter.updateAnswers(soAnswersResponse.getItems());
}
});
}
查看Android上的ReactiveX入门Ashraff Hathibelagal了解有关RxJava和RxAndroid的更多信息。
Conclusion
在本教程中,您了解了Retrofit:为什么应该使用它以及如何使用它。 我还解释了如何添加RxJava与Retrofit集成。 在下一篇文章中,我将向您展示如何执行POST,PUT和DELETE,如何发送Form-Urlencoded数据以及如何取消请求。
- 开始使用Retrofit 2 HTTP 客户端
- 开始在Android上使用Retrofit并创建Http请求客户端
- Retrofit http客户端
- 一种类型安全的Java HTTP客户端库Retrofit
- 如何使用OkHttp/Retrofit重试HTTP请求
- Retrofit 2 使用教程
- OkHttp+ Retrofit使用从0开始(一)
- OkHttp+ Retrofit使用从0开始(二)
- 用 Retrofit 2 简化 HTTP 请求
- 用 Retrofit 2 简化 HTTP 请求
- 用 Retrofit 2 简化 HTTP 请求
- Retrofit--开始之旅
- 【Android】Retrofit的使用(2)-使用Retrofit提交JSON数据
- Retrofit 2简单使用教程
- Retrofit 2 使用(转)
- Retrofit的使用(2)
- Android中使用Retrofit库进行Http通讯
- 使用Retrofit进行Http、Https网络请求(快速上手)
- xmpp协议详解一:xmpp基本概念
- java泛型
- spring两种常用依赖注入方式
- 我的框架--QiCai(视图类)
- 谁在说谎
- 开始使用Retrofit 2 HTTP 客户端
- Android TV ViewPager翻页控制
- Android 源码分析之基于Stagefright的MediaPlayer播放框架[3]
- 集合和数组的区别
- sql中设置字段的显示个数
- varchar与char 的区别
- Spark算子:RDD基本转换操作(7)–zipWithIndex、zipWithUniqueId
- Java之封装
- HTTP中的URL长度限制