专栏首页Android-薛之涛Android-Retrofit简介

Android-Retrofit简介

上一篇文章讲了RxJava,这一篇当然就该讲Retrofit了,参考资料: https://blog.csdn.net/gumufuyun/article/details/83619879

1.Retrofit简介

Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装,网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责 网络请求接口的封装。在服务端返回数据之后,OkHttp 将原始的结果交给 Retrofit,Retrofit根据用户的需求对结果进行解析。

Retrofit 主要定义了 4 个接口:

  • Callback<T>:请求数据的返回;
  • Converter<F, T>:对返回数据进行解析,一般用 GSON ;
  • Call<T>:发送请求,Retrofit 默认的实现是 OkHttpCall<T>,也可以依需自定义 Call<T>;
  • CallAdapter<T>:将 Call 对象转换成其他对象,如转换成支持 RxJava 的 Observable对象

2.相关依赖

相关依赖:

//添加Retrofit依赖
    implementation 'com.squareup.retrofit2:retrofit:2.6.0'
    //用Gson解析json的转换器
    implementation 'com.squareup.retrofit2:converter-gson:2.0.2'

当然还有网络请求权限:

<uses-permission android:name="android.permission.INTERNET"/>

Retrofit将 Http请求 抽象成 Java接口:采用 注解 描述网络请求参数 和配置网络请求参数,用 动态代理 动态 将该接口的注解“翻译”成一个 Http的url请求,最后再执行 Http 请求。那我们先来解释Retrofit的各种注解吧。

3.注解类型

3.1网络请求方式的注解

网络请求中的网络请求方式的注解,如下:

  • 网络请求方式之@GET、@POST、@PUT、@DELETE、@HEAD 相关代码如下:
 //第一部分代码
    /**
     * 直接获得Responsebody中的内容,定义网络请求返回值为Call<ResponseBody>
     *  call<T> T是接受的数据返回类型
     */
    @GET("article/list/1/json")
    Call<ResponseBody> getCall();
//第二部分代码
Retrofit  retrofit = new Retrofit.Builder()
                 //设置网络请求的Url地址
                .baseUrl(baseUrl)
                 //设置数据解析器
                .addConverterFactory(GsonConverterFactory.create()).build();

由上图代码可以看到Rtrofit的封装请求url至少由两部分组成:baseUrl+@Get()中的内容动态拼接而成,但不是绝对的,也可以:

retrofit = new Retrofit.Builder()
                 //设置网络请求的Url地址
                .baseUrl("https://www.wanandroid.com/article/list/1/json")
                 //设置数据解析器
                .addConverterFactory(GsonConverterFactory.create()).build();

实际开发中不会这样写的,而且第一部分和第二部分的代码也是分开写的,我这里是为了演示方便,理解。 上面的代码也可以通过@Http这样写:

  • 网络请求方式之Http注解
/**
     * method 请求方法
     * path 请求路径,其中的{变量名}表示是一个变量
     * hasBody 是否有请求体
     * @param pageNum 参数
     * @return
     */
    @HTTP(method = "GET",path = "article/list/{pageNum}/json",hasBody = false)
    Call<ResponseBody> getCallData(@Path("pageNum") int pageNum);

@Http注解的作用和@GET,@Post作用一样,可替换@GET、@POST、@PUT、@DELETE、@HEAD注解且进行更多功能拓展。

3.2 标记

  • @FormUrlEncoded 使用场景:表示发送form-encoded的数据,那么什么是FormUrlEncoded数据呢?

image.png

每个键值对需要用@Filed来注解键名,随后的对象需要提供值。

/**
     * 表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
     * @param pageNum
     * @return
     */
    @GET("article/list/{pageNum}/json")
    @FormUrlEncoded
    Call<ResponseBody> getFormUrlEncodedData(@Field("pageNum") int pageNum );
  • @Multipart 使用场景: 作用:表示发送form-encoded的数据(适用于 有文件 上传的场景),也就是就是http请求中的multipart/form-data,它会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件。当上传的字段是文件时,会有Content-Type来表名文件类型;content-disposition,用来说明字段的一些信息;由于有boundary隔离,所以multipart/form-data既可以上传文件,也可以上传键值对,它采用了键值对的方式,所以可以上传多个文件。如图:

image.png

每个键值对需要用@Part来注解键名,随后的对象需要提供值。 代码:

  /**
     {@link Part} 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型
     * @param account 账号
     * @param pwd 密码
     * @param file file文件
     * @return
     */
    @GET("/form")
    @Multipart
    Call<ResponseBody> getMultipart(@Part("account") RequestBody account , @Part("pwd")
            RequestBody pwd, @Part MultipartBody.Part file);


    //具体执行代码:
    //既可以提交普通键值对,也可以提交(多个)文件键值对。
    public static final MediaType MEDIA_TYPE_MULTIPART_FORM = MediaType.parse("multipart/form-data;charset=utf-8");
    RequestBody account =RequestBody.create(MEDIA_TYPE_MULTIPART_FORM,"123");
    RequestBody pwd =RequestBody.create(MEDIA_TYPE_MULTIPART_FORM,"123");

    RequestBody fileResponseBody =RequestBody.create(MEDIA_TYPE_MULTIPART_FORM,"134646464");
    MultipartBody .Part file =MultipartBody.Part.createFormData("file","test.txt",fileResponseBody);

    Call<ResponseBody> call = (Call<ResponseBody>) getMultipart(account, pwd, file);
//同步请求
  Call<ResponseBody>  result =call .excute();

3.3 网络请求参数

  • @Header & @Headers 作用:添加请求头 &添加不固定的请求头 相关代码:
/ @Header
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
 
// @Headers
@Headers("Authorization: authorization")
@GET("user")
Call<User> getUser()
 
// 以上的效果是一致的。
// 区别在于使用场景和使用方式
// 1. 使用场景:@Header用于添加不固定的请求头,@Headers用于添加固定的请求头
// 2. 使用方式:@Header作用于方法的参数;@Headers作用于方法
  • Body 作用:以 Post方式 传递 自定义数据类型 给服务器 特别注意:如果提交的是一个Map,那么作用相当于 @Field 不过Map要经过 FormBody.Builder 类处理成为符合 Okhttp 格式的表单,如:
FormBody.Builder builder = new FormBody.Builder();
builder.add("key","value");
  • @Field & @FieldMap 作用:发送 Post请求 时提交请求的表单字段 具体使用:与 @FormUrlEncoded 注解配合使用
   /**
     * 表面明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
     * @param pageNum
     * @param pageSize
     * @return
     */
    @POST("/form")
    @FormUrlEncoded
    Call<ResponseBody> getFormUrlEncodedData(@Field("pageNum") int pageNum ,@Field("pageSize") int pageSize);

    /**
     * 表面明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
     * @param map
     * @return
     */
    @POST("/form")
    @FormUrlEncoded
    Call<ResponseBody> getFormUrlEncodedMap(@FieldMap Map<String, Object> map);

//具体执行代码:
       // @Field
        Call<ResponseBody> call1 =HttpRequest.getInstance().getInterfaceInstance().getFormUrlEncodedData(1,15);
        // @FieldMap
        // 实现的效果与上面相同,但要传入Map
        Map<String, Object> map = new HashMap<>();
        map.put("pageNum", 1);
        map.put("pageSize", 15);
        Call<ResponseBody> call2 = HttpRequest.getInstance().getInterfaceInstance().getFormUrlEncodedMap(map);
  • part & @PartMap 作用:发送 Post请求 时提交请求的表单字段 与@Field的区别:功能相同,但携带的参数类型更加丰富,包括数据流,所以适用于 有文件上传 的场景
  • @Query和@QueryMap 作用:用于 @GET 方法的查询参数(Query = Url 中 ‘?’ 后面的 key-value) 相关代码:
    /**
     * 登录接口
     * @param username 用户名
     * @param password 密码
     * @return
     */
    @POST("user/login")
    Observable<BaseResponse<LoginOutBean>> login(@Query("username") String username , @Query("password") String password);

其使用方式同 @Field与@FieldMap.

  • @Path 作用:URL地址的缺省值
  • @Url 作用:直接传入一个请求的 URL变量 用于URL设置,当有URL注解时,@GET传入的URL就可以省略。 4.实战操作

说了那么多不如来一个实战看看,这里以鸿洋大神的wanAndroid接口进行测试: 第一步定义请求接口类,用来管理所有的请求接口:

image.png

第二步:通过单例构建retrofit实例

public class HttpRequest {

    private static volatile HttpRequest httpRequest;
    private static String baseUrl = "https://www.wanandroid.com/";
    private static Retrofit retrofit;
    private static RequestInterface requestInterface;

    private HttpRequest() {
        //第二部分代码
        retrofit = new Retrofit.Builder()
                //设置网络请求的Url地址
                .baseUrl(baseUrl)
                //设置数据解析器
                .addConverterFactory(GsonConverterFactory.create()).build();
        //生成接口实例
        requestInterface = retrofit.create(RequestInterface.class);
    }

    public static HttpRequest getInstance() {
        if (httpRequest == null) {
            synchronized (HttpRequest.class) {
                if (httpRequest == null) {
                    httpRequest = new HttpRequest();
                }
            }
        }
        return httpRequest;
    }
    
    /**
     * 返回接口实例的方法
     *
     * @return
     */
    public RequestInterface getInterfaceInstance() {
        return requestInterface;
    }
    
}

第三步:执行相关请求

btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //调用方法  返回Call对象
                Call<ResponseBody> call = HttpRequest.getInstance().getInterfaceInstance().getCall(1);
                //call对象执行异步请求,访问网络
                call.enqueue(new Callback<ResponseBody>() {
                    @Override
                    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                        String jsonString = null;
                        try {
                             jsonString =new String((response.body()).bytes());
                            //再使用Retrofit自带的Gson去解析
                            PageBean pageBean = new Gson().fromJson(jsonString, PageBean.class);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        Toast.makeText(MainActivity.this,jsonString,Toast.LENGTH_LONG).show();

                    }

                    @Override
                    public void onFailure(Call<ResponseBody> call, Throwable t) {
                        Toast.makeText(MainActivity.this,t.getMessage(),Toast.LENGTH_LONG).show();
                    }
                });
            }
        });

简单就说这么多,如果你想更深的了解retrofit的源码,推荐文章:Retrofit想用得好就得这么死磕。 完毕!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java-策略设计模式

    今天 我们来学习一下策略模式,什么是策略模式呢?比如我们一个功能的实现可以有多个策略去选择,比如:出行方式可以选择:共享单车,拼车,私家车,出租等。如果将这个出...

    android_薛之涛
  • Android-Lambda表达式

    Lambda,中文名“兰布达”。是匿名函数的别名,Java8后开始引入Lambda表达式.而Android方面Android Studio 2.4 Previe...

    android_薛之涛
  • Andriod-Dagger2

    参考资料: https://www.jianshu.com/p/1d84ba23f4d2 https://mp.weixin.qq.com/s/lh3dgJ...

    android_薛之涛
  • php面试笔记(6)-php基础知识-正则表达式考点

    在面试中,考官往往喜欢基础扎实的面试者,而正则表达式相关的考点,往往是大家容易忽视的一个点,今天冷月就来帮各位小伙伴们梳理一下,在面试中正则表达式相关的注意点。

    学长冷月
  • JavaWeb三大组件之Filter学习详解

    JavaWeb三大组件之Filter学习详解 Filter基本上可以说存在所有的JavaWeb项目中,比如最基本的一个请求参数的编码CharacterEnco...

    一灰灰blog
  • Linux 服务器模型

    http://blog.csdn.net/lingfengtengfei/article/details/12348903

    bear_fish
  • 2019-1-2-使用AggregateException的Handle过滤指定异常

    在一些并行操作或者任务列表执行的过程中,会需要用到AggregateException进行聚合异常的处理

    黄腾霄
  • RxAndroid完全教程

    不知现在的编辑,能否好用一些呢?希望大家有什么意见,在公众号直接回复即可,想了解哪些知识点,也可以直接留言了。 炎炎夏日,清凉一夏 这章节主要介绍下Rx系列的经...

    用户1263308
  • 感知机初探

    感知机模型的假设空间:定义在特征空间中的所有线性分类模型(linear classification model)或线性分类器(linear classifie...

    王强
  • 马云呼吁治理酒驾式打假获雷军们力挺,为中国创造踢上临门一脚

    今年两会期间与实体经济、虚拟经济、新零售相关的声音不绝于耳,已成为中国经济命脉一部分的电子商务也是热门话题。不是两会代表的马云不忘在微博发声,致信两会代表呼吁“...

    罗超频道

扫码关注云+社区

领取腾讯云代金券