Retrofit2+Rxjava+OkHttp的使用和网络请求

首先介绍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<User>> 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<MovieEntity> getMovie(@Path("id") String id);

根据不同的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<Object> 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<Object> 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.
*
* <p>Example:
* <pre>{@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
* }</pre>
*/

打印请求类型,URL,请求体大小,返回值状态以及返回值的大小
具体例子:
这里写图片描述
HEADERS

 /**
     * Logs request and response lines and their respective headers.
     *
     * <p>Example:
     * <pre>{@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
     * }</pre>
     */

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

 /**
     * Logs request and response lines and their respective headers and bodies (if present).
     *
     * <p>Example:
     * <pre>{@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
     * }</pre>
     */

打印返回请求和返回值的头部信息,请求类型,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<Movie250Modle> 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…..) 被观察者订阅观察者。


基本使用已经完成了,后续写下请求封装。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值