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<List<User>> getUsers();
}

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

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

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

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

二、动态url访问@PATH

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

public interface ApiService {

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

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

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

            @Override
            public void onFailure(Call<User> call, Throwable t) {
             
            }
        });
三、带有查询参数设置@Query的Get请求

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

public interface ApiService {

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

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

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

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

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

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

public interface ApiService {

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

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

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

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

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

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

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

public interface ApiService {

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

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

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

            @Override
            public void onFailure(Call<User> call, Throwable t) {
                // 处理异常
            }
        });
六、单文件@Multipart

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

public interface ApiService {

    @Multipart
    @POST("register")
    Call<User> 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://api.example.com/")
                .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<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                // 处理结果
            }

            @Override
            public void onFailure(Call<User> call, Throwable t) {
                // 处理异常
            }
        });
七、多文件上传@PartMap

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

public interface ApiService {

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

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

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.example.com/")
                .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<String,RequestBody> 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<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                // 处理结果
            }

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

八、请求头Headers的添加

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

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

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

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

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

@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
@GET("user")
Call<User> getUser(@HeaderMap Map<String, String> headers)

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

九、同步和异步请求

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

十、Retrofit框架引入方式(Gradle)
implementation 'com.squareup.retrofit2:retrofit:(insert latest version)'
十一、Retrofit混淆配置
十二、下载文件

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

public interface ApiService {

    @GET("download")
    Call<ResponseBody> download();
}

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

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

            @Override
            public void onFailure(Call<ResponseBody> 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<T>类型的接口返回,如果需要和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://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

GitHubService service = retrofit.create(GitHubService.class);
四、自定义ConverterFactory

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

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值