Retrofit Tutorial

(Source Code Tutorial): https://www.jianshu.com/p/0c055ad46b6c

text

Introduction to Retrofit
        Rotrofit is an encapsulation of a RESTful HTTP network request framework. The network request work is essentially done by Okhttp , and Retrofit is only responsible for the encapsulation of the network request interface. The Retrofit interface layer encapsulates requestParams, Header, Url and other information, and the subsequent request work is completed by OkHttp. After the server returns the data, OkHttp will return the result to Retrofit, and Retrofit will analyze the result according to the user's needs.
public interface GetRequest_Interface {
    @GET("openapi.do?keyfrom=Yanzhikai&key=2032414398&type=data&doctype=json")
    Call<Translation>  getCall();
    // @GET注解的作用:采用Get方法发送网络请求
    // getCall() = 接收网络请求数据的方法
    // 其中返回类型为Call<*>,*是接收数据的类(即上面定义的Translation类)
    // 如果想直接获得Responsebody中的内容,可以定义网络请求返回值为Call<ResponseBody>
}
Comparison of Retrofit and other frameworks
Composition of URLs in Retrofit
Url consists of BaseUrl and path. BaseUrl is set when Rotrofit is instantiated. path is set in the interface description.
BaseUrl = “ http://host:port/aaa/ ”;
path =  “pathname ”;
Retrofit annotation type
1. Annotate the network request method
2. Annotation---marker class
3. Note --- network request parameters
Detailed Retrofit annotations
1. Request method --- Annotation
1.1 The role of @HTTP annotation
@HTTP annotations can replace @GET, @POST, @PUT, @DELETE, @HEAD annotations, and more functional extensions.
public interface GetRequest_Interface {
    /**
     * method:网络请求的方法(区分大小写)
     * path:网络请求地址路径
     * hasBody:是否有请求体
     */
    @HTTP(method = "GET", path = "blog/{id}", hasBody = false)
    Call<ResponseBody> getCall(@Path("id") int id);
    // {id} 表示是一个变量
    // method的值,retrofit不会做检查处理,所以要自行保证准确
}
2. Marker class --- Annotation
2.1 @FormUrlEncode
    @FormUrlEncode indicates that the request body is a Form form, and the key name of each key-value pair needs to be annotated with @Filed , and the subsequent object needs to provide the value of the key-value pair. It is used for Post requests and cannot be used for Get requests because there is no request body in Get requests.
public interface GetRequest_Interface {
    /**
     * @FormUrlEncoded表明是一个表单格式的请求(application/x-www-form-urlencoded)
     * Field("username") 表示将后面的String name 中name的取值作为 username 的值
     */
     @POST("/form")
     @FormUrlEncoded
     Call<ResponseBody> testFormUrlEncoded(
        @Field("username") String name,
        @Field("age") int age
     );
}

//具体使用
GetRequest_Interface service = retrofit.create(GetRequest_Interface.class);
Call<ResponseBody> call1 = service.testFormUrlEncoded("Carson", 24);

2.2@Multipart

    @Multipart indicates that the request body is a form for file upload, and @Part needs to be used to annotate the key name of each key-value pair, and the subsequent object needs to provide the value of the key-value pair.
public interface GetRequest_Interface {
   /**
    * @Part后面支持三种类型:RequestBody、okhttp3.MultipartBody.Part、任意类型;
    * 除了okhttp3.MultipartBody.Part类型以外,其它类型都必须带上表单字段,
    * okhttp3.MultipartBody.Part中已经包含了表单字段的信息。
    */
    @POST("/form")
    @Multipart
    Call<ResponseBody> testFileUpload(
        @Part("name") RequestBody name, 
        @Part("age") RequestBody age, 
        @Part MultipartBody.Part file
    );
}

// 具体使用
GetRequest_Interface mService = retrofit.create(GetRequest_Interface.class);
MediaType textType = MediaType.parse("text/plain");
RequestBody name = RequestBody.create(textType, "Carson");
RequestBody age = RequestBody.create(textType, "24");
RequestBody file = RequestBody
                .create(MediaType.parse("application/octet-stream"), "这里是模拟文件的内容");
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);
Call<ResponseBody> call3 = mService.testFileUpload(name, age, filePart);
3. Network request parameters --- Notes
3.1 @Headers
@Headers is used to statically add request headers in the description interface
public interface GetRequest_Interface {
    @Headers(
        "Authorization: authorization”,
        "Accept: application/vnd.yourapi.v1.full+json",
        "User-Agent: Your-App-Name"
    )
    @GET("user")
    Call<User> getUser()
}
3.2  @Header
@Header is used to dynamically add request headers , that is, the request headers that need to be added will be passed in as parameters
public interface GetRequest_Interface {
    @GET("user")
    Call<User> getUser(
        @Header("Authorization") String authorization
    )
}
3.3 @Body
@Body is used for non-form request bodies and submits custom data types in Post mode . But if a Map collection is submitted, then @Body is equivalent to @Field, but at this time the map needs to be processed by FormBody.Builder into a form that conforms to Okhttp.
public interface GetRequest_Interface {
    @FormUrlEncoded
    @POST("book/reviews")
    Call<String> addReviews(@Body Reviews reviews);
}

public class Reviews {
    public String book;
    public String title;
    public String content;
    public String rating;
}

//具体使用
Reviews reviews = new Reviews();
reviews.setBook(“百科全书”);
reviews.setTitle(“标题”);
reviews.setContent(“描述内容”);
reviews.setRating(“hello!”);
Call<ResponseBody> call = service.addReviews(reviews);

//Map处理过程
FormBody.Builder builder = new FormBody.Builder();
builder.add("key","value");
3.4 @Field & @FieldMap
@Field and FieldMap are used in conjunction with the tag class @FormUrlEncode as form fields for submitting request parameters when sending Post requests.
public interface GetRequest_Interface {
    /**
     * 表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
     * Field("username")表示将后面的String name中name的取值作为 username 的值
     */
    @POST("/form")
    @FormUrlEncoded
    Call<ResponseBody> testFormUrlEncoded1(
        @Field("username") String name,
        @Field("age") int age
    );

    /**
     * Map的key作为表单的键
     */
    @POST("/form")
    @FormUrlEncoded
    Call<ResponseBody> testFormUrlEncoded2(
        @FieldMap Map<String, Object> map
    );
}

//具体使用
// @Field
Call<ResponseBody> call1 = service.testFormUrlEncoded1("Carson", 24);
// @FieldMap
// 实现的效果与上面相同,但要传入Map
Map<String, Object> map = new HashMap<>();
map.put("username", "Carson");
map.put("age", 24);
Call<ResponseBody> call2 = service.testFormUrlEncoded2(map);
3.5 @Part & @PartMap
@Part and @PartMap are used in conjunction with the tag class @Multipart as a form field for submitting request parameters when sending a Post request. Suitable for file upload scenarios.
public interface GetRequest_Interface {
    /**
     * @Part后面支持三种类型:RequestBody、 okhttp3.MultipartBody.Part、任意类型:
     * 除了okhttp3.MultipartBody.Part以外,其它类型都必须带上表单字段,
     * okhttp3.MultipartBody.Part中已经包含了表单字段的信息。
     */
    @POST("/form")
    @Multipart
    Call<ResponseBody> testFileUpload1(
        @Part("name") RequestBody name, 
        @Part("age") RequestBody age,
        @Part MultipartBody.Part file
    );
    /**
     * PartMap 注解支持一个Map作为参数,支持RequestBody类型,
     * 如果有其它的类型,会被retrofit2.Converter转换,如后面会介绍的,
     * 使用com.google.gson.Gson的retrofit2.converter.gson.GsonRequestBodyConverter
     * 所以MultipartBody.Part就不适用了,所以文件只能用@Part MultipartBody.Part
     */
    @POST("/form")
    @Multipart
    Call<ResponseBody> testFileUpload2(
        @PartMap Map<String, RequestBody> args, 
        @Part MultipartBody.Part file
    );
}

// 具体使用
MediaType textType = MediaType.parse("text/plain");
RequestBody name = RequestBody.create(textType, "Carson");
RequestBody age = RequestBody.create(textType, "24");
RequestBody file = RequestBody
                .create(MediaType.parse("application/octet-stream"), "这里是模拟文件的内容");
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);

// @Part
Call<ResponseBody> call3 = service.testFileUpload1(name, age, filePart);
ResponseBodyPrinter.printResponseBody(call3);

// @PartMap
// 实现和上面同样的效果
Map<String, RequestBody> fileUpload2Args = new HashMap<>();
fileUpload2Args.put("name", name);
fileUpload2Args.put("age", age);
Call<ResponseBody> call4 = service.testFileUpload2(fileUpload2Args, filePart); 
// 单独处理文件
ResponseBodyPrinter.printResponseBody(call4);
3.6 @Query & @QueryMap
@Query and @QueryMap are used for the query parameters of the @Get method (the key-value after Query = Url?)
public interface GetRequest_Interface {
    @GET("book/search")    
    Call<ResponseBody> cate1(
        @Query("username") String username,
        @Query(“age”) int age
    );
   
    @GET("book/search")
    Call<ResponseBody> cate2(
        @QueryMap Map<String, Object> map
    );

    Call<ResponseBody> cate3(
        @Query("list") List<String> strList,
    );
}

//具体使用
// @Query
Call<ResponseBody> call1 = service.cate1("Carson",19);
// @QueryMap
// 实现的效果与上面相同,但要传入Map
Map<String, Object> map = new HashMap<>();
map.put("username", "Carson");
map.put("age", 24);
Call<ResponseBody> call2 = service.cate2(map);
3.7 @Path
@Path is used as the default value for Url address. Can be used in Post, Get, Put, Delete
public interface GetRequest_Interface {
    @GET("users/{user}/repos")
    Call<ResponseBody> getBlog(@Path("user") String user );
    // 访问的API是:https://api.github.com/users/{user}/repos
    // 在发起请求时, {user} 会被替换为方法的第一个参数 user(被@Path注解作用)
}

//具体使用
Call<ResponseBody> call = service.getBlog(“carson”);
//则描述的接口为:“users/carson/repos”。
3.8 @Url
@Url is used to directly pass in a request Url variable for Url setting when making a network request.
public interface GetRequest_Interface {
    @GET
    Call<ResponseBody> testUrlAndQuery(
        @Url String url, 
        @Query("showAll") boolean showAll
    );
    // 当@HTTP、@POST、@GET中设置了path时,@GET传入的URL就可以省略
    // 当GET、POST...HTTP等方法中没有设置Url时,则必须使用 {@link Url}提供
}

//具体使用
Url url = “http://www.baidu.com/abc”
Call<ResponseBody> call = service.testUrlAndQuery(url, true);
//则描述的接口为:“http://www.baidu.com/abc?showAll=true”。

Custom Interceptor interceptor

Create a custom Interceptor interceptor, and then add it through addInterceptor() where the Retrofit instance is created, through which we can implement some interception operations, such as the following: We want to intercept every request and add a public request parameter .
//自定义拦截器——添加公共参数
public class CustomInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        HttpUrl httpUrl = request.url().newBuilder()
                .addQueryParameter("token", "tokenValue")
                .build();
        request = request.newBuilder().url(httpUrl).build();
        return chain.proceed(request);
    }
}

//自定义拦截器—添加 header
//添加header参数Request提供了两个方法,一个是header(key, value),
//另一个是.addHeader(key, value),两者的区别是,header()如果有重名的将会覆盖,
//而addHeader()允许相同key值的header存在
public class RequestInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request original = chain.request();
        Request request = original.newBuilder()
            .header("User-Agent", "Your-App-Name")
            .header("Accept", "application/vnd.yourapi.v1.full+json")
            .method(original.method(), original.body())
            .build();
        return chain.proceed(request);
    }
}

//添加拦截器Interceptor
private static OkHttpClient getNewClient(){
  return new OkHttpClient.Builder()
    .addInterceptor(new CustomInterceptor())
    .addInterceptor(new RequestInterceptor())
    .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
    .build();
}
Retrofit officially provides an Interceptor interceptor that is convenient for viewing logs, and it is convenient to control the type of printed information. HttpLoggingInterceptor provides 4 levels of control printing information types, namely: NONE, BASIC, HEADERS, BODY.
//引入依赖
implement 'com.squareup.okhttp3:logging-interceptor:3.4.1’
//具体使用
private static OkHttpClient getNewClient(){
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);
    return new OkHttpClient.Builder()
           .addInterceptor(logging)
           .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
           .build();
}

Retrofit parsers and adapters

1. Data parser Converter, Retrofit supports multiple parsers
   
2. Network request adapter CallAdapter, Retrofit supports multiple network request adapters
   

APIs in Retrofit

Call<ResponseBody> call = servers.lookBookDetail(“参数”);
call.enqueue(new Callback<Translation>() {
    //请求成功时回调
    @Override
    public void onResponse(Call<Translation> call, Response<Translation> response) {
        // 对返回数据进行处理
        response.body().show();
    }
    //请求失败时候的回调
    @Override
    public void onFailure(Call<Translation> call, Throwable throwable) {
         System.out.println("连接失败");
    }
});
1. Asynchronous request
   call.enqueue();
2. Synchronous request
   call.execute();
3. Cancellation request
    call.cancel();
4. Process the data
   respose.body().show();

Use of Retrofit

1. Introduce the Retrofit framework in the build.gradle file of the app module.
implement 'com.squareup.retrofit2:retrofit:2.0.2’
implement 'com.squareup.retrofit2:converter-gson:2.0.2'
2. Custom data receiving entity class
3. Create an interface for describing network requests
4. Create a Retrofit object
5. Create a network request interface instance
6. Send network request
7. Process the returned data,
//创建Retrofit对象
Retrofit retrofit = new Retrofit.Builder()
  .baseUrl(""http://fanyi.youdao.com/"")
  .addConverterFactory(ProtoConverterFactory.create()) // 支持Prototocobuff解析
  .addConverterFactory(GsonConverterFactory.create()) // 支持Gson解析
  .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 支持RxJava
  .build();
//创建 网络请求接口 的实例
PostRequest_Interface request = retrofit.create(PostRequest_Interface.class);
//对网络请求进行封装
Call<Translation1> call = request.getCall("I love you”);
//发送网络请求(异步)
call.enqueue(new Callback<Translation1>() {
//请求成功时回调
    @Override
    public void onResponse(Call<Translation1> call, Response<Translation1> response) {
        // 步骤7:处理返回的数据结果:输出翻译的内容  
        System.out.println(response.body().getTranslateResult().get(0).get(0).getTgt());
    }
    //请求失败时回调
    @Override
    public void onFailure(Call<Translation1> call, Throwable throwable) {
        System.out.println("请求失败");
        System.out.println(throwable.getMessage());
    }
});

Retrofit implements file upload

public interface FileUploadService {  
    // 上传单个文件
    @Multipart
    @POST("upload")
    Call<ResponseBody> uploadFile(
            @Part("description") RequestBody description,
            @Part MultipartBody.Part file);

    // 上传多个文件
    @Multipart
    @POST("upload")
    Call<ResponseBody> uploadMultipleFiles(
            @Part("description") RequestBody description,
            @Part MultipartBody.Part file1,
            @Part MultipartBody.Part file2);
}
//Retrofit上传文件的工具类
public class retrofitFileUtil{
    public static final String MULTIPART_FORM_DATA = "multipart/form-data”;

    private RequestBody createPartFromString(String descriptionString) {  
        return RequestBody.create(
                 MediaType.parse(MULTIPART_FORM_DATA), descriptionString);
    }

    private MultipartBody.Part prepareFilePart(String partName, Uri fileUri) {  
        File file = FileUtils.getFile(this, fileUri);
        // 为file建立RequestBody实例
        RequestBody rf = RequestBody.create(MediaType.parse(MULTIPART_FORM_DATA), file);
        // MultipartBody.Part借助文件名完成最终的上传
        return MultipartBody.Part.createFormData(partName, file.getName(), rf);
    }
}
Uri file1Uri = ... // 从文件选择器或者摄像头中获取
Uri file2Uri = …

// 创建上传的service实例
FileUploadService service = ServiceGenerator.createService(FileUploadService.class);
// 创建文件的part (photo, video, ...)
MultipartBody.Part body1 = prepareFilePart("video", file1Uri);  
MultipartBody.Part body2 = prepareFilePart("thumbnail", file2Uri);
// 添加其他的part
RequestBody description = createPartFromString("hello, this is description speaking");
// 最后执行异步请求操作
Call<ResponseBody> call = service.uploadMultipleFiles(description, body1, body2);  
call.enqueue(new Callback<ResponseBody>() {  
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        Log.v("Upload", "success");
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        Log.e("Upload error:", t.getMessage());
    }
});

Guess you like

Origin blog.csdn.net/m0_49508485/article/details/127041124