1. Overview
1.概述
Retrofit is a type-safe HTTP client for Android and Java – developed by Square (Dagger, Okhttp).
Retrofit是一个适用于Android和Java的类型安全的HTTP客户端 – 由Square(Dagger,Okhttp)开发。
In this article, we’re going to explain how to use Retrofit, with a focus on its most interesting features. More notably we’ll discuss the synchronous and asynchronous API, how to use it with authentication, logging, and some good modeling practices.
在这篇文章中,我们将解释如何使用Retrofit,重点是它最有趣的功能。更值得注意的是,我们将讨论同步和异步的API,如何使用它的认证、日志和一些良好的建模实践。
2. Setting up the Example
2.设置实例
We’ll start by adding the Retrofit library and the Gson converter:
我们将首先添加Retrofit库和Gson转换器。
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-gson</artifactId>
<version>2.3.0</version>
</dependency>
For the latest versions, have a look at Retrofit and converter-gson on Maven Central repository.
关于最新版本,请查看Maven Central资源库中的Retrofit和converter-gson。
3. API Modeling
3.API建模
Retrofit models REST endpoints as Java interfaces, making them very simple to understand and consume.
Retrofit将REST端点建模为Java接口,使其非常容易理解和使用。
We’ll model the user API from GitHub; this has a GET endpoint that returns this in JSON format:
我们将对GitHub的用户API进行建模;它有一个GET端点,以JSON格式返回。
{
login: "mojombo",
id: 1,
url: "https://api.github.com/users/mojombo",
...
}
Retrofit works by modeling over a base URL and by making interfaces return the entities from the REST endpoint.
Retrofit的工作方式是在一个基本的URL上建模,并使接口从REST端点返回实体。
For simplicity purposes we’re going to take a small part of the JSON by modeling our User class that is going to take the values when we have received them:
为了简单起见,我们将通过对我们的User类进行建模来获取JSON的一小部分,当我们收到这些值时,我们将接受这些值。
public class User {
private String login;
private long id;
private String url;
// ...
// standard getters an setters
}
We can see that we’re only taking a subset of properties for this example. Retrofit won’t complain about missing properties – since it only maps what we need, it won’t even complain if we were to add properties that are not in the JSON.
我们可以看到,在这个例子中,我们只取了一个属性的子集。Retrofit 不会抱怨丢失的属性 – 因为它只映射我们需要的东西,如果我们要添加 JSON 中没有的属性,它甚至不会抱怨。
Now we can move to the interface modeling, and explain some of the Retrofit annotations:
现在我们可以转到界面建模,并解释一些Retrofit注释。
public interface UserService {
@GET("/users")
public Call<List<User>> getUsers(
@Query("per_page") int per_page,
@Query("page") int page);
@GET("/users/{username}")
public Call<User> getUser(@Path("username") String username);
}
The metadata provided with annotations is enough for the tool to generate working implementations.
与注释一起提供的元数据足以让工具产生工作实现。
The @GET annotation tells the client which HTTP method to use and on which resource, so for example, by providing a base URL of “https://api.github.com” it will send the request to “https://api.github.com/users”.
@GET注解告诉客户端要使用哪种HTTP方法和哪种资源,因此,例如,通过提供一个 “https://api.github.com “的基本URL,它将把请求发送到 “https://api.github.com/users”。
The leading “/” on our relative URL tells Retrofit that it is an absolute path on the host.
我们的相对 URL 上的前导”/”告诉 Retrofit 它是主机上的一个绝对路径。
Another thing to note is that we use completely optional @Query parameters, which can be passed as null if we don’t need them, the tool will take care of ignoring these parameters if they do not have values.
另外需要注意的是,我们使用了完全可选的@Query参数,如果我们不需要这些参数,可以把它们传成null,如果这些参数没有值,工具会负责忽略它们。
And last but not least, @Path lets we specify a path parameter that will be placed instead of the markup we used in the path.
最后但并非最不重要的是,@Path让我们指定一个路径参数,它将代替我们在路径中使用的标记被放置。
4. Synchronous/Asynchronous API
4.同步/异步的API
To construct an HTTP request call, we need to build our Retrofit object first:
为了构建一个HTTP请求调用,我们需要首先构建我们的Retrofit对象。
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build();
Retrofit provides a convenient builder for constructing our required object. It needs the base URL which is going to be used for every service call and a converter factory – which takes care of the parsing of data we’re sending and also the responses we get.
Retrofit 提供了一个方便的构建器来构建我们所需的对象。它需要一个基本的URL,这个URL将被用于每次服务调用和一个转换器工厂–它负责解析我们发送的数据以及我们得到的响应。
In this example, we’re going to use the GsonConverterFactory, which is going to map our JSON data to the User class we defined earlier.
在这个例子中,我们将使用GsonConverterFactory,它将把我们的JSON数据映射到我们之前定义的User类。
It’s important to note that different factories serve different purposes, so keep in mind that we can also use factories for XML, proto-buffers or even create one for a custom protocol. For a list of already implemented factories, we can have a look here.
值得注意的是,不同的工厂有不同的用途,所以请记住,我们也可以为XML、proto-buffers使用工厂,甚至为自定义协议创建一个工厂。关于已经实现的工厂的列表,我们可以看一下这里。
The last dependency is OKHttpClient – which is an HTTP & HTTP/2 client for Android and Java applications. This is going to take care of connecting to the server and the sending and retrieval of information. We could also add headers and interceptors for every call, which we’re going to see in our authentication section.
最后一个依赖是OKHttpClient – 这是一个用于Android和Java应用程序的HTTP和HTTP/2客户端。它将负责与服务器的连接以及信息的发送和检索。我们还可以为每次调用添加头信息和拦截器,我们将在认证部分看到这些。
Now that we have our Retrofit object, we can construct our service call, let’s take a look at how to do this the synchronous way:
现在我们有了Retrofit对象,我们可以构建我们的服务调用,让我们来看看如何以同步的方式完成这个任务。
UserService service = retrofit.create(UserService.class);
Call<User> callSync = service.getUser("eugenp");
try {
Response<User> response = callSync.execute();
User user = response.body();
} catch (Exception ex) { ... }
Here, we can see how Retrofit takes care of the construction of our service interface by injecting the code necessary to make the request, based on our previous annotations.
在这里,我们可以看到Retrofit是如何根据我们之前的注释,通过注入发出请求所需的代码,来处理我们的服务接口的构建。
After that, we get a Call<User> object which is the one used for executing the request to the GitHub API. The most important method here is execute, which is used to execute a call synchronously and will block the current thread while transferring the data.
之后,我们得到一个Call<User>对象,该对象用于执行对GitHub API的请求。这里最重要的方法是execute,它用于同步执行一个调用,在传输数据时将阻塞当前线程。
After the call is executed successfully, we can retrieve the body of the response – already on a user object – thanks to our GsonConverterFactory.
在调用成功执行后,我们可以检索响应的主体–已经在一个用户对象上–这要感谢我们的GsonConverterFactory。
Making a synchronous call is very easy, but usually, we use a non-blocking asynchronous request:
进行同步调用是非常容易的,但通常情况下,我们使用非阻塞的异步请求。
UserService service = retrofit.create(UserService.class);
Call<User> callAsync = service.getUser("eugenp");
callAsync.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
User user = response.body();
}
@Override
public void onFailure(Call<User> call, Throwable throwable) {
System.out.println(throwable);
}
});
Now instead of the execute method, we use the enqueue method – which takes a Callback<User>interface as a parameter to handle the success or failure of the request. Note that this will execute in a separate thread.
现在,我们使用enqueue方法来代替execute方法–它接收一个Callback<User>接口作为参数来处理请求的成功或失败。注意,这将在一个单独的线程中执行。
After the call finished successfully, we can retrieve the body the same way we did previously.
调用成功后,我们可以按照之前的方法来检索主体。
5. Making a Reusable ServiceGenerator Class
5.制作一个可重复使用的ServiceGenerator类
Now that we saw how to construct our Retrofit object and how to consume an API, we can see that we don’t want to keep writing the builder over and over again.
现在我们看到了如何构建我们的Retrofit对象以及如何消费一个API,我们可以看到我们并不希望不断地重复编写构建程序。
What we want is a reusable class that allows us to create this object once and reuse it for the lifetime of our application:
我们想要的是一个可重用的类,它允许我们创建一次这个对象,并在我们的应用程序的生命周期内重用它。
public class GitHubServiceGenerator {
private static final String BASE_URL = "https://api.github.com/";
private static Retrofit.Builder builder
= new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
private static Retrofit retrofit = builder.build();
private static OkHttpClient.Builder httpClient
= new OkHttpClient.Builder();
public static <S> S createService(Class<S> serviceClass) {
return retrofit.create(serviceClass);
}
}
All the logic of creating the Retrofit object is now moved to this GitHubServiceGenerator class, this makes it a sustainable client class which stops the code from repeating.
所有创建Retrofit对象的逻辑现在都被转移到这个GitHubServiceGenerator类中,这使得它成为一个可持续发展的客户端类,从而阻止代码的重复。
Here’s a simple example of how to use it :
下面是一个如何使用它的简单例子。
UserService service
= GitHubServiceGenerator.createService(UserService.class);
Now if we, for example, were to create a RepositoryService, we could reuse this class and simplify the creation.
现在,如果我们要创建一个RepositoryService,我们可以重用这个类并简化创建。
In the next section, we’re going to extend it and add authentication capabilities.
在下一节中,我们将对其进行扩展,并增加认证功能。
6. Authentication
6.身份验证
Most APIs have some authentication to secure access to it.
大多数API都有一些认证,以确保对它的访问。
Taking into account our previous generator class, we’re going to add a create service method, that takes a JWT token with the Authorization header :
考虑到我们之前的生成器类,我们将添加一个创建服务的方法,它需要一个带有Authorization头的JWT令牌。
public static <S> S createService(Class<S> serviceClass, final String token ) {
if ( token != null ) {
httpClient.interceptors().clear();
httpClient.addInterceptor( chain -> {
Request original = chain.request();
Request request = original.newBuilder()
.header("Authorization", token)
.build();
return chain.proceed(request);
});
builder.client(httpClient.build());
retrofit = builder.build();
}
return retrofit.create(serviceClass);
}
To add a header to our request, we need to use the interceptor capabilities of OkHttp; we do this by using our previously define builder and by reconstructing the Retrofit object.
为了给我们的请求添加一个头,我们需要使用OkHttp的拦截器功能;我们通过使用我们之前定义的构建器和重构Retrofit对象来做到这一点。
Note that this a simple auth example, but with the use of interceptors we can use any authentication such as OAuth, user/password, etc.
请注意,这是一个简单的认证例子,但通过使用拦截器,我们可以使用任何认证,如OAuth、用户/密码等。
7. Logging
7.登录
In this section, we’re going to further extend our GitHubServiceGenerator for logging capabilities, which are very important for debugging purposes in every project.
在这一节中,我们将进一步扩展我们的GitHubServiceGenerator的日志功能,这对每个项目的调试目的非常重要。
We’re going to use our previous knowledge of interceptors, but we need an additional dependency, which is the HttpLoggingInterceptor from OkHttp, let us add it to our pom.xml:
我们将使用之前关于拦截器的知识,但我们需要一个额外的依赖,这就是来自OkHttp的HttpLoggingInterceptor,让我们把它添加到我们的pom.xml。
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>3.9.0</version>
</dependency>
Now let us extend our GitHubServiceGenerator class :
现在让我们扩展我们的GitHubServiceGenerator类。
public class GitHubServiceGenerator {
private static final String BASE_URL = "https://api.github.com/";
private static Retrofit.Builder builder
= new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
private static Retrofit retrofit = builder.build();
private static OkHttpClient.Builder httpClient
= new OkHttpClient.Builder();
private static HttpLoggingInterceptor logging
= new HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BASIC);
public static <S> S createService(Class<S> serviceClass) {
if (!httpClient.interceptors().contains(logging)) {
httpClient.addInterceptor(logging);
builder.client(httpClient.build());
retrofit = builder.build();
}
return retrofit.create(serviceClass);
}
public static <S> S createService(Class<S> serviceClass, final String token) {
if (token != null) {
httpClient.interceptors().clear();
httpClient.addInterceptor( chain -> {
Request original = chain.request();
Request.Builder builder1 = original.newBuilder()
.header("Authorization", token);
Request request = builder1.build();
return chain.proceed(request);
});
builder.client(httpClient.build());
retrofit = builder.build();
}
return retrofit.create(serviceClass);
}
}
This is the final form of our class, we can see how we added the HttpLoggingInterceptor, and we set it for basic logging, which is going to log the time it took to make the request, the endpoint, status for every request, etc.
这是我们类的最终形式,我们可以看到我们是如何添加HttpLoggingInterceptor的,我们将其设置为基本的日志记录,它将记录发出请求的时间、端点、每个请求的状态等等。
It’s important to take a look at how we check if the interceptor exists, so we don’t accidentally add it twice.
看看我们是如何检查拦截器是否存在的,这很重要,这样我们就不会不小心把它加进去两次。
8. Conclusion
8.结论
In this extensive guide, we took a look at the excellent Retrofit library by focusing on its Sync/Async API, some best practices of modeling, authentication, and logging.
在这个广泛的指南中,我们通过关注它的同步/同步API、建模的一些最佳实践、认证和日志,对优秀的Retrofit库进行了考察。
The library can be used in very complex and useful ways; for an advanced use case with RxJava, please take a look at this tutorial.
该库可以以非常复杂和有用的方式使用;关于RxJava的高级使用案例,请看本教程。
And, as always, the source code can be found over on GitHub.
而且,像往常一样,可以在GitHub上找到源代码。