Retrofit2+Rxjava+OkHttp的配合使用

本文详细介绍了如何在Android中使用Retrofit2、RxJava和OkHttp进行网络请求。首先,Retrofit作为网络请求框架,依赖于OkHttp。接着,展示了如何配置Retrofit,包括设置baseURL、添加ConverterFactory如Gson,以及定义接口。同时,文章讲解了Retrofit的各种注解如@GET、@POST、@Query等。此外,还提到了OkHttp的配置,如添加拦截器和Cookie管理。最后,文章介绍了如何结合RxJava进行异步操作,让网络请求更加便捷。

首先介绍Retrofit: Retrofit是Square 公司开发的一款正对Android 网络请求的框架。底层基于OkHttp 实现。版本要求至少需要java7或者Android2.3。
github地址

使用
在项目的build.gradle下dependencies下添加:

compile ‘com.squareup.retrofit2:retrofit:2.1.0’
创建retrofit 实例

String `Basrurl` = "Https://api.douban.com/v2/movie/";
    //Basrurl 代表服务器根地址
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(Basrurl)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

Retrofit2的baseurl必须要以斜线结束,不然会抛出一个IllegalArgumentException。
添加一个factory 来响应返回数据进行序列化解析。这里添加Gson解析。
在你项目的build.gradle上的dependencies下加入:

compile ‘com.squareup.retrofit2:converter-gson:2.1.0’
还有其他converter如下:

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
定义接口.
在retrofit2中,端口是定义在一个interface里面的,通过注解来映射参数以及请求类型或者请求头等细节。另外返回值都是一个被参数化得Call< T >对象。

public interface HttpInterface {

/**
* @param start 开始位置
* @param count 数量
* @return
*/

    @GET("top250")
    Call<Object> getMovie(@Query("start") int start,
                            @Query("count") int count);
}

各种注解含义(主要解释GET和POST两种):
@Query
用于GET请求参数传递

@GET(“login/users”)
Call<List> checkLogin(@Query(“id”) int useId);
等同于:url后面接?加参数的K-V值

@GET(“loger/users?id=useId”)
@QueryMap
用于GET多参数集成一个Map统一上传

@GET("movie/search")
Call<MovieSearchEntity> getSearchMovies(@Query("type") String type,
                             @QueryMap Map<String, String> params);

调用时候将多个参数存入一个map里面一起调用,如下:

Map<String, String> params= new HashMap<>();
map.put("name", "星际");
map.put("time", null);
map.put("start", "0");
map.put("count", "5");
Call<MovieSearchEntity> call = httpService.getSearchMovies("科幻",params);

等同于:

@GET(“movie/search?type=科幻”&name=星际&start=0&count=5)
@Path
用于url上的占位符,可以用双大括号包裹,参数中替换,可用于任何请求方式中。如下:

@GET(“movie/{id}”)
Call getMovie(@Path(“id”) String id);
1
根据不同的id得到的数据也不同。可以动态传递,比如调用:

Call<BookResponse> call = mBlueService.httpService("123456");
相对与请求以下@GET

@GET("movie/123456")
@field 
用于post请求,post请求需要把参数放置在请求体中,并非是拼接在url后面:

    @POST("user/login")
    @FormUrlEncoded
    Call<Object> login(@Field("usename") String usename,
                       @Field("password") String password);

@FormUrlEncoded是会自动将请求参数的类型调整为表单类型application/x-www-form-urlencoded,不能用于GET请求,如果不用传递参数可以不用加。

@Field注解是将每个请求参数都存在请求体中,还可以添加encoded参数,该参数为boolean型,比如:

@Field(value = “usename”,encoded = ture) String usename
encoded参数为true的话,key-value-pair将会被编码,即将中文和特殊字符进行编码转换。

@Body
用于POST请求,可以将实例对象转换为对应的字符串传递参数,如果在Retrofit初始化时候添加了GsonConverterFactory,则是将body转换为gson字符进行串传递的,其他Converter大同小异。如下:

@POST("user/login")
@FormUrlEncoded
Call<String> loginTestBody(@Body LoginEntity loginEntity);

public class LoginEntity {
    public String usename;
    public String password;
    public String vip;
    public String money;
}

@Part
用于文件的上传,这里先不介绍

@Headers
Retrofit提供了两种定义Http请求头的参数:静态和动态。静态方法在头部信息初始化的时候已经固定写死了,而动态方法则必须为每个请求单独设置。
静态设置:

@Headers("User-Agent: android")
@POST("login")
@FormUrlEncoded
Call<Object> login1(@Field("usename") String usename,
                   @Field("password") String password);

添加多个header参数也可以,类似数组:

@Headers({“User-Agent: android”
,“Cache-Control: max-age=640000”})
@POST(“login”)
@FormUrlEncoded
Call login1(@Field(“usename”) String usename,
@Field(“password”) String password);
静态也可以设置okhttp的拦截器去添加头布局:

 okHttpClient = new OkHttpClient.Builder()
       .addInterceptor(new Interceptor() {
      @Override
       public Response intercept(Chain chain) throws IOException {
            Request request = chain.request()
              .newBuilder()
              .addHeader("userDeviceID", MyApplication.DEVICETOKEN)
              .header("User-Agent", "android-27")
              .build();
              return chain.proceed(request);
       }
   })
   .cookieJar(new CookiesManager())
   .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)//设置读取超时时间
   .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)//设置写的超时时间
   .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
   .build();

需要注意的是,addHeader是可以存在相同的key值,而header则是覆盖重复的。
设置好OkHttp后在Retrofit中client加入okHttpClient

retrofit = new Retrofit.Builder()
    .client(okHttpClient)                
    .addConverterFactory(GsonConverterFactory.create())
    .baseUrl(baseUrl)
    .build();
动态设置:

 @POST("login")
    @FormUrlEncoded
    Call<Object> login2(@Header("User-Agent") String userAgent,
                        @HeaderMap Map<String,String> headers,
                        @Field("usename") String usename,
                        @Field("password") String password);

@Url
需求总是变化的,如果需要请求的地址不是以baseUrl开头的话,就需要使用这个注解,直接请求完整的url,忽视baseurl:

    @POST
    @FormUrlEncoded
    Call<Object> login3(@Url String url,
                        @Field("usename") String usename,
                        @Field("password") String password);
 
@Streaming 
大文件下载的时候,同个该注解,防止直接写入内存中。比如app更新时候,下载music的时候。

    @Streaming
    @GET
    Observable<ResponseBody> download(@Header("RANGE") String start, @Url String url);

前面Retrofit的各种参数讲的差不多了,都不知道刚开始讲的是什么(手动滑稽)
上面讲了创建Retrofit的实例,创建了请求接口。通过retrofit动态代理拿到接口对象的实例:

   String Basrurl = "Https://api.douban.com/v2/movie/";
    //Basrurl 代表服务器根地址
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(Basrurl)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    httpInterface = retrofit.create(HttpInterface.class);

拿到getMovie返回的Call对象:

Call movie = httpInterface.getMovie(0, 10);
执行enqueue(可以链式连写):

movie.enqueue(new Callback<Object>() {
            @Override
            public void onResponse(Call<Object> call, Response<Object> response) {
                Object body = response.body();
                body.toString();
                Log.e("TAG",body.toString());
                try {
                    Gson gson = new Gson();
                    String str = gson.toJson(body);
                    JSONObject jsonObject = new JSONObject(str);
                    JSONArray subjectsArray = jsonObject.optJSONArray("subjects");
                    for(int i = 0;i < subjectsArray.length(); i++){
                        JSONObject jsonChild = subjectsArray.getJSONObject(i);
                        String title = jsonChild.optString("title");
                       Log.e("TAG","TOP250电影第"+(i+1)+"的名字:  "+title);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

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

            }
        });

这里简单的请求就完成了,这里是打印出来的,没有界面,比较懒。
Call对象还可以执行取消操作,当网络请求还没有完成的时候取消:

call.cancel();

这里写图片描述

是不是觉得很麻烦,还要一层层的解析。是的,既然是泛型,为何不加入实体类就行了嘛。加入泛型改动如下:
实体类Movie250Modle实现Serializable接口,完成序列化操作。
接口添加泛型:

@GET("top250")
Call<Movie250Modle> getMovie(@Query("start") int start,
                             @Query("count") int count);

请求时回调会自动加入泛型,如下:

String Basrurl = "Https://api.douban.com/v2/movie/";
        //Basrurl 代表服务器根地址
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(Basrurl)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        httpInterface = retrofit.create(HttpInterface.class);

        Call<Movie250Modle> movie = httpInterface.getMovie(0, 10);
        movie.enqueue(new Callback<Movie250Modle>() {
            @Override
            public void onResponse(Call<Movie250Modle> call, Response<Movie250Modle> response) {
                Movie250Modle movie250Modle = response.body();
                List<Movie250Modle.SubjectsBean> movie250ModleSubjects = movie250Modle.getSubjects();
                for(int i = 0; i < movie250ModleSubjects.size(); i++){
                    Log.e("TAG","实体类TOP250电影第"+(i+1)+"的名字:  "+movie250ModleSubjects.get(i).getTitle());
                }
            }

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

            }
        });

看看结果也是一样的:
这里写图片描述

添加OkHttp参数
你们所知道的,retrofit2内部网络请求使用的就是okhttp,光是用上面的retrofit2往往是不够全面的,还需要添加okhttp参数:

 /**
         * addInterceptor   设置拦截器
         * cookieJar    设置cook管理类
         * readTimeout   设置读取超时时间
         * writeTimeout  设置写的超时时间
         * connectTimeout  设置链接超时时间
         * retryOnConnectionFailure 设置是否重试链接
         */
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(new MyInterceptor())
                .cookieJar(new CookiesManager())
                .readTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10,TimeUnit.SECONDS)
                .connectTimeout(10,TimeUnit.SECONDS)
                .retryOnConnectionFailure(true)
                .build();

前面讲了
cookieJar 是cook持久化管理,用于免登陆使用
addInterceptor是前面设置header用过的,可以添加一些参数:

class MyInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        HttpUrl httpUrl = request.url()
                .newBuilder()
                // add common parameter
                .addQueryParameter("token", "123")
                .addQueryParameter("username", "tt")
                .build();
        Request build = request.newBuilder()
                // add common header
                .addHeader("contentType", "text/json")
                .url(httpUrl)
                .build();
        Response response = chain.proceed(build);
        return response;
   } 
   }
在retrofit创建的时候加入client:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(Basrurl)
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .build();

这样就设置好了okhttp的参数

网络日志
okhttp支持网络请求的信息打印,这样更方便查看网络请求信息。在项目中build.gradle文件中引入logging-interceptor:

compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
在初始化okhttp时候加入:

HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        /**
         * addInterceptor   设置拦截器
         * addInterceptor   设置logg拦截器
         * cookieJar    设置cook管理类
         * readTimeout   设置读取超时时间
         * writeTimeout  设置写的超时时间
         * connectTimeout  设置链接超时时间
         * retryOnConnectionFailure 设置是否重试链接
         */
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(new MyInterceptor())
                .addInterceptor(loggingInterceptor)
                .cookieJar(new CookiesManager())
                .readTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10,TimeUnit.SECONDS)
                .connectTimeout(10,TimeUnit.SECONDS)
                .retryOnConnectionFailure(true)
                .build();

没错HttpLoggingInterceptor也是implements Interceptor接口;loggingInterceptor其中包含了4个打印等级:在logcat用okhttp过滤信息
NONE
No logs,无信息
BASIC
/**

  • Logs request and response lines and their respective headers.
  • Example:

  • {@code 
    
  • –> POST /greeting http/1.1
  • Host: example.com
  • Content-Type: plain/text
  • Content-Length: 3
  • –> END POST
  • <-- 200 OK (22ms)
  • Content-Type: plain/text
  • Content-Length: 6
  • <-- END HTTP
  • }
    */
    打印请求类型,URL,请求体大小,返回值状态以及返回值的大小
    具体例子:
    这里写图片描述
    HEADERS

/**
* Logs request and response lines and their respective headers.
*
*

Example:
*

{@code
* --> POST /greeting http/1.1
* Host: example.com
* Content-Type: plain/text
* Content-Length: 3
* --> END POST
*
* <-- 200 OK (22ms)
* Content-Type: plain/text
* Content-Length: 6
* <-- END HTTP
* }

*/
打印返回请求和返回值的头部信息,请求类型,URL以及返回值状态码
这里写图片描述
BODY

/**
* Logs request and response lines and their respective headers and bodies (if present).
*
*

Example:
*

{@code
* --> POST /greeting http/1.1
* Host: example.com
* Content-Type: plain/text
* Content-Length: 3
*
* Hi?
* --> END POST
*
* <-- 200 OK (22ms)
* Content-Type: plain/text
* Content-Length: 6
*
* Hello!
* <-- END HTTP
* }

*/
打印返回请求和返回值的头部信息,请求类型,URL以及返回值状态码以及Body信息(数据信息)
这里写图片描述

加入Rxjava
如果rxjava没学过的,可以看看这个大牛写的博客:
初学者Rxjava2教程
先引入rxjava全家桶,在项目build.gradle下加入:

  compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
    compile 'io.reactivex.rxjava2:rxjava:2.0.1'
    // 此处一定要注意使用RxJava2的版本
    compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'

然后在Retrofit实例化加入addCallAdapterFactory

 Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(Basrurl)
                .client(okHttpClient)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
        httpInterface = retrofit.create(HttpInterface.class);

接口类中的修改,不能返回Call对象了,要返回被观察者,,观察者观察网络请求,将网络请求当做被观察者,完成一个异步操作。

/**
     * @param start 开始位置
     * @param count 数量
     * @return
     */
    @GET("top250")
    Observable<Movie250Modle> getMovie(@Query("start") int start,
                                       @Query("count") int count);

那么网络请求拿到的接口对象如下:

Flowable movieObservable = httpInterface.getMovie(0, 10);
执行异步网络操作:

movieObservable.subscribeOn(Schedulers.io())

            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Observer<Movie250Modle>() {
                @Override
                public void onSubscribe(Disposable d) {

                }

                @Override
                public void onNext(Movie250Modle value) {

                }

                @Override
                public void onError(Throwable e) {

                }

                @Override
                public void onComplete() {

                }
            });

等待,我们这里用的是rxjava2,所以对应被观察者(上游)Observable变成Flowable,下游Observer就变成了Subscriber,所以就变成如下:

 movieObservable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Movie250Modle>() {
                    @Override
                    public void onSubscribe(Subscription s) {

                    }

                    @Override
                    public void onNext(Movie250Modle movie250Modle) {

                    }

                    @Override
                    public void onError(Throwable t) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });

subscribeOn(Schedulers.io())指定被观察者事件发生io线程,耗时操作
observeOn(AndroidSchedulers.mainThread())指定观察者事件发生在主线程,更新UI必须在主线程中。
subscribe(new Subscriber……) 被观察者订阅观察者。

end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值