Android Retrofit2(一)相关概念和基本使用

Retrofit是什么

retrofit是一个restful的http网络请求框架的封装,但网络请求不是由retrofit来完成的,它只是封装了请求参数、header、url、返回结果等信息,而真正处理网络请求的是由OkHttp来完成的。

基础使用

  • 1、导包
//网络请求相关 implementation
"com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion"
       implementation "com.squareup.retrofit2:retrofit-
mock:$rootProject.retrofitVersion"
       implementation "com.squareup.retrofit2:converter-
gson:$rootProject.retrofitVersion"
       implementation 'com.squareup.okhttp3:logging-interceptor:3.5.0'
       implementation "com.squareup.retrofit2:converter-
scalars:$rootProject.retrofitVersion"
       implementation "com.squareup.retrofit2:adapter-
rxjava2:$rootProject.retrofitVersion"
       implementation "com.squareup.retrofit2:converter-
gson:$rootProject.retrofitVersion"
  • 2、定义一个Http API接口类

interface WanAndroidApi {
   @GET("project/tree/json")
    Call<ProjectBean> getProject();
}
  • 3、使用Retrofit类生成WanAndroidApi 接口实现
Retrofit retrofit = new Retrofit.Builder()//建造者模式 .baseUrl("https://www.wanandroid.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        WanAndroidApi wanAndroidApi =
retrofit.create(WanAndroidApi.class);//代理实例
  • 4、发送HTTP请求,返回Response可以同步或者异步处理
Call<ProjectBean> call = wanAndroidApi.getProject();//获取具体的某个业务 //同步请求
Response<ProjectBean> response = call.execute();
ProjectBean projectBean = response.body();
//异步请求
call.enqueue(new Callback<ProjectBean>() {
@Override
            public void onResponse(final Call<ProjectBean> call, final
Response<ProjectBean> response) {
                Log.i("Zero","response: " + response.body());
            }
@Override
            public void onFailure(final Call<ProjectBean> call, final
Throwable t) {}
});

我们知道retrofit是通过注解配置请求的,在源码里我们可以看到一共有26个注解,我们最常用的肯定是GET和POST注解了。

/** Make a GET request. */
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {
  /**
   * A relative or absolute path, or full URL of the endpoint. This value is optional if the first
   * parameter of the method is annotated with {@link Url @Url}.
   *
   * <p>See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how
   * this is resolved against a base URL to create the full endpoint URL.
   */
  String value() default "";
}

在Retrofit中其实是通过注解 + 泛型 + 动态代理来实现的,并且其中使用了非常多的设计模式,具体我们在源码分析的时候再展开讲。我们回到GET注解:1、@Target(METHOD):代表是一个方法;@Target(PARAMETER):代表是一个参数,2、Retention(RUNTIME):并且是在运行期的注解。我们都知道运行期的注解,我们是需要通过反射获取他的内容的。在Retrofit中所有的26个注解都是运行期的注解。Retrofit在运行期的时候会去动态的解析标记上这些注解的内容。例如我们的GET注解,Retrofit在运行期的时候会去动态的解析标记上@GET,然后获取到"project/tree/json"这些值

interface WanAndroidApi {
   @GET("project/tree/json")
    Call<ProjectBean> getProject();
}

在retrofit中注解可以分成三类:1、请求方法类 2、标志类 3、参数类

1、请求方法类注解

image.png
  • 分别对应HTTP的请求方法
  • 接收一个字符串表示接口的path,与baseUrl组成完整的请求Url

其中我们需要注意的是HTTP注解,它是一个通用注解,可以替换以上所有的注解,其拥有method, path, hasBody三个属性。例如上面的get请求也可以这样写

 @GET("project/tree/json")
   fun getProject(): Call<ProjectBean>

 @HTTP(method = "get",path = "project/tree/json",hasBody = false)
   fun getProject(): Call<ResponseBody>

开发过后端接口的小伙伴看到这个肯定会觉得很熟悉,是不是和SpringBoot的写法很像,细心的小伙伴肯定也注意到了,顺带提一嘴,第一种Call里写的是ProjectBean,另外一种Call写了ResponseBody,其实这两种都可以,ResponseBody是更原始的方式,而你写了具体的实体类的话,retrofit会在内部帮你解析成ProjectBean,其实就是在你构建一个retrofit对象的时候.addConverterFactory(GsonConverterFactory.create())添加了这个GSON转化器,当然你觉得自己很牛逼想自己解析的话可以写成ResponseBody。转换器也是retrofit里很好用的东西,例如我们熟悉的GSON转化器,RxJava转化器,都可以在构建retrofit对象的时候添加进去。

2、参数类注解

image.png
  • Headers
 //使用 `@Headers`注解设置固定的请求头,不同的请求头不会相互覆盖,即使名字相同。
  @Headers("Cache-Control: max-age=640000")
@GET("project/list")
Call<ProjectBean> getMsg1();
@Headers({ "Accept: application/vnd.github.v3.full+json","User-Agent:
Retrofit-Sample-App"})
@GET("project/{username}")
Call<ProjectBean> getMsg2(@Path("username") String username);

需要注意的是,在Headers注解里,你可以传入多个Vlaue,源码里面是通过String数组接收的。

  • Header
    使用 @Header 注解动态更新请求头,匹配的参数必须提供给 @Header ,若参数值为 null ,这个头会 被省略,否则,会使用参数值的 toString 方法的返回值。
@GET("project")
Call<ProjectBean>getProject3(@Header("Authorization")String
   authorization);
  • Body
    使用 @Body 注解,指定一个对象作为 request body 。在使用post请求的时候会被用到。
@POST("project/new")
Call<ProjectBean> createProject(@Body ProjectBean user);
  • Field和FieldMap
 @FormUrlEncoded
    @POST("xxx")
    fun exmaple4(@Field("name") vararg name:String,
                 @Field("array") array: Array<String>,//name=xxx&age=22
                 @FieldMap map: Map<String,String>
                 ): Call<ResponseBody>
  • Path
  @GET("project/{id}/list")
    fun exmaple5(@Path("id") id: Int): Call<ResponseBody>
  • Query和QueryMap
 //http://www.xxx.xxx?name=sss&age=22
    @GET("xxxx")
    fun search(@Query("name") name: String,
               @QueryMap map: Map<String,String>): Call<ResponseBody>

Query和QueryMap是非常常用的,用于Get请求中指定参数

剩下的参数类注解都是用于表单字段的和Form表单请求一起讲。

3、 标志类注解

image.png
  • Streaming
    例如我们服务端返回给我们的图片是以流的方法返回的,我们就可以用到Streaming注解,我这里写个伪代码给大家看看Streaming的使用。
  @Streaming
    @GET
    Call<ProjectBean> downloadFile(@Url String fileUrl);

4、上传单个、多个文件

  • 单个文件
//第一种方法:RequestBody
 @Multipart
    @POST("project/upload")
    fun upload(@Part("file\";filename=\"test.png") file: RequestBody): Call<ResponseBody>

       //上传单个文件
        val file = File("")
        val requestBody = RequestBody.create(MediaType.parse("image/png"),file)

        val call = wanAndroidApi.upload(requestBody)

        call.enqueue(object : Callback<ResponseBody>{
            override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
            }

            override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
            }

        })

//第二种方法:MultipartBody.Part
 @Multipart
    @POST("project/upload")
    fun upload2(@Part file: MultipartBody.Part): Call<RequestBody>

  val file = File("")
        val requestBody = RequestBody.create(MediaType.parse("image/png"),file)
        val filePart =  MultipartBody.Part.createFormData("上传的key",
            file.name,requestBody)
        val call = wanAndroidApi.upload2(filePart)
        call.execute()//我这里为了演示,其实是要异步的

  • 多个文件
//第一种方法:RequestBody
 @Multipart
    @POST("project/upload")
    fun upload3(@PartMap map: Map<String,RequestBody>): Call<RequestBody>

 //图片集合
        val files = listOf<File>()
        val map = mutableMapOf<String,RequestBody>()
        files.forEach() {file ->
            val requestBody = RequestBody.create(MediaType.parse("image/png"),file)
            map["file\";filename=\"test.png"] = requestBody
        }
        wanAndroidApi.upload3(map)

//第二种方法:MultipartBody.Part
 @Multipart
    @POST("project/upload")
    fun upload4(@PartMap map: Map<String,MultipartBody.Part>): Call<RequestBody>

 val files = listOf<File>()
        val map = mutableMapOf<String,MultipartBody.Part>()
        files.forEachIndexed { index, file ->
            val requestBody = RequestBody.create(MediaType.parse("image/png"),file)
            val part = MultipartBody.Part.createFormData("上传的key${index}",file.name,requestBody)
            map["上传的key${index}"] = part
        }
        wanAndroidApi.upload4(map)