阅读 224

OkHttp、rxJava、Retrofit联合网络请求(二)

上一篇文章我们讲到了OkHttp的一些相应的用法,如果没有看的那就没有看吧!这篇文章是不会用到相应内容的,因为Retrofit是基于OkHttp进行封装的!所以很好的避开的相应的操作!!!就酱紫了。。。

惯例一张电影图片镇楼!!!这次就用《一生一世》来镇楼吧!

本文知识点

  • Retrofit的简单使用
  • Retrofit注解的含义
  • Retrofit中一些常用的操作

重要的事情说三遍,网络权限一定要加,一定要加!!!

1. Retrofit的简单使用

Retrofit的简单使用记住一下几个步骤

  1. 创建Retrofit的实例
  2. 定义相应的接口,获取代理对象
  3. 发送请求
  4. 创建请求的回调

其实以上的步骤和OkHttp的差不多,如果你没看上一篇的话,那么就记住上面的话就可以了!!!接下来我们一步一步实现,其实还是很简单的!!!

1.1 创建Retrofit的实例

请求的地址是这个样子滴:

http://api.jisuapi.com/news/get?channel=头条&start=0&num=10&appkey=274ff62c9225cfa9
复制代码

我反手就是一串代码:

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://api.jisuapi.com/news/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
复制代码

其实Retrofit是封装了相应的OkHttp,所以写法当然类似了但是有一点值得注意的,这里有个baseUrl的概念,比如说你们公司所有的API的域名都是以http://api.XXX.xxx/开头的,后面有指定的内容进行匹配。那么这个baseUrl就可以这么写!拿上面这个地址为例:baseUrl就是http://api.jisuapi.com/news/注意:这个baseUrl的结尾一定是"\"结尾的! 如果没有这个就会抛出一个IllegalArgumentException异常!切记,切记。。。再说后面的addConverterFactory(GsonConverterFactory.create())这句,其实就是返回json的结构,因为现在一般的服务器都是以json进行传递的。所以这句话一定要加上,否则也会抛出IllegalArgumentException异常。记住就好了。

1.2 定义相应的接口,获取代理对象

因为Retrofit中存在大量的注解,所有的代理对象都在相应的接口中完成,所以这个回事后面的讲解重点,多以可以先不用理解相应的含义,后面会详细讲解。

public interface NewsService {
    @GET("get?channel=头条&start=0&num=10&appkey=274ff62c9225cfa9")
    Call<News> getNews();
}
复制代码

简单的说明一下:上面这个接口,代表是一个GET请求,请求成功后返回一个News对象。

NewsService newsService = retrofit.create(NewsService.class);
复制代码

然后回去相应的代理对象的代码。

1.3 发送请求

其实这里就是创建一个Call对象,来获取相应的回调!仅此而已。。。

Call<News> news = newsService.getNews();
复制代码

这里面的方法,对应的是顶部接口的那个方法,从而获取一个Call对象。

1.4 创建请求的回调

这里面的回调和OkHttp的回调基本上是一样的,但是比OkHttp的更人性化而已!因为这里直接可以获取到相应的亲求对象,而且不管你调几次都不会有异常。并且回调是同步的!是不是很6。。。

    news.enqueue(new Callback<News>() {
        @Override
        public void onResponse(Call<News> call, Response<News> response) {
            Log.e(TAG, "成功的回调: " + response.body());
            News news = response.body();
            Log.e(TAG, "onResponse: " + news.toString());
        }

        @Override
        public void onFailure(Call<News> call, Throwable t) {
            Log.e(TAG, "失败的回调: " + t.toString());
        }
    });
复制代码

以上就是最简单的一个请求了!下面我们看一下整体代码吧!

        /*1.创建Retrofit的实例*/
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://api.jisuapi.com/news/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        /*2.定义相应的接口,获取代理对象*/
        NewsService newsService = retrofit.create(NewsService.class);
        /*3.发送请求*/
        Call<News> news = newsService.getNews();
        /*4.创建请求的回调*/
        news.enqueue(new Callback<News>() {
            @Override
            public void onResponse(Call<News> call, Response<News> response) {
                Log.e(TAG, "成功的回调: " + response.body());
                News news = response.body();
            }

            @Override
            public void onFailure(Call<News> call, Throwable t) {
                Log.e(TAG, "失败的回调: " + t.toString());
            }
        });
复制代码

2. Retrofit注解的含义

其实Retrofit中的注解很多,但是基本上可以分为一下几类:

  1. GET请求相关的注解
  2. POST请求相关的注解
  3. 公共请求的注解

2.1 GET请求相关的注解

在GET请求中无非就是更改中间的参数或者在后面拼接相应的参数而已无非项目中常用的就这几种而已!

2.1.1 @Query 和 @QueryMap (用于GET请求添加参数)

这两个注解用在GET请求中的我就不说了!咦,这不是说了吗?其实说简单点就是在后面追加参数的!!!两个的区别呢?@Query能追加一个,@QueryMap能追加多个!剩下的都一样了!!!

拿上面的地址举个例子说明下@Query:

    /**
     * @author : 贺金龙
     * email : 753355530@qq.com
     * create at 2018/9/7  10:50
     * description : 演示@Query的示例
     */
    @GET("get?channel=头条&start=0&num=10")
    Call<News> getNews(@Query("appkey") String qppKey);
复制代码

和上面的区别主要是我把最后一个参数放到getNews方法中传递了!调用的地方只要修改一个地方就行。

Call<News> news = newsService.getNews("274ff62c9225cfa9");
复制代码

其他的内容完全不用动,其实结果和上面一样,这里主要是为了演示而已

好再拿上面的地址举个例子说明下@QueryMap:

    /**
     * @author : 贺金龙
     * email : 753355530@qq.com
     * create at 2018/9/7  10:50
     * description : 演示@QueryMap的示例
     */
    @GET("get?")
    Call<News> getNews(@QueryMap Map<String, String> map);
复制代码

和上面的区别在于我把所有的参数都放到Map中进行传递了!调用的地方修改成这样就可以了!

Map<String, String> map = new HashMap<>();
        //channel = 头条 & start = 0 & num = 10 & appkey = 274f f62c9225cfa9
        map.put("channel", "头条");
        map.put("start", "0");
        map.put("num", "10");
        map.put("appkey", "274ff62c9225cfa9");

Call<News> news = newsService.getNews(map);
复制代码

结果还是一样的,只是所有参数都通过map集合进行传递,以上方案选择那个看你们需求就可以了!!!

2.1.2 @Path URL的缺省值补充

其实这个很好理解,就是在URL地址中替换相应的路径中的某一段名称,有的URL地址中间有相应“/”分割的内容,path也就是替换这部分的内容;

这回我们拿一段新的URL演示一下@Path注解的用法:

//原始路径地址:http://api.jisuapi.com/train/station2s?appkey=274ff62c9225cfa9&start=杭州&end=北京&ishigh=0

    /**
     * @author : 贺金龙
     * email : 753355530@qq.com
     * create at 2018/9/7  9:47
     * description : 演示@Path注解的示例
     */
    @GET("{path}/station2s?appkey=274ff62c9225cfa9&start=杭州&end=北京")
    Call<StationBean> getTrainStation(@Path("path") String path, @Query("ishigh") String ishigh);
复制代码

请不要看后面的"ishigh"那个传递,因为之前写了一个参数的方法,所以才加上的!主要看path那块的代码。这里path替换的是"train"这个内容,没有什么记住写法就可以了,调用的时候像下面这样写就可以了:

Call<StationBean> train = newsService.getTrainStation("train", "0");
复制代码

有的人会问这个有什么用?其实对于上面这个请求真的没有什么用,怎么说呢?有的Get请求中间这块是一些用户的参数,比如userId什么的,是动态的话。上面的@Path注解就有相应的用处了。如果上面的地址改成http://api.jisuapi.com/uesrId/station2s?appkey=274ff62c9225cfa9&start=杭州&end=北京&ishigh=0userId是根据用户的ID进行更改的,那么就可以使用@Path这个注解了。好了剩下的场景自己发觉吧!!!

2.2 POST请求相关的注解

  1. @FormUrlEncoded和@Multipart
  2. @Field和@FieldMap 添加请求参数(与@FormUrlEncoded注解配合使用)
  3. @Part和@PartMap 添加请求参数(与@Multipart注解配合使用)
  4. @Body 封装一个对象

上面最开始演示的是GET请求,对于POST请求小伙伴们还是不了解,所以这里先给大家演示一下正常的POST请求是怎么弄的!其实其他的代码都不用动,只需要更改相应的接口就可以了,像如下这样:

    /**
     * @author : 贺金龙
     * email : 753355530@qq.com
     * create at 2018/9/7  11:51
     * description : 演示POST的请求内容
     */
    @FormUrlEncoded
    @POST("train/station2s?")
    Call<StationBean> getPostTrainStation(@FieldMap Map<String, String> map);
复制代码

使用的时候和上面一样传递相应的内容就可以了!!!

2.2.1 @FormUrlEncoded和@Multipart注解

其实这个是和POST请求一起作用才有效的注解,怎么理解呢?记得请求的时候有相应的文件上传吧?其实这个东西就是标记你是上传文件,还是上传表单的;

  • @FormUrlEncoded 纯表单形式(不包含上传文件)
  • @Multipart 表单形式(包含文件上传)

看上面那个案例,POST请求都应该添加@FormUrlEncoded或者@Multipart注解,否则会抛出java.lang.IllegalArgumentException: @FieldMap parameters can only be used with form encoding. 的异常!

记住上面这些东西,下面用到的时候我会详细讲解的

2.2.2 @Field和@FieldMap 添加请求参数

千万要注意上面两个注解要与@FormUrlEncoded注解配合使用!!!发送POST的表单请求。这里注意@Field和@FieldMap只能携带String类型的参数!

@Field的使用和上面@Query的用法是类似的!只是更改一下相应的注解而已但是@FormUrlEncoded别忘记添加!!!

@FieldMap的案例参考上面getPostTrainStation的内容就可以了!!!所以这里就不贴相应的代码了,请原谅我这种懒癌患者。。。

2.2.3 @Part和@PartMap 添加请求参数

千万要注意上面两个注解要与@Multipart注解配合使用!!!发送POST的表单请求。这里要注意于@Field和@FieldMap的区别在于携带的参数类型更加丰富,包括数据流,所以适用于“有文件上传”的场景!上面都说到了@Part和@PartMap有文件上传的功能,所以这里就直接演示一下相应的文件上传,代码如下:

    @Multipart
    @POST("/form")
    Call<ResponseBody> testLoadFile(@Part("name") String name, @Part("age") String age, @Part MultipartBody.Part file);
复制代码

使用的时候代码如下:

    MediaType textType = MediaType.parse("text/plain");
    RequestBody name = RequestBody.create(textType, "Carson");
    RequestBody age = RequestBody.create(textType, "24");
    RequestBody file = RequestBody.create(MediaType.parse("application/octet-stream"), "这里是模拟文件的路径");
    MultipartBody.Part body = MultipartBody.Part.createFormData("picture", "文件名称", file);
复制代码

这里最值得说明的就是MediaType.parse("application/octet-stream")这个代表上传文件的类型!

参数 说明
text/html HTML格式
text/plain 纯文本格式
text/xml XML格式
image/gif gif图片格式
image/jpeg jpg图片格式
image/png png图片格式
application/xhtml+xml XHTML格式
application/xml XML数据格式
application/atom+xml Atom XML聚合格式
application/json JSON数据格式
application/pdf pdf格式
application/msword Word文档格式
application/octet-stream 二进制流数据

参照以上的类型就可以了!!!因为这里不能用公司的地址去尝试,找了半天没有相应上传的地址,所以我放弃了,不过你按照上面的方式处理就可以了,应该不会错的。如果错了你找我,我给你看看!!!

有人会说,我如果要传一堆参数怎么弄?也就是多个key/value的形式,其实你可以这么弄!

    @Multipart
    @POST("/form")
    Call<ResponseBody> testLoadFileMore(@FieldMap Map<String, String> map, @Part MultipartBody.Part file);
复制代码

或者

    @Multipart
    @POST("/form")
    Call<ResponseBody> testLoadFileMore(@Part Map<String, RequestBody> map, @Part MultipartBody.Part file);
复制代码

怎么选择就看你项目里面的需求了!!!

2.2.4 @Body 封装一个对象

这个注解是用在POST请求的,这一点千万要注意,其实就是把Body中每一个属性取出来,以表单的形式传给服务器的!这里传递的对象服务器传递的参数不同是这样的{"appkey":"274ff62c9225cfa9","end":"北京","ishigh":"0","start":"杭州"}首先你要明确一点,这个@Body是把内容放在body总传递地的!不是通过header传递的!!!

代码是这样的:

    /**
     * @author : 贺金龙
     * email : 753355530@qq.com
     * create at 2018/9/7  14:57
     * description : 演示@Body注解的使用
     */
    @POST("train/station2s?")
    Call<StationBean> getPostTrainStationBody(@Body BodyBean bodyBean);
复制代码

传递的时候,正常传递就好了!!!

像下面这样:

    BodyBean bodyBean = new BodyBean("274ff62c9225cfa9", "杭州", "北京", "0");
    Call<StationBean> postTrainStationBody = newsService.getPostTrainStationBody(bodyBean);
复制代码

注意点:

  1. 不要像POST请求那样设置@FormUrlEncoded否则会抛出@Body parameters cannot be used with form or multi-part encoding. (parameter #1)异常!
  2. 是传递到相应Request的body中,不是通过header传递的,这个一定要明确。不懂的话,问问后台的人员!!!

2.3 一些公共的请求参数

2.3.1 @Header和@Headers

关于这两个注解很好理解,就是添加请求头的,但是@Header添加固定的请求头,@Headers添加的是不固定的请求头

这里就直接贴一段代码就好了,看了你就能懂了

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

// @Headers
@Headers("Authorization: authorization")
@GET("user")
Call<User> getUser()
复制代码

如果你问我请求头是什么的话,你可以在Google浏览器中按F12然后给你一张图!

上面这个你要是不理解,找你们后台的去问问,要么就自己学习一下!我怕我讲不懂,因为我不是服务器的。。。

2.3.2 @Url 允许我们传入一个URL地址(可以实现相应的重定向)

我是这么理解的,其实我不知道这么理解对不对,但是效果是能看见的!!!也就是我可以不管你baseUrl中设置的内容,这里直接更改相应的URL地址!像下面这样:

首先看一下baseUrl的设置

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://api.jisuapi.com/news/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
复制代码

然后我们在看我们怎么重新设置相应的地址:

    /**
     * @author : 贺金龙
     * email : 753355530@qq.com
     * create at 2018/9/7  9:47
     * description : 获取铁路信息的接口
     */
    @GET
    Call<StationBean> getTrainStation(@Url String url);
复制代码

对就是通过@Url这个注解来重新定制相应的注解,所以这里不管你之前的baseUrl什么样都会从这个url中获取地址,来看一下完整的代码:

    /**
     * @author : 贺金龙
     * email : 753355530@qq.com
     * create at 2018/9/7  9:48
     * description : Retrofit更改请求网址的演示
     */
    public void retrofitChange(View view) {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://api.jisuapi.com/news/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        NewsService newsService = retrofit.create(NewsService.class);
        Call<StationBean> trainStation = newsService.getTrainStation("http://api.jisuapi.com/train/station2s?appkey=274ff62c9225cfa9&start=杭州&end=北京&ishigh=0");
        trainStation.enqueue(new Callback<StationBean>() {
            @Override
            public void onResponse(Call<StationBean> call, Response<StationBean> response) {
                Log.e(TAG, "请求成功的话证明URL地址已经修改了" + response.body().toString());
            }

            @Override
            public void onFailure(Call<StationBean> call, Throwable t) {
                Request request = call.request();
                HttpUrl url = request.url();

                Log.e(TAG, "请求失败的话打印一下URL地址" + url);
                Log.e(TAG, "onFailure: " + t);
            }
        });
    }
复制代码

其实这里不管你baseUrl设置的地址怎么样?最后访问的都是http://api.jisuapi.com/train/station2s?appkey=274ff62c9225cfa9&start=杭州&end=北京&ishigh=0 这个地址!

那么很多人会问这个有什么作用,想想你们公司正常的网络请求和修改头像的这两个地址一样吗?反正我们不一样,所以才需要这个东西的!!!


基本上使用的时候就这么多问题,可能有些讲解不到的,如果有什么不到位的,及时补充!!!我理解的也有限。有问题留言,我们一期讨论!!!

github地址奉上

关注下面的标签,发现更多相似文章
评论