preface
I recently came up with the idea of doing an in-depth analysis of the main Android open source framework, and then writing a series of articles, including detailed usage and source code analysis of the framework. The purpose is to understand the underlying principle of the framework by appreciating the source code of god, that is, to do not only know it, but also know why.
Here I say their own experience reading source code, I generally in accordance with the peacetime use of a framework or a system source code process, first of all to know how to use, and then go to the bottom of each step to do what, with what good design patterns, why so design.
Series of articles:
- Android mainstream open source framework (a) OkHttp -HttpClient and HttpURLConnection use details
- Android main open source framework (ii) OkHttp usage details
- Android mainstream open source framework (three) OkHttp source code analysis
- Android mainstream open source framework (iv) Retrofit usage details
- Android mainstream open source framework (v) Retrofit source code analysis
- Android mainstream open source framework (six) Glide execution process source code analysis
- More frameworks continue to be updated…
Check out AndroidNotes for more dry stuff
I. Introduction to Retrofit
The previous article has introduced the use of OkHttp and OkHttp source code analysis, do not know the strongly recommended first look at these two articles. This article is about Retrofit, Square’s open source web framework, which is based on OkHttp at its base, but is easier to use than OkHttp and better suited to RESTful API format requests.
Ii. Use of Retrofit
2.1 Preparations before Use
Add network permissions to androidmanifest.xml file as follows:
<uses-permission android:name="android.permission.INTERNET"/>
Copy the code
Since we need to convert the ResponseBody returned from the server into an entity class, we need to add the Gson library dependency as the data parser. Finally add the following dependencies to build.gradle in the currently used Module:
// Retrofit
implementation 'com. Squareup. Retrofit2: retrofit: 2.5.0'
// Gson
implementation 'com. Squareup. Retrofit2: converter - gson: 2.5.0'
Copy the code
2.2 Simple GET Request
This is demonstrated using the GET interface provided by Postman. (Postman Echo)
(1) Create an entity class to receive data from the server:
public class PostmanGetBean {
private String url;
// Omit other fields, see demo for details.
}
Copy the code
(2) Create an interface for defining network requests:
public interface PostmanService {
@GET("get")
Call<PostmanGetBean> testGet(a);
}
Copy the code
As you can see, there is a testGet() method. The annotation @get on top of the method represents the GET request, and the “GET” in the annotation is concatenated with the following baseUrl to form a full path. For example, if baseUrl is https://postman-echo.com/, the complete path is https://postman-echo.com/get. It is recommended that baseUrl end with a/(slash), and that path in annotations do not start with a/(slash), as this is more intuitive. (3) Create an instance of Retrofit:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://postman-echo.com/")// baseUrl
.addConverterFactory(GsonConverterFactory.create())// Parse json data
.build();
Copy the code
(4) Create an instance of the network request interface and Call the method in the interface to obtain the Call object:
PostmanService service = retrofit.create(PostmanService.class);
Call<PostmanGetBean> call = service.testGet();
Copy the code
(5) Make network requests
call.enqueue(new Callback<PostmanGetBean>() {
@Override
public void onResponse(Call<PostmanGetBean> call, Response<PostmanGetBean> response) {
System.out.println(response.body().getUrl());
}
@Override
public void onFailure(Call<PostmanGetBean> call, Throwable t) {}});Copy the code
Print result:
https://postman-echo.com/get
Copy the code
Example source code: retrofitactivity-testgetrequest
Retrofit notes
There are a number of annotations used in Retrofit, which are divided into three categories.
3.1 The first class: network request methods
They are @get, @post, @PUT, @delete, @path, @head, @options and @http. The first seven correspond to the network request methods in HTTP respectively. They all receive a string and baseUrl to form a complete URL. Set via @http annotation. The last @HTTP annotation can be used to replace the previous seven annotations, as well as other extended features. I’ll focus on the @HTTP annotation here. The other annotations are similar to the @GET annotation.
@http annotation example: the @http annotation has three attributes: method, path, and hasBody. It says that this annotation can be used to replace the previous seven annotations, so let’s replace the @get annotation mentioned earlier in the GET request.
Here only need to modify the interface, other unchanged:
public interface PostmanService {
@HTTP(method = "GET", path = "get", hasBody = false)
Call<PostmanGetBean> testHTTP(a);
}
Copy the code
Run result: Same as the @get annotation example. Example source code: retrofitActivity-testhTTp
3.2 Type 2: marks
3.2.1 @ FormUrlEncoded annotation
Summary: Indicates that the request body is a Form. Example: The POST interface provided by Postman is used to demonstrate this.
(1) create entity class:
public class PostmanPostBean {
// Fields are omitted from overriding the toString() method, see demo
}
Copy the code
(2) Create interface:
public interface PostmanService {
@POST("post")
@FormUrlEncoded
Call<PostmanPostBean> testFormUrlEncoded1(@Field("username") String name, @Field("password") String password);
}
Copy the code
As you can see, the @field annotation is used, which is the third type of annotation used to pass key-value pairs to the Post form, where username represents the key and name represents the value.
(3) Initiate a request:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://postman-echo.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
PostmanService service = retrofit.create(PostmanService.class);
Call<PostmanPostBean> call = service.testFormUrlEncoded1("wildma"."123456");
call.enqueue(new Callback<PostmanPostBean>() {
@Override
public void onResponse(Call<PostmanPostBean> call, Response<PostmanPostBean> response) {
System.out.println(response.body().getForm().toString());
}
@Override
public void onFailure(Call<PostmanPostBean> call, Throwable t) {}});Copy the code
Running results:
FormEntity{username='wildma', password='123456'}
Copy the code
Example source code: RetrofitActivity-testFormUrlEncoded1
Pass a Map set: Pass key-value pairs to the Post form. Instead of passing them one by one, you can pass a Map set using the @fieldMap annotation as follows:
(1) Create interface:
public interface PostmanService {
@POST("post")
@FormUrlEncoded
Call<PostmanPostBean> testFormUrlEncoded2(@FieldMap Map<String, Object> map);
}
Copy the code
(3) Initiate a request:
// Omit the instance code to create Retrofit
Map<String, Object> map = new HashMap<>();
map.put("username"."wildma");
map.put("password"."123456");
Call<PostmanPostBean> call = service.testFormUrlEncoded2(map);
// Omit the network request code
Copy the code
Example source code: RetrofitActivity-testFormurlencoded2
3.2.2 @ Multipart annotation
Description: The request body is a Form that supports file uploading. Example: The image upload interface provided by THE YESAPI is used to demonstrate this.
(1) create entity class:
public class UploadImgBean {
// Fields are omitted from overriding the toString() method, see demo
}
Copy the code
(2) Create interface:
public interface FileUploadService {
@POST("? service=App.CDN.UploadImg")
@Multipart
Call<UploadImgBean> testFileUpload1(@Part MultipartBody.Part file, @Part("app_key") RequestBody appKey);
}
Copy the code
As you can see, the @Part annotation is used, which is the third type of annotation for form fields and is suitable for file uploads. Two types of @part are used here, multipartBody. Part means to upload a file, and RequestBody means to upload a key-value pair, where app_key represents the key and appKey represents the value.
(3) Initiate a request:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://hn216.api.yesapi.cn/")
.addConverterFactory(GsonConverterFactory.create())
.build();
RequestBody appKey = RequestBody.create(null."Replace it with the appKey you got on YESAPI.");
// test. PNG is the file in the SD card and directory
File file = new File(Environment.getExternalStorageDirectory(), "test.png");
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);
// Build multipartbody. Part, where file is the key agreed by the server and test. PNG is the file name
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file"."test.png", requestBody);
FileUploadService service = retrofit.create(FileUploadService.class);
Call<UploadImgBean> call = service.testFileUpload1(filePart, appKey);
call.enqueue(new Callback<UploadImgBean>() {
@Override
public void onResponse(Call<UploadImgBean> call, Response<UploadImgBean> response) {
System.out.println(response.body().toString());
}
@Override
public void onFailure(Call<UploadImgBean> call, Throwable t) {}});Copy the code
Running results:
UploadImgBean {ret = 200, data = DataEntity {err_code = 0, err_msg = ", url = "http://cd7.yesapi.net/xxx.png"}, MSG = 'current small white interface: App.CDN.UploadImg'}Copy the code
Example source: retrofitActivity-testFileupload1
Multifile upload: If you want to upload multiple files, you can use @partMap to pass a Map set with
as follows:
(1) Create interface:
public interface FileUploadService {
@POST("? service=App.CDN.UploadImg")
@Multipart
Call<UploadImgBean> testFileUpload2(@PartMap Map<String, RequestBody> map);
}
Copy the code
(1) Initiate a request:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://hn216.api.yesapi.cn/")
.addConverterFactory(GsonConverterFactory.create())
.build();
RequestBody appKey = RequestBody.create(null."Replace it with the appKey you got on YESAPI.");
// test. PNG is the file in the SD card and directory
File file = new File(Environment.getExternalStorageDirectory(), "test.png");
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);
Map<String, RequestBody> requestBodyMap = new HashMap<>();
requestBodyMap.put("app_key", appKey);
// Add a file. File is the key agreed by the server, and test. PNG is the file name
requestBodyMap.put("file\"; filename=\"test.png", requestBody);
// If there are more files, continue putting ()...
FileUploadService service = retrofit.create(FileUploadService.class);
Call<UploadImgBean> call = service.testFileUpload2(requestBodyMap);
call.enqueue(new Callback<UploadImgBean>() {
@Override
public void onResponse(Call<UploadImgBean> call, Response<UploadImgBean> response) {
System.out.println(response.body().toString());
}
@Override
public void onFailure(Call<UploadImgBean> call, Throwable t) {}});Copy the code
Example source: retrofitActivity-testFileupload2
3.2.3 @ Streaming annotation
Summary: The data representing the response body is returned as a stream. If this annotation is not used, the data will be loaded into memory by default, and then the data will be retrieved from memory. Therefore, this annotation is usually used when returning large data, such as downloading a large file. Example: the head is used here to download my blog (wildma. Making. IO/medias avat…). Demonstrate.
[ResponseBody] [ResponseBody] [ResponseBody] [ResponseBody] (For the sake of demonstration, the following example will not parse into an entity class and will use ResponseBody directly to receive the raw data returned by the server.)
(2) Create interface:
public interface FileDownloadService {
@Streaming
@GET("medias/avatars/avatar.jpg")
Call<ResponseBody> testFileDownload(a);
}
Copy the code
The @Streaming annotation is used here to indicate that the data in the response body is returned as a stream.
(3) Initiate a request:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://wildma.github.io/")
.addConverterFactory(GsonConverterFactory.create())
.build();
FileDownloadService service = retrofit.create(FileDownloadService.class);
Call<ResponseBody> call = service.testFileDownload();
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
InputStream is = response.body().byteStream();
// Save the file...
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {}});Copy the code
Example source code: retrofitactivity-testFileDownload
3.3 Type 3: Network request parameters
3.3.1 @header, @headers and @HeaderMap annotations
Introduction: @header and @headermap are used to add a request Header with a fixed value, and @headers is used to add a request Header with a fixed value. @header and @headermap are passed in as parameters to the request method, and @headers is added directly to the request method. Example:
// @Header
@GET("headers")
Call<ResponseBody> testHeader(@Header("token") String token);
// @Headers
@Headers("token: 123")
@GET("headers")
Call<ResponseBody> testHeaders(a);
// @headers Multiple request Headers
@Headers({"token: 123", "sign: 456"})
@GET("headers")
Call<ResponseBody> testHeaders2(a);
// @HeaderMap
@GET("headers")
Call<ResponseBody> testHeaderMap(@HeaderMap Map<String, String> map);
Copy the code
Retrofitactivity-testheader (), testHeaders(), testHeaders2()
3.3.2 rainfall distribution on 10-12 @ Body annotation
Summary: @body is used for non-form request Body. The @body annotation can be easily implemented because it can be passed directly to an entity class, which will be converted into a JSON string request Body during the request process. (1) Create interface:
public interface PostmanService {
@POST("post")
Call<ResponseBody> testBody(@Body TestBodyBean testBodyBean);
}
Copy the code
(2) Initiate a request:
// Omit the instance code to create Retrofit
TestBodyBean bean = new TestBodyBean();
bean.setUsername("wildma");
bean.setPassword("123456");
PostmanService service = retrofit.create(PostmanService.class);
Call<ResponseBody> call = service.testBody(bean);
// Omit the network request code
Copy the code
Retrofitactivity-testbody ()
3.3.3 @field and @fieldMap annotations
Summary: Used to pass key-value pairs to the Post form. Example: The use of the @formurlencoded annotation was explained earlier. Retrofitactivity-testformurlencoded1 (), testFormUrlEncoded2()
3.3.4 @Part and @partMap annotations
Description: Used for form fields, applicable to file uploads. Example: The specific use of the @multipart annotation was covered earlier. Retrofitactivity-testfileupload1 (), testFileUpload2()
3.3.5 @query and @queryMap annotations
Description: Used for form fields, the function is the same as @field and @FiledMap, the difference is that @Query and @QueryMap data is reflected in the URL, and @field and @FiledMap data is reflected in the request body, but the generated data is the same. (1) Create interface:
public interface PostmanService {
@GET("get")
Call<ResponseBody> testQuery(@Query("username") String username);
}
Copy the code
(2) Initiate a request:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://postman-echo.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
PostmanService service = retrofit.create(PostmanService.class);
Call<ResponseBody> call = service.testQuery("wildma");
// Omit the network request code
Copy the code
The above baseUrl is https://postman-echo.com/, part of the URL in the @get annotation is “GET”, and the final full URL should be https://postman-echo.com/get if the @query annotation is not used, With annotations, it becomes https://postman-echo.com/get?username=wildma.
The @queryMap annotation corresponds to the Map collection with the following interface:
public interface PostmanService {
@GET("get")
Call<ResponseBody> testQueryMap(@QueryMap Map<String, String> params);
}
Copy the code
The code that initiates the request will not be posted, just pass in a corresponding Map collection.
Retrofitactivity-testquery (), testQueryMap()
3.3.6 @ QueryName annotation
Brief introduction: This annotation is rarely used in actual projects. Its function is similar to that of @Query and @QueryMap. Parameters are all concatenated in THE URL, but @Query and @QueryMap are concatenated in the URL with key-value pairs, while @QueryName is only concatenated with keys. There is no value. (1) Create interface:
public interface PostmanService {
@GET("get")
Call<ResponseBody> testQueryName(@QueryName String... filters);
}
Copy the code
Annotations can be followed by String filter or String… Filters, where the latter is a variable length parameter that can pass multiple parameters or none. (2) Initiate a request:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://postman-echo.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
PostmanService service = retrofit.create(PostmanService.class);
Call<ResponseBody> call = service.testQueryName("wildma"."tom");
// Omit the network request code
Copy the code
The final concatenated URL above is https://postman-echo.com/get?wildma&tom.
Retrofitactivity-testqueryname ()
3.3.7 @ Path annotation
Description: @path is used to set the default URL address. Example: This is demonstrated using the official API, which is to get a list of warehouses for the specified user. (1) Create interface:
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<RepoBean>> listRepos(@Path("user") String user);
}
Copy the code
(2) Initiate a request:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
Call<ResponseBody> call = service.testPath("wildma");
// Omit the network request code
Copy the code
As you can see, the @get annotation has a “{user}” in the “users/{user}/repos”, which is the default URL address, The @path (“user”) String user in the listRepos() method indicates that the urSE passed is used to replace {user} above. So it’s the complete URL to https://api.github.com/users/wildma/repos.
Retrofitactivity-testpath ()
3.3.8 @ Url annotation
Description: @Url is used to dynamically set a complete Url. (1) Create interface:
public interface PostmanService {
@GET()
Call<ResponseBody> testUrl(@Url String url);
}
Copy the code
(2) Initiate a request:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
PostmanService service = retrofit.create(PostmanService.class);
Call<ResponseBody> call = service.testUrl("https://postman-echo.com/get");
// Omit the network request code
Copy the code
As you can see, both baseUrl() and testUrl() have a URL set, but since the @url annotation is dynamically set, the URL set in testUrl() prevails. The result is https://postman-echo.com/get.
Retrofitactivity-testurl ()
Set up a custom OkHttpClient
When creating an instance of Retrofit, you can set up a custom OkHttpClient using the client() method, which can set uniform headers, add log interceptors, cookies, and so on. Here’s how to set a unified header. (1) Create OkHttpClient by adding an interceptor and setting a uniform header in the intercept() method:
OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request request = originalRequest.newBuilder()
.header("token"."123")
.header("sign"."456")
.build();
return chain.proceed(request);
}
}).build();
Copy the code
(2) Set a custom OkHttpClient using the client() method:
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)// Set up a custom OkHttpClient
.baseUrl("https://postman-echo.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
Copy the code
Example source code: RetrofitActivity-testCustomokHttpClient
Fifth, About Converter
Retrofit uses ResponseBody by default to receive data returned by the server. If you want to convert to the corresponding entity class, you can set up a data parser when creating an instance of Retrofit using the addConverterFactory() method. There are many options for data parsers, and Retrofit documentation provides many of them:
- Gson: com.squareup.retrofit2:converter-gson
- Jackson: com.squareup.retrofit2:converter-jackson
- Moshi: com.squareup.retrofit2:converter-moshi
- Protobuf: com.squareup.retrofit2:converter-protobuf
- Wire: com.squareup.retrofit2:converter-wire
- Simple XML: com.squareup.retrofit2:converter-simplexml
- Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars
In addition to the documents provided by these, but there is a kind of commonly used: fastjson: ‘org. Ligboy. Retrofit2: converter – fastjson – android
This is demonstrated using Gson. (1) Create interface:
public interface PostmanService {
@GET("get")
Call<PostmanGetBean> testGet(a);
}
Copy the code
Here we directly replace ResponseBody with the entity class PostmanGetBean. (2) Initiate a request:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://postman-echo.com/")
.addConverterFactory(GsonConverterFactory.create())// Add a Gson parser
.build();
// Omit the network request code
Copy the code
Here Gson is added as the data parser.
Example source code: retrofitactivity-testget
6. About CallAdapter
If you want to return any other type, you can set up a CallAdapter when creating an instance of Retrofit by using the addCallAdapterFactory() method. Retrofit provides the following Calladapters:
- Guava: com. Squareup. Retrofit2: adapter – guava
- Java8: com. Squareup. Retrofit2: adapter – Java8:2.0.2
- Rxjava: com. Squareup. Retrofit2: adapter – rxjava
RxJava is used to demonstrate this. (1) Add related dependencies:
/ / support rxjava2
implementation 'com. Squareup. Retrofit2: adapter - rxjava2:2.5.0'
// rxjava2
compile 'the IO. Reactivex. Rxjava2: rxjava: 2.2.13'
compile 'the IO. Reactivex. Rxjava2: rxandroid: 2.1.1'
Copy the code
(2) Create interface:
@GET("get")
Observable<ResponseBody> testCallAdapter(a);
Copy the code
This replaces Call with an Observable. (3) Initiate a request:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://postman-echo.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())// Set RxJava as the current CallAdapter
.build();
PostmanService service = retrofit.create(PostmanService.class);
Observable<ResponseBody> observable = service.testCallAdapter();
observable.subscribeOn(Schedulers.io()) // Network requests are made in the IO thread
.observeOn(AndroidSchedulers.mainThread()) // Process the result of the request in the main thread
.subscribe(new Observer<ResponseBody>() {
@Override
public void onSubscribe(Disposable d) {}@Override
public void onNext(ResponseBody responseBody) {
try {
System.out.println(responseBody.string());
} catch(IOException e) { e.printStackTrace(); }}@Override
public void onError(Throwable e) {}@Override
public void onComplete(a) {}});Copy the code
Set RxJava as the current CallAdapter and call Observable methods to handle network requests and request results. Example source code: retrofitActivity-testCallAdapter
Seven, the source code
Use of Retrofit demo
About me
I am Wildma, CSDN certified blog expert, excellent author of Simple book programmer, good at screen adaptation. If the article is helpful to you, a “like” is the biggest recognition for me!