Retrofit梳理

作者: 蜗牛是不是牛 | 来源:发表于2021-12-31 19:49 被阅读0次

    本文为过往笔记整理, 在此只作记录,不做严谨的技术分享。

    概述

    RESTful

    Representational State Transfer ,架构风格(不是标准),使用正确的格式来请求和响应

    • 使用资源格式来定义URL
    • 规范使用method来定义网络请求操作
      • GET、POST、DELETE等方法,后台定义好的规则前端遵循。
      • 但现在存在的问题是,后台并不一定会按照规则设计接口。
    • 规范使用status code来表示响应状态
      • statuscode、message需按规则设计返回的内容。

    Retrofit

    Retrofit本质是一个RESTful风格的网络请求框架,用Java接口来描述Http请求,在接口里通过注解和动态代理,生成Request由OKHttp进行真正的网络请求

    注解

    Retrofit 共22个注解,根据功能大概分为三类

    分类 注解
    方法注解 @GET、@POST、@HEAD、@PUT、@DELETE、@HTTP、@PATH、@OPTIONS
    标记注解 @FormUrlEncoded、@Multipart、@Streaming
    参数注解 @Query、@QueryMap、@Body、@Field、@FieldMap、@Part、@PartMap、@Path、@Url、@Header、@Headers

    方法注解

    @GET、@POST、@HEAD、@PUT、@DELETE

    分别对应 HTTP中的网络请求方式

    @HTTP

    替换@GET、@POST、@PUT、@DELETE、@HEAD注解的作用, 及更多功能拓展
    具体使用:通过属性method、path、hasBody进行设置

    /**
     * method:网络请求的方法(区分大小写)
     * path:网络请求地址路径
     * hasBody:是否有请求体
     */
    @HTTP(method = "GET", path = "blog/{id}", hasBody = false)
    Call<ResponseBody> getCall(@Path("id") int id);
    
    

    标记注解

    @FormUrlEncoded、@Multipart、@Streaming

    @FormUrlEncoded

    表示发送form-encoded的数据,每个键值对需要用@Filed来注解键名,随后的对象需要提供值。

    /**
      *表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
      * <code>Field("username")</code> 表示将后面的 <code>String name</code>    中name的取值作为 username 的值
      */
     @POST("/form")
     @FormUrlEncoded
     Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name,@Field("age") int age);
    
    
    @Multipart

    表示发送form-encoded的数据(适用于 有文件 上传的场景),每个键值对需要用@Part来注解键名,随后的对象需要提供值。

    /**
     * {@link Part} 后面支持三种类型,{@link RequestBody}、{@link    okhttp3.MultipartBody.Part} 、任意类型
     * 除 {@link okhttp3.MultipartBody.Part}    以外,其它类型都必须带上表单字段({@link okhttp3.MultipartBody.Part}    中已经包含了表单字段的信息),
     */
    @POST("/form")
    @Multipart
    Call<ResponseBody> testFileUpload1(@Part("name") RequestBodyname,@Part("age")    RequestBody age, @Part MultipartBody.Part file);
    
    

    参数注解

    @Body、@Path、@Url
    @Field、@FieldMap
    @Query、@QueryMap
    @Part、@PartMap
    @Header、@Headers

    @Body

    以 Post方式传递,自定义数据类型给服务器
    如果提交的是一个Map,那么作用相当于 @Field,不过Map要经过 FormBody.Builder类处理成为符合 Okhttp格式的表单,如:

    FormBody.Builder builder = new FormBody.Builder();
    builder.add("key","value");
    FormBody body = builder.build();
    
    HashMap<String, String> map = new HashMap<>();
    RequestBody body = RequestBody
        .create(MediaType.parse("application/json;charset=utf-8"), new Gson().toJson(map));
    
    Map body = new HashMap();
    
    @POST("api/gameKkz/receiveGameKkzBox")
    Observable<BaseResponse<Empty>> test(@Body RequestBody body);
    Observable<BaseResponse<Empty>> test(@Body Map<String,Object> map);
    
    
    @Path

    URL地址的缺省值

    /**
    * 访问的API是:https://api.github.com/users/{user}/repos
    * 在发起请求时, {user} 会被替换为方法的第一个参数user(被@Path注解作用)
    */
    @GET("users/{user}/repos")
    Call<ResponseBody>  getBlog(@Path("user") String user );
    
    
    @Url

    直接传入一个请求的 URL变量 用于URL设置

    /**
    * 当有URL注解时,@GET传入的URL就可以省略
    * 当GET、POST...HTTP等方法中没有设置Url时,则必须使用 {@link Url}提供
    */
    @GET
    Call<ResponseBody> testUrlAndQuery(@Url String url, @Query("showAll") boolean showAll);
    
    
    @Field & @FieldMap

    发送 Post请求时提交请求的表单字段,与 @FormUrlEncoded 注解配合使用

    /**
     *表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
     * <code>Field("username")</code> 表示将后面的 <code>String name</code> 中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);
    
    
    @Query和@QueryMap

    用于 @GET 方法的查询参数(Query = Url 中 ‘?’ 后面的 key-value)
    如:url = www.println.net/?cate=andro… = cate 配置时只需要在接口方法中增加一个参数即可:

    // 其使用方式同 @Field与@FieldMap
    @GET("/")    
    Call<String> cate(@Query("cate") String cate);
    
    
    @Part & @PartMap

    发送 Post请求时提交请求的表单字段
    与@Field的区别:功能相同,但携带的参数类型更加丰富,包括数据流,所以适用于 有文件上传 的场景,与 @Multipart 注解配合使用

     /**
     * {@link Part} 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型
     * 除 {@link okhttp3.MultipartBody.Part} 以外,其它类型都必须带上表单字段({@link okhttp3.MultipartBody.Part} 中已经包含了表单字段的信息),
     */
    @POST("/form")
    @Multipart
    Call<ResponseBody> testFileUpload1(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file);
    
    /**
     * PartMap 注解支持一个Map作为参数,支持 {@link RequestBody } 类型,
     * 如果有其它的类型,会被{@link retrofit2.Converter}转换,如后面会介绍的 使用{@link com.google.gson.Gson} 的 {@link retrofit2.converter.gson.GsonRequestBodyConverter}
     * 所以{@link MultipartBody.Part} 就不适用了,所以文件只能用<b> @Part MultipartBody.Part </b>
     */
    @POST("/form")
    @Multipart
    Call<ResponseBody> testFileUpload2(@PartMap Map<String, RequestBody> args, @Part MultipartBody.Part file);
    
    @POST("/form")
    @Multipart
    Call<ResponseBody> testFileUpload3(@PartMap Map<String, RequestBody>args);
    
    
    @Header & @Headers

    效果是一致的,区别在于使用场景和使用方式:
    使用场景:@Header用于添加不固定的请求头,@Headers用于添加固定的请求
    使用方式:@Header作用于方法的参数,@Headers作用于方法

    // @Header
    @GET("user")
    Call<User> getUser(@Header("Authorization") String authorization)
    
    // @Headers
    @Headers("Authorization: authorization")
    @GET("user")
    Call<User> getUser()
    
    

    使用

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(ApiRetrofit.BASE_URL) 
            //都可以添加多个Factory
            .addConverterFactory(GsonConverterFactory.create())
            //.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build();        
    ApiRetrofit apiRetrofit = retrofit.create(ApiRetrofit.class);
    
    Call<Result<List<Chapter>>> call = apiRetrofit.chapters();
    call.enqueue(new Callback<Result<List<Chapter>>>() {
        @Override
        public void onResponse(Call<Result<List<Chapter>>> call, 
                               Response<Result<List<Chapter>>> response) {
            Result<List<Chapter>> list = response.body();
        }
    
        @Override
        public void onFailure(Call<Result<List<Chapter>>> call, Throwable t) {
        }
    });
    
    

    原理

    使用动态代理,根据method上的注解封装请求数据

      public <T> T createService(Class<T> clz) {
        return retrofit.create(clz);
      }
    
      public <T> T create(final Class<T> service) {
        validateServiceInterface(service);
        return (T) Proxy.newProxyInstance(service.getClassLoader()
                                          //公共接口
                                          , new Class<?>[] { service }
                                          //代理类
                                          ,new InvocationHandler() {
              private final Platform platform = Platform.get();
              private final Object[] emptyArgs = new Object[0];
    
              @Override public @Nullable Object invoke(Object proxy, Method method,
                  @Nullable Object[] args) throws Throwable {
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                if (platform.isDefaultMethod(method)) {
                  return platform.invokeDefaultMethod(method, service, proxy, args);
                }
                return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
              }
            });
      }
    
      //根据method上的注解,封装网络访问数据
      ServiceMethod<?> loadServiceMethod(Method method) {
        ServiceMethod<?> result = serviceMethodCache.get(method);
        if (result != null) return result;
    
        synchronized (serviceMethodCache) {
          result = serviceMethodCache.get(method);
          if (result == null) {
            result = ServiceMethod.parseAnnotations(this, method);
            serviceMethodCache.put(method, result);
          }
        }
        return result;
      }
    
    

    设计模式

    • 工厂模式
    • 适配模式
    • Builder
    • 策略模式
    • 代理模式

    本文转自 https://juejin.cn/post/7037782763524587551,如有侵权,请联系删除。

    相关文章

      网友评论

        本文标题:Retrofit梳理

        本文链接:https://www.haomeiwen.com/subject/rffnqrtx.html