Retrofit2基本使用

概述

Retrofit是当下最热门的Android网络请求库,准确的来说Retrofit是一个RESTful的HTTP网络请求框架的封装,因为它内部网络请求的工作,本质上是通过OkHttp完成,而Retrofit仅负责网络请求接口的封装。具体是客户端通过Retrofit请求网络,实际上是通过Retrofit的接口层封装了请求参数,header,url等信息,之后由OkHttp完成后续的请求工作。然后在服务端返回数据之后,OkHttp将原始的结果传递给Retrofit,Retrofit根据客户端的相关配置,将结果进行解析回调给客户端

基本使用

一、Get请求

Retrofit在使用的过程中需要定义接口层,接口层中的每个方法标识一个独立的请求,如下定义接口层ApiService

public interface ApiService {

    @GET("users/list")
    Call> getUsers();
}

@Get注解作用于方法之上,表明这是一个Get请求,@Get注解的value值与Retrofit中配置的baseUrl组成完整的请求url,List代表响应结果类型,下面通过Retrofit请求完成上述请求

        // builder模式构建Retrofit对象
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://xxx/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        // 创建接口层的代理对象,内部通过动态代理创建了ApiService的代理对象  
        ApiService api = retrofit.create(ApiService.class);
        // 执行异步请求
        api.getUsers().enqueue(new Callback>() {
            @Override
            public void onResponse(Call> call, Response> response) {
                // 处理结果
            }

            @Override
            public void onFailure(Call> call, Throwable t) {
                // 处理异常
            }
        });

我们知道原始的请求响应结果其实是ResponseBody,那么我们这里声明了响应数据类型为List,Retrofit内部是如何实现的呢?其实是通过我们构建Retrofit时传入的转换器GsonConverterFactory完成一个响应结果的转换

二、动态url访问@PATH

比如我现在要访问类似这样的url https://api.example.com/user/1 那么接口层定义如下:

public interface ApiService {

    @GET("user/{id}")
    Call getUser(@Path("id") String id);
}

@Get注解中使用了{id}作为占位符,实际运行会通过方法中@Path(“id”)注解标注的参数进行替换,访问案例如下:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://xxx/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        ApiService api = retrofit.create(ApiService.class);
        api.getUser("1").enqueue(new Callback() {
            @Override
            public void onResponse(Call call, Response response) {
              
            }

            @Override
            public void onFailure(Call call, Throwable t) {
             
            }
        });

三、带有查询参数设置@Query的Get请求

例如我们要访问这样的url https://api.example.com/users?username=zhangsan 接口层定义如下:

public interface ApiService {

    @GET("users")
    Call getUserByUserName(@Query("username") String username);
}

通过@Get表明了请求方式,通过@Query标注了请求的参数名,同样的也适用与Post请求,只需要将@Get注解替换为@Post即可,访问案例如下:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://xxx/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        ApiService api = retrofit.create(ApiService.class);
        api.getUserByUserName("zhangsan").enqueue(new Callback() {
            @Override
            public void onResponse(Call call, Response response) {
              
            }

            @Override
            public void onFailure(Call call, Throwable t) {

            }
        });

四、POST请求体的方式向服务器传入json字符串@Body

在平时与服务端的接口交互中,我们一般会将请求参数直接封装成一个json字符串传递给服务端,通过Retrofit如何实现呢?接口层定义如下:

public interface ApiService {

    @POST("users/add")
    Call> addUser(@Body User user);
}

可以看到我们通过@Body注解标注参数对象即可,而后Retrofit内部将User对象转换了json字符串传递给服务端,访问案例如下:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://xxx/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        ApiService api = retrofit.create(ApiService.class);
        User user = new User("zhangsan",24,"[email protected]")
        api.addUser(user).enqueue(new Callback() {
            @Override
            public void onResponse(Call> call, Response response) {
                
            }

            @Override
            public void onFailure(Call> call, Throwable t) {
              
            }
        });

Retrofit默认只能接受@Body的RequestBody类型,那么我们这里传入了一个User对象,那么Retrofit内部是如何转换的呢?其实还是利用上面说的转换器GsonConverterFactory。

五、表单的方式传递键值对@FormUrlEncoded

Web开发中表单提交很常见,那么通过Retrofit如何实现一个表单提交的请求呢?接口层定义如下:

public interface ApiService {

    @POST("login")
    @FormUrlEncoded
    Call> login(@Field("username") String username,@Field("password") String password);
}

通过@POST指明url,添加@FormUrlEncoded,然后通过@Field添加参数即可。访问案例如下:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://xxx/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        ApiService api = retrofit.create(ApiService.class);
        api.login("zhangsan","123456").enqueue(new Callback() {
            @Override
            public void onResponse(Call call, Response response) {
                // 处理结果
            }

            @Override
            public void onFailure(Call call, Throwable t) {
                // 处理异常
            }
        });

六、单文件@Multipart

通过Retrofit实现单文件上传,接口层定义如下:

public interface ApiService {

    @Multipart
    @POST("register")
    Call register(@Part MultipartBody.Part header, @Part("username") RequestBody username,@Part("password") RequestBody password);
}

这里@MultiPart的意思就是允许多个@Part了,我们这里使用了3个@Part,第一个我们准备上传个文件,使用了MultipartBody.Part类型,其余两个均为简单的键值对。访问案例如下:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://xxx/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        ApiService api = retrofit.create(ApiService.class);
        // 创建需要上传服务端的用户头像文件
        File file = new File(Environment.getExternalStorageDirectory(), "photo.png");
        RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file);
        MultipartBody.Part photo = MultipartBody.Part.createFormData("photo", file.getName(), photoRequestBody);
        ReqeustBody usernameRequestBody = RequestBody.create(null, "zhangsan");
        ReqeustBody passwordRequestBody = RequestBody.create(null, "123456");
        api.registerUser(photo, usernameRequestBody,passwordRequestBody).enqueue(new Callback() {
            @Override
            public void onResponse(Call call, Response response) {
                // 处理结果
            }

            @Override
            public void onFailure(Call call, Throwable t) {
                // 处理异常
            }
        });

七、多文件上传@PartMap

Retrofit实现多文件上传,接口层定义如下:

public interface ApiService {

    @Multipart
    @POST("register")
    Call register(@PartMap Map params, @Part("password") RequestBody password);
}

这里使用了一个新的注解@PartMap,这个注解用于标识一个Map,Map的key为String类型,代表上传的键值对的key(与服务器接受的key对应),value即为RequestBody,有点类似@Part的封装版本。访问案例如下:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://xxx")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        ApiService api = retrofit.create(ApiService.class);
        File file = new File(Environment.getExternalStorageDirectory(), "photo.png");
        RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png", file);
        Map params = new HashMap<>();
        params.put("photo\"; filename=\"photo.png", photoRequestBody);
        params.put("username",  RequestBody.create(null, "zhangsan"));
        ReqeustBody passwordRequestBody = RequestBody.create(null, "123456");
        api.registerUser(params,passwordRequestBody).enqueue(new Callback() {
            @Override
            public void onResponse(Call call, Response response) {
                // 处理结果
            }

            @Override
            public void onFailure(Call call, Throwable t) {
                // 处理异常
            }
        });

八、请求头Headers的添加

有时候,我们需要为某个请求单独添加请求头,那么如何操作呢?接口层定义如下:

  • 通过@Headers注解进行设置
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call> widgetList();
@Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call getUser(@Path("username") String username);

注意,@Headers添加的请求头不会相互覆盖,具有相同名称的所有标头将包含在请求中。

  • 使用@Header注解动态更新请求头

必须为@Header提供相应的参数,如果该值为null,则标题将被省略。否则,将对值调用toString,并使用结果

@GET("user")
Call getUser(@Header("Authorization") String authorization)
@GET("user")
Call getUser(@HeaderMap Map headers)

我们还可以使用OkHttp拦截器指定需要添加到每个请求的请求头

九、同步和异步请求

Call实例可以同步或者异步执行,每个Call实例只能使用一次,但是调用Call#clone()将创建一个可以使用的新实例。 在Android上,回调将在主线程上执行。在JVM上,回调将在执行HTTP请求的同一线程上发生。

十、Retrofit框架引入方式(Gradle)

implementation 'com.squareup.retrofit2:retrofit:(insert latest version)'

十一、Retrofit混淆配置

 

# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
# EnclosingMethod is required to use InnerClasses.
-keepattributes Signature, InnerClasses, EnclosingMethod

# Retrofit does reflection on method and parameter annotations.
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations

# Retain service method parameters when optimizing.
-keepclassmembers,allowshrinking,allowobfuscation interface * {
    @retrofit2.http.* ;
}

# Ignore annotation used for build tooling.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

# Ignore JSR 305 annotations for embedding nullability information.
-dontwarn javax.annotation.**

# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
-dontwarn kotlin.Unit

# Top-level functions that can only be used by Kotlin.
-dontwarn retrofit2.KotlinExtensions
-dontwarn retrofit2.KotlinExtensions$*

# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
# and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
-if interface * { @retrofit2.http.* ; }
-keep,allowobfuscation interface <1>

 

十二、下载文件

Retrofit实现文件下载,接口层定义如下:

public interface ApiService {

    @GET("download")
    Call download();
}

这里直接返回类型定义为ResponseBody,然后直接拿到其byteStream(),读流即可。访问案例如下:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://xxx")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        ApiService api = retrofit.create(ApiService.class);
        api.download().enqueue(new Callback() {
            @Override
            public void onResponse(Call call, Response response) {
                 InputStream in = response.body().byteStream();
                 // 进行保存文件操作 这里是主线程!!!
            }

            @Override
            public void onFailure(Call call, Throwable t) {
                 // 处理异常
            }
        });

可以看到,我们在回调onResponse中拿到了文件流,然后就可以进行写文件的操作了,但是注意这里是主线程,文件读写操作属于耗时操作,我们还得切到子线程操作,所以文件的下载操作直接使用OkHttp就行了。

进阶配置

一、配置callFactory

查看Retrofit构建过程得知,如果没有配置callFactory,那么Retrofit内部会创建默认的OkHttpClient对象作为callFactory

public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      // callFactory为空,则创建默认的OkHttpClient
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
      // ...省略代码
}

如果需要对OkHttpClient进行详细的设置,我们需要构建OkHttpClient对象,然后通过Retrofit.Builder#callFactoy(okhttp3.Call.Factory factory)方法Retrofit.Builder#client(OkHttpClient client)或者传入。

二、配置CallAdapterFactory

默认情况下,Retrofit只能支持Call类型的接口返回,如果需要和RxJava一起使用,那么需要配置RxJavaCallAdapterFactory,这样接口返回值类型就是Observable对象,即可以使用RxJava进行请求操作。

三、配置ConverterFactory

默认情况下,Retrofit只能将HTTP正文反序列化为OkHttp的ResponseBody类型,并且只能接受@Body的RequestBody类型,可以添加转换器以支持其他类型。六个成员模块采用了流行的序列化库,以方便我们使用。

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
JAXB: com.squareup.retrofit2:converter-jaxb
Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

这是一个使用GsonConverterFactory类生成GitHubService接口的实现的示例,该接口使用Gson进行反序列化。

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https:xxx")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

GitHubService service = retrofit.create(GitHubService.class);

四、自定义ConverterFactory

如果需要与使用Retrofit不支持的现成格式(例如YAML,txt,自定义格式)的API进行通信,或者希望使用其他库来实现现有格式,则可以轻松创建 您自己的转换器。 创建一个扩展Converter.Factory类的类,并在构建适配器时传入实例。

你可能感兴趣的:(知识点,Retrofit2,android)