使用Retrofit 2 HTTP Client for Android发送数据

本文介绍了Retrofit,一个适用于Android和Java的类型安全的HTTP客户端,用于连接REST Web服务。通过创建Android Studio项目,声明依赖,添加权限,自动生成数据模型,构建Retrofit实例和API接口,展示了如何执行POST、PUT和DELETE请求。同时,讨论了使用RxJava进行集成以及如何取消请求。
摘要由CSDN通过智能技术生成
最终产品图片
您将要创造的

什么是翻新?

Retrofit是适用于Android和Java的类型安全的HTTP客户端。 通过将API转换为Java接口,翻新可以轻松连接到REST Web服务。 在本教程中,我将向您展示如何使用可用于Android的最受欢迎的HTTP库之一。

这个功能强大的库使您可以轻松使用JSON或XML数据,然后将其解析为原始Java对象(PO​​JO)。 GETPOSTPUTPATCHDELETE请求都可以执行。

像大多数开源软件一样,Retrofit建立在其他一些强大的库和工具之上。 在幕后,Retrofit利用OkHttp (来自同一开发人员)来处理网络请求。 此外,Retrofit没有内置的JSON转换器可将JSON解析为Java对象。 相反,它提供了对以下JSON转换器库的支持以处理该问题:

  • Gson: com.squareup.retrofit:converter-gson
  • 杰克逊: com.squareup.retrofit:converter-jackson
  • Moshi: com.squareup.retrofit:converter-moshi

对于协议缓冲区 ,翻新支持:

  • Protobuf: com.squareup.retrofit2:converter-protobuf
  • 连线: com.squareup.retrofit2:converter-wire

对于XML Retrofit,支持:

  • 简单框架: com.squareup.retrofit2:converter-simpleframework

那么为什么要使用改造?

开发自己的类型安全的HTTP库以与REST API接口可能是一个真正的痛苦:您必须处理许多方面,例如建立连接,缓存,重试失败的请求,线程,响应解析,错误处理等等。 另一方面,翻新是经过精心计划,记录和测试的库,可为您节省很多宝贵的时间和麻烦。

在本教程中,我将通过构建一个简单的应用程序来解释如何使用Retrofit 2处理网络请求,该应用程序将执行POST请求, PUT请求(以更新实体)和DELETE请求。 我还将向您展示如何与RxJava集成以及如何取消请求。 我们将使用JSONPlaceholder提供的API-这是用于测试和原型制作的虚假在线REST API。

1.创建一个Android Studio项目

启动Android Studio并创建一个名为MainActivity的空活动的新项目。

创建一个新的空活动

2.声明依赖关系

创建新项目后,在build.gradle声明以下依赖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'

添加依赖项后,确保同步项目。

3.添加Internet权限

为了执行网络操作,我们需要在应用程序清单AndroidManifest.xml中包含INTERNET权限。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.chikeandroid.retrofittutorial2">

    <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=".PostActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
    
</manifest>

4.自动生成模型

我们将利用一个非常有用的工具jsonschema2pojo从JSON响应数据自动创建模型。 我们想在API上发出POST请求(创建新资源)。 但是在执行此请求之前,我们需要知道成功执行后应该期望的JSON响应,以便Retrofit可以解析JSON响应并将其反序列化为Java对象。 根据API ,如果我们在POST请求中发送以下数据:

data: {
    title: 'foo',
    body: 'bar',
    userId: 1
}

我们应该得到以下回应:

{
  "title": "foo",
  "body": "bar",
  "userId": 1,
  "id": 101
}

将JSON数据映射到Java

复制上一部分中的样本响应数据。 现在访问jsonschema2pojo并将JSON响应粘贴到输入框中。 选择JSON的源类型, Gson的注释样式,取消选中“ 允许其他属性” ,并将类名称从Example更改为Post

jsonschema2pojo输入

然后单击“ 预览”按钮以生成Java对象。

jsonschema2pojo输出

您可能想知道@SerializedName@Expose批注在此生成的代码中的作用! 不用担心,我会解释一切!

Gson需要@SerializedName批注才能将JSON键映射到Java对象字段。

@SerializedName("userId")
@Expose
private Integer userId;

在这种情况下,JSON密钥userId映射到类字段userId 。 但请注意,由于它们相同,因此无需在字段上包含@SerializedName批注,因为Gson会自动为我们映射它。

@Expose批注指示应公开该类成员以进行JSON序列化或反序列化。

将数据模型导入Android Studio

现在让我们回到Android Studio。 在main包内创建一个新的子包,并将其命名为data 。 在新创建的包中,创建另一个包并将其命名为model 。 在此包中,创建一个新的Java类,并将其命名为Post 。 现在复制由jsonschema2pojo生成的Post类,并将其粘贴到您创建的Post类中。

package com.chikeandroid.retrofittutorial2.data.model;

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class Post {

    @SerializedName("title")
    @Expose
    private String title;
    @SerializedName("body")
    @Expose
    private String body;
    @SerializedName("userId")
    @Expose
    private Integer userId;
    @SerializedName("id")
    @Expose
    private Integer id;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
   
    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }
    
    @Override
    public String toString() {
        return "Post{" +
                "title='" + title + '\'' +
                ", body='" + body + '\'' +
                ", userId=" + userId +
                ", id=" + id +
                '}';
    }
}

除了getter和setter之外,我还包括toString()方法。 (在Intellij中,可以使用Generate命令简化此操作:在Windows上为Alt-Insert ,在macOS上为Command-N 。)

5.创建改造实例

要使用Retrofit向RESTful API发出网络请求,我们需要使用Retrofit Builder类创建一个实例,并使用基本URL对其进行配置。

data包内创建一个新的子包,并将其命名为remote 。 现在,在此包中,创建一个Java类并将其命名为RetrofitClient 。 此类将在getClient(String baseUrl)方法中创建Retrofit的单例并将其返回给调用方。

如前所述,Retrofit需要一个基本URL来构建其实例,因此在调用RetrofitClient.getClient(String baseUrl)时,我们将为其传递一个URL。 然后,该URL将在第12行中用于构建实例。我们还在第13行中指定所需的JSON转换器(Gson)。

package com.chikeandroid.retrofittutorial2.data.remote;
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接口

在远程包中,创建一个接口,并将其APIService 。 该接口包含我们将用于执行HTTP请求的方法,例如POSTPUTDELETE 。 让我们从POST请求开始。

package com.chikeandroid.retrofittutorial2.data.remote;
import com.chikeandroid.retrofittutorial2.data.model.Post;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;

public interface APIService {

    @POST("/posts")
    @FormUrlEncoded
    Call<Post> savePost(@Field("title") String title,
                        @Field("body") String body,
                        @Field("userId") long userId);
}

查看APIService类,我们有一个名为savePost()的方法。 该方法的顶部是@POST批注,它指示我们希望在调用此方法时执行POST请求。 @POST批注的参数值是终结点,即/posts 。 因此,完整的URL为http://jsonplaceholder.typicode.com/posts

好吧, @FormUrlEncoded呢? 这将指示该请求将其MIME类型(标头字段,用于标识HTTP请求或响应的主体的格式)设置为application/x-www-form-urlencoded ,并且其字段名称和值将为UTF-8编码,然后再进行URI编码。 参数名称为@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注释

我们还可以在服务方法参数上使用@Body批注,而不用指定具有多个单独字段的表单样式请求主体。 该对象将使用创建期间指定的Retrofit实例Converter进行序列化。 仅在执行POSTPUT操作时使用。

@POST("/posts")
@FormUrlEncoded
Call<Post> savePost(@Body Post post);

7.创建API实用程序

我们将创建一个实用程序类。 因此,在data.remote创建一个类,并将其命名为ApiUtils 。 此类将基URL作为静态变量,并且还将通过getAPIService()静态方法为应用程序的其余部分提供APIService接口。

package com.chikeandroid.retrofittutorial2.data.remote;

public class ApiUtils {

    private ApiUtils() {}

    public static final String BASE_URL = "http://jsonplaceholder.typicode.com/";

    public static APIService getAPIService() {

        return RetrofitClient.getClient(BASE_URL).create(APIService.class);
    }
}

确保使用/结束基本URL

8.创建布局

文件activity_main.xmlMainActivity的布局。 此布局将具有一个用于文本标题的文本编辑字段,以及一个用于文本正文的文本编辑字段。 它还包括一个按钮,用于将帖子提交到API。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_post"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        tools:context="com.chikeandroid.retrofittutorial2.AddEditPostActivity">

    <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:textAppearance="@style/TextAppearance.AppCompat.Title"
            android:text="@string/title_enter_post"/>
    <EditText
            android:id="@+id/et_title"
            android:layout_marginTop="18dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/hint_title"/>

    <EditText
            android:id="@+id/et_body"
            android:lines="4"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/hint_body"/>

    <Button
            android:id="@+id/btn_submit"
            android:layout_marginTop="18dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorAccent"
            android:textColor="@android:color/white"
            android:text="@string/action_submit"/>

    <TextView
            android:id="@+id/tv_response"
            android:layout_marginTop="35dp"
            android:visibility="gone"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    
</LinearLayout>

9.执行POST请求

MainActivityonCreate()方法中,我们初始化APIService接口的实例(第14行)。 我们还初始化了EditText字段和一个提交按钮,单击该按钮时将调用sendPost()方法(第22行)。

private TextView mResponseTv;
private APIService mAPIService;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    final EditText titleEt = (EditText) findViewById(R.id.et_title);
    final EditText bodyEt = (EditText) findViewById(R.id.et_body);
    Button submitBtn = (Button) findViewById(R.id.btn_submit);
    mResponseTv = (TextView) findViewById(R.id.tv_response);

    mAPIService = ApiUtils.getAPIService();

    submitBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            String title = titleEt.getText().toString().trim();
            String body = bodyEt.getText().toString().trim();
            if(!TextUtils.isEmpty(title) && !TextUtils.isEmpty(body)) {
                sendPost(title, body);
            }
        }
    });
}

MainActivity类的sendPost(String, String)方法中,我们将帖子的标题和正文传递给了此方法。 该方法将执行我们的API服务接口方法savePost(String, String)该方法的工作是执行POST请求,以将标题和正文发送给API。 showResponse(String response)方法将在屏幕上显示响应。

public void sendPost(String title, String body) {
mAPIService.savePost(title, body, 1).enqueue(new Callback<Post>() {
    @Override
    public void onResponse(Call<Post> call, Response<Post> response) {

        if(response.isSuccessful()) {
            showResponse(response.body().toString());
            Log.i(TAG, "post submitted to API." + response.body().toString());
        }
    }

    @Override
    public void onFailure(Call<Post> call, Throwable t) {
        Log.e(TAG, "Unable to submit post to API.");
    }
});
}

public void showResponse(String response) {
    if(mResponseTv.getVisibility() == View.GONE) {
        mResponseTv.setVisibility(View.VISIBLE);
    }
    mResponseTv.setText(response);
}

我们的APIService实例mAPIService方法savePost(String, String)将返回一个Call实例,该实例具有一个名为enqueue(Callback<T> callback)

了解enqueue()

enqueue()异步发送请求,并在响应返回时通过回调通知您的应用。 由于此请求是异步的,因此Retrofit在后台线程上处理执行,以便不会阻塞或干扰主UI线程。

要使用enqueue()方法,必须实现两个回调方法: onResponse()onFailure() 。 这些方法中只有一种会响应给定的请求而被调用。

  • onResponse() :为收到的HTTP响应调用。 调用此方法是为了即使服务器返回错误消息也可以正确处理响应。 因此,如果状态代码为404或500,则仍将调用此方法。 为了获取状态代码以便您根据情况进行处理,可以使用response.code()方法。 您还可以使用isSuccessful()方法找出状态码是否在200-300范围内,表示成功。
  • onFailure() :在发生与服务器通信的网络异常时,或者在处理请求或处理响应时发生意外异常时调用。
同步请求

要执行同步请求,可以在Call实例中使用execute()方法。 但是请注意,主/ UI线程上的同步方法将阻止任何用户操作。 因此,请勿在Android的main / UI线程上执行同步方法! 而是在后台线程上运行它们。

使用RxJava

RxJava默认情况下已集成到Retrofit 1中,但是在Retrofit 2中,您需要包括一些额外的依赖项。 Retrofit附带有用于执行Call实例的默认适配器。 因此,您可以通过包含RxJava CallAdapter来将Retrofit的执行机制更改为包含CallAdapter 。 这些步骤是:

第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步

构建改造实例时,添加新的CallAdapter RxJavaCallAdapterFactory.create() (第5行)。

public static Retrofit getClient(String baseUrl) {
    if (retrofit==null) {
        retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
    return retrofit;
}
第三步

更新APIService savePost(String title, String body, String userId)方法以使其成为Observable。

@POST("/posts")
@FormUrlEncoded
Observable<Post> savePost(@Field("title") String title,
                          @Field("body") String body,
                          @Field("userId") long userId);
第4步

在发出请求时,我们的匿名订阅者响应可观察对象的流,该流发出事件,在我们的案例中为Post 。 当订户收到任何事件时,将调用onNext方法,然后将其传递给我们的showResponse(String response)方法。

public void sendPost(String title, String body) {

    // RxJava
    mAPIService.savePost(title, body, 1).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<Post>() {
                @Override
                public void onCompleted() {

                }

                @Override
                public void onError(Throwable e) {

                }

                @Override
                public void onNext(Post post) {
                    showResponse(post.toString());
                }
            });
}

10.测试应用

此时,您可以运行该应用程序,并在输入标题和正文后单击“提交”按钮。 API的响应将显示在“提交”按钮下方。

最终应用的屏幕截图

11.执行一个PUT请求

现在我们知道如何执行POST请求,让我们看看如何执行更新实体的PUT请求。 将以下新方法添加到APIService类。

@PUT("/posts/{id}")
@FormUrlEncoded
Call<Post> updatePost(@Path("id") long id,
                      @Field("title") String title,
                      @Field("body") String body,
                      @Field("userId") long userId);

要通过API更新帖子,我们使用端点/posts/{id}其中{id}是要更新的帖子{id}的占位符。 @Path注释是URL路径段{id}的命名替换。 请注意,使用String.valueOf(Object)和URL编码将值转换为字符串。 如果该值已被编码,则可以禁用如下URL编码: @Path(value="name", encoded=true)

12.执行DELETE请求

我们还要看看如何执行DELETE请求。 使用JSONPlaceholder API删除帖子资源,所需端点是HTTP方法DELETE /posts/{id} 。 回到我们的APIService接口,我们只需要包含将执行该方法的deletePost()方法deletePost() 。 我们将帖子的ID传递给方法,然后在URL路径段{id}中将其替换。

@DELETE("/posts/{id}")
Call<Post> deletePost(@Path("id") long id);

13.取消请求

假设您想让用户能够取消或中止请求。 在翻新中这很容易做到。 Retrofit Call类具有一个名为cancel()的方法,该方法可以做到这一点(下面的第32行)。 此方法将在回调中触发onFailure()方法。

例如,如果没有互联网连接,或者在创建请求或处理响应时发生意外异常,则可以调用此方法。 因此,要知道请求是否已取消,请使用Call类中的isCanceled()方法(第20行)。

private Call<Post> mCall;

...

public sendPost(String title, String body) {
    mCall = mAPIService.savePost(title, body, 1);
    mCall.enqueue(new Callback<Post>() {
        @Override
        public void onResponse(Call<Post> call, Response<Post> response) {

            if(response.isSuccessful()) {
                showResponse(response.body().toString());
                Log.i(TAG, "post submitted to API." + response.body().toString());
            }
        }

        @Override
        public void onFailure(Call<Post> call, Throwable t) {

            if(call.isCanceled()) {
                Log.e(TAG, "request was aborted");
            }else {
                Log.e(TAG, "Unable to submit post to API.");
            }
            showErrorMessage();

        }
    });
}

public void cancelRequest() {
    mCall.cancel();
}

结论

在本教程中,您学习了Retrofit:为什么要使用Retrofit,以及如何将其集成到项目中以执行POSTPUTDELETE和取消请求。 您还学习了如何将RxJava与之集成。 在有关使用Retrofit的下一篇文章中,我将向您展示如何上传文件。

翻译自: https://code.tutsplus.com/tutorials/sending-data-with-retrofit-2-http-client-for-android--cms-27845

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值