Bootstrap

android retrofit2.0框架的使用介绍

准备学习下retrofit框架的使用,然后封装下,现在不懂retrofit都不意思说自己是android开发,所以也学习下,在github上的地址是:https://github.com/square/retrofit


要使用retrofit要引入的库还是挺多的

//rxjava引入的包
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.1.7'
//retrofit引入的包
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.google.code.gson:gson:2.2.4'
//返回给我的是一个json解析后的bean
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
//rxjava2+retrofit搭配使用的依赖
compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
//okhttp日记拦截器
compile 'com.squareup.okhttp3:logging-interceptor:3.9.1'
在这多说一句,像前面三个引入的在rxandroid,retrofit的 github文档上就可以直接copy过来,但是像后面三个我怎么在studio下依赖就是找不到,后面实在没办法,就直接百度了下到他的官网介绍中去找对应的版本了,


算是一个小插曲吧.

开发包引入了以后就是学习怎么使用了,首先看下他的文档,肯定是有demo给我们介绍的.

http://square.github.io/retrofit/

上面是他的官网文档,首先看下它给的例子,然后照着他写的抄就行


第一步:

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}
它定义了一个接口,然后有个方法listRepos,返回的是一个Call对应,还是画图说下容易解释


把上面的图翻译下就是这个:

 
/** * method 表示请的方法, * path表示路径 * hasBody表示是否有请求体 */ @HTTP(method = "get", path = "users/{user}", hasBody = false)
这是从网上看到别人写的,觉得对理解起来很不错,就copy过来了,非常感谢,现在对着这个来模仿写一个请求方法
public interface KSService {
    @GET("api/data/{Android}/{size}/{page}")
    Call<Info> getInfoData(@Path("Android") String Android, @Path("size") String size, @Path("page") int page);
}
第二步:

构建一个Retrofit对象

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://gank.io/")
        .addConverterFactory(GsonConverterFactory.create())//支持把请求的json转换bean
        .build();
第三步:
KSService service = retrofit.create(KSService.class);
第四步:
Call<Info> call = service.getInfoData("Android","10",1);//传递的参数
第五步:
call.enqueue(new Callback<Info>() {
    @Override
    public void onResponse(Call<Info> call, Response<Info> response) {
        Info info = response.body();
        List<Info.ResultsBean> list = info.getResults();
        Log.e(TAG,"url="+list.get(0).getUrl());
    }
    @Override
    public void onFailure(Call<Info> call, Throwable t) {

    }
});

上面就是发送一个异步get的请求,同步我不会再讲了,几乎没人用同步去请求网络,可能第一次写感觉乖乖的,但是多写几次就熟练了.

我们知道get方式请求,我们可以直接把参数拼接在url后面,这样就不用带参数,

@GET("api/data/Android/10/1")
Call<Info> getInfoData();
还有一种就是如果我们要传的参数有很多,这个时候,就可以使用使用这个注解:@QueryMap
@GET("api/data/")
Call<Info> getInfoData(@QueryMap Map<String, String> map);
在传参的时候传递一个map集合就可以了
Map<String,String> map  = new HashMap<>();
map.put("Android","Android");
map.put("size","10");
map.put("page","1");
Call<Info> call = service.getInfoData(map);//传递的参数
但有的时候用不了这个,因为这个是把参数拼接在url后面,比如这个url:http://gank.io/api/data/Android/10/1如果使用@QueryMap传递多个参数,最后的请求url是这个:http://gank.io/api/data/?page=1&size=10&Android=Android

还有一个用于在参数的注解@Query

@GET("api/data/")
Call<Info> getInfoData(@Query("Android") String Android, @Query("size") String size, @Query("page") int page);
记住如果使用@Query注解作用在参数的时候,url后面不要带参数,请看请求的地址:


发现也是把key_value进行拼接的在url后面,所以@Path和@Query注解有啥不同就出现了

@Path是将value值直接放在url后面,用/分割

@Query是将key-value的形式拼接在url后面

@QueryMap是将多个key-value的形式拼接在url后面

当然如果想对参数进行编码的话,他们都提供的有个encode,默认值是为false,如果不对参数进行url编码的话就传true,

@Query还能传递一个数组,相当于是我们Java中的可变参数一样

@GET("api/data/")
Call<Info> getInfoData(@Query ("Android")String...Android );
也就是说他的key是一样的,只是传递的value不同而已,

http://gank.io/api/data/?Android=Android&Android=java&Android=php 

Post请求

现在自己使用tomcat搭建一个简单的服务器,来玩下post请求

@POST("aa/HelloServlet")
Call<Person> login(@Field("username")String username, @Field("password") String password);
这是用户登录的,传递了二个参数
public void post(View view){
    OkHttpClient httpClient = new OkHttpClient();
    if (BuildConfig.DEBUG) {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        httpClient = new OkHttpClient.Builder().addInterceptor(logging).build();
    }
    Retrofit retrofit = new Retrofit.Builder()
            .client(httpClient)
            .baseUrl("http://192.168.1.107:8080/")
            .addConverterFactory(GsonConverterFactory.create())//支持把请求的json转换bean
            .build();
    KSService service = retrofit.create(KSService.class);
    Call<Person> call =  service.login("zhouguizhi","123456");
    call.enqueue(new Callback<Person>() {
        @Override
        public void onResponse(Call<Person> call, Response<Person> response) {
                Log.e(TAG,"登录请求成功");
            Log.e(TAG,"response="+response.body());
        }
        @Override
        public void onFailure(Call<Person> call, Throwable t) {

        }
    });
}
发现报错了,错误日记:


意思是说我们传递的参数没有进行编码,所以post请求一定要加@FormUrlEncoded

@FormUrlEncoded
@POST("aa/HelloServlet")
Call<Person> login(@Field("username")String username, @Field("password") String password);
再试试就成功了,

服务器接受的消息:


客户端接收到的


如果有多个参数也可以使用@FieldMap

文件下载

首先使用get方法从豆瓣上下载一张图片

@GET("/view/photo/m/public/p2505783407.webp")
Call<ResponseBody> download_get();
我把下载后的文件存放在sd卡下的:
public void download_get(View view){
    OkHttpClient httpClient = new OkHttpClient();
    if (BuildConfig.DEBUG) {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        httpClient = new OkHttpClient.Builder().addInterceptor(logging).build();
    }
    Retrofit retrofit = new Retrofit.Builder()
            .client(httpClient)
            .baseUrl("https://img1.doubanio.com")
            .build();
    KSService service = retrofit.create(KSService.class);
    Call<ResponseBody> call =  service.download_get();
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            InputStream inputStream = response.body().byteStream();
            Log.e(TAG,"下载成功=");
            FileOutputStream fileOutputStream = null;
            try {
                fileOutputStream = new FileOutputStream(new File(getPath(),"/zgz2.jpg"));
                byte[] buffer = new byte[2048];
                int len = 0;
                while ((len = inputStream.read(buffer)) != -1) {
                    fileOutputStream.write(buffer, 0, len);
                }
                fileOutputStream.flush();
            } catch (IOException e) {
                Log.e(TAG,"下载过程中失败="+e);
            }finally {
                try {
                    if(null!=fileOutputStream){
                        fileOutputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if(inputStream!=null){
                        inputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }            }
        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.e(TAG,"下载失败="+t.getLocalizedMessage());
        }
    });
}
查看日记:


下载成功了我们就到文件管理器中去找下刚才下载的文件


刚才使用get方式文件下载,现在使用post方式文件下载也是一样:

@POST("/view/photo/m/public/p2505783407.webp")
Call<ResponseBody> download_post();
其他代码不变.文件下载好了,开始学习下文件上传,

文件上传

先来个简单的单文件上传

@Multipart
@POST("/aa/UploadServlet")
Call<ResponseBody> upload_post(@Part  MultipartBody.Part file);
实现:
public void upload(View view){
    OkHttpClient httpClient = new OkHttpClient();
    if (BuildConfig.DEBUG) {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        httpClient = new OkHttpClient.Builder().addInterceptor(logging).build();
    }
    Retrofit retrofit = new Retrofit.Builder()
            .client(httpClient)
            .baseUrl("http://192.168.1.107:8080/")
            .build();

    File file = new File(getPath(),"zgz3.jpg");
    RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
    MultipartBody.Part body = MultipartBody.Part.createFormData("uploadfile", file.getName(), requestBody);

    KSService service = retrofit.create(KSService.class);
    Call<ResponseBody> call =  service.upload_post(body);
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            try {
                Log.e(TAG,"文件上传成功="+response.body().string());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.e(TAG,"文件上传失败="+t.getLocalizedMessage());
        }
    });
}
log:


在这我弄好久,刚开始是豆瓣上下载的图片上传的

https://img1.doubanio.com/view/photo/albumcover/public/p2220219198.webp

他是webp格式的,我下载后在我电脑上打不开,请求也不成功,后来突然想到了换个jpg格式的,一下子就成功了,折腾了1个多小时,我也是晕死.

下载到我的电脑上的:


服务端代码,也贴下

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Part part=request.getPart("uploadfile");
PrintWriter out=response.getWriter();
System.out.println("ContentType="+part.getContentType()+"\n");
out.print("ContentType="+part.getContentType()+"\n");
System.out.println("size="+part.getSize()+"\n");
out.print("size="+part.getSize()+"\n");
String name = part.getName();
out.print("name="+name+"\n");
part.write("C://Users/zhouguizhijxhz/zgz1.png");
Gson gson=new Gson();
UpLoad upLoad = new UpLoad();
upLoad.setCode(0);
upLoad.setMsg("success");
response.getWriter().write("success");
}

单个文件上传成功了,但是可能要求你上传文件的同时,还要传递参数,这个也搞定下,很快的。

@Multipart
@POST("/aa/UploadServlet")
Call<ResponseBody> mulitupload(@Part  MultipartBody.Part file,@QueryMap  Map<String,String> map);

实现

public void upload_params(View view){
    OkHttpClient httpClient = new OkHttpClient();
    if (BuildConfig.DEBUG) {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        httpClient = new OkHttpClient.Builder().addInterceptor(logging).build();
    }
    Retrofit retrofit = new Retrofit.Builder()
            .client(httpClient)
            .baseUrl("http://192.168.1.107:8080/")
            .build();

    File file = new File(getPath(),"zgz3.jpg");

    RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
    MultipartBody.Part body = MultipartBody.Part.createFormData("uploadfile", file.getName(), requestBody);

    KSService service = retrofit.create(KSService.class);
    Map<String,String> map = new HashMap<>();
    map.put("name","zhouugizhi");
    map.put("time","2017-11-30");
    Call<ResponseBody> call =  service.mulitupload(body,map);
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            try {
                Log.e(TAG,"文件上传成功="+response.body().string());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.e(TAG,"文件上传失败="+t.getLocalizedMessage());
        }
    });
}
看下服务器接受到传递的参数:


name就是我传递的参数,time没有做打印,现在看下客户度的返回什么数据,我把name,time返回给客户端了


?是中文乱码了,不管,单上文上传并传参也实现了,多文件上传,由于不会服务器端多文件怎么处理,暂时放一放.

retrofit+rxjava

retrofit内置式支持rxjava操作的,当然需要去设置下,请求方法不再是返回一个Call对象了,而是返回一个被观察者Observable,加上下面这行代码:

addCallAdapterFactory(RxJava2CallAdapterFactory.create())

但是发现我怎么发现我没有这个类呢?,原来是我那个rxjava适配器的包没有使用大神的

//    compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
    compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0-RC3'
使用下面的

使用rxjava方式请求一个get,方法的定义

@GET("api/data/{Android}/{size}/{page}")
Observable<ResponseBody> getInfoDataByRxjava(@Path(value = "Android",encoded = true) String Android, @Path(value = "size",encoded = true) String size, @Path(value = "page",encoded = true) int page);
实现
public void get_by_rxjava(View view){
    OkHttpClient httpClient = new OkHttpClient();
    if (BuildConfig.DEBUG) {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        httpClient = new OkHttpClient.Builder().addInterceptor(logging).build();
    }
    Retrofit retrofit = new Retrofit.Builder()
            .client(httpClient)
            .baseUrl("http://gank.io")
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build();
    KSService service = retrofit.create(KSService.class);
    Observable<ResponseBody> observable =  service.getInfoDataByRxjava("Android","10",1);
    observable
           .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Consumer<ResponseBody>(){
                @Override
                public void accept(ResponseBody responseBody) throws Exception {
                    String s = responseBody.string();
                    Log.e(TAG,"请求的结果:"+s);
                    tv.setText(s);
                }
            });
}
log:


关于retrofit+rxjava就是这么使用的,只是我们原来单独使用rxjava的是通过操作符create返回一个Observable,现在不用 了在retrofit中,还有就是我们之前是去调异步或者同步的方法去请求网络,使用了额rxjava就不用去掉了,


添加网络日志信息

前面我打印出来的log,如果不做什么处理,是没有这些log的,在retrofit要设置,请求的和返回的log才会打印出来,特别是我们的开发阶段,一定要打log,又可能一个问题,看log就直接解决了,比如后面返回的json数据一个类型出错了,就会导致你转bean抛错误了,在此一定别忘记了添加依赖

compile 'com.squareup.okhttp3:logging-interceptor:3.9.1

关于拦截器都是在OkhttpClient中去设置的,

OkHttpClient httpClient = new OkHttpClient();
if (BuildConfig.DEBUG) {
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);
    httpClient = new OkHttpClient.Builder().addInterceptor(logging).build();
}
它有四个等级,看下源码就知道了.
public enum Level {
  /** No logs. */
  NONE,
  /**
   * Logs request and response lines.
   *
   * <p>Example:
   * <pre>{@code
   * --> POST /greeting http/1.1 (3-byte body)
   *
   * <-- 200 OK (22ms, 6-byte body)
   * }</pre>
   */
  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>
   */
  HEADERS,
  /**
   * 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>
   */
  BODY
}
然后设置给retrofit
Retrofit retrofit = new Retrofit.Builder()
        .client(httpClient)
        .baseUrl("http://gank.io")
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .build();
第2行就是了.

body级别的log包含请求头,响应头,以及请求数据都会打印出来。

URL统一追加参数

public class AppendUrlParamInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();//拿到发送的请求
        HttpUrl httpUrl =  request.url().newBuilder().addQueryParameter("version","1.0")
                .addQueryParameter("channel","360")
                .addQueryParameter("versionCode","10")
                .addQueryParameter("deviceId","abasersgdhdjjfs5688")
                .build();

        //重要一步 使用新的url构建一个Request发送给后台
        Request newRequest = request.newBuilder().url(httpUrl).build();
        return chain.proceed(newRequest);
    }
}
添加什么拦截都是实现InterceptorInterceptor接口,然后去里面拿到Request去按需求做你的操作,大部分都是套路代码,写一个遍会了就会了,最后别忘记了把这个拦截器设置给OkHttpClient

完整的代码贴下:

public void append_url_by_interceptor(View view){
    OkHttpClient httpClient = new OkHttpClient();
    if (BuildConfig.DEBUG) {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        //统一在url后面添加参数
        AppendUrlParamInterceptor appendUrlParamInterceptor = new AppendUrlParamInterceptor();
        httpClient = new OkHttpClient.Builder().addInterceptor(logging).addInterceptor(appendUrlParamInterceptor).build();
    }
    Retrofit retrofit = new Retrofit.Builder()
            .client(httpClient)
            .baseUrl("http://gank.io")
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build();
    KSService service = retrofit.create(KSService.class);
    Observable<ResponseBody> observable =  service.getInfoDataByRxjava("Android","10",1);
    observable
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Consumer<ResponseBody>(){
                @Override
                public void accept(ResponseBody responseBody) throws Exception {
                    String s = responseBody.string();
                    Log.e(TAG,"请求的结果:"+s);
                }
            });
}
现在查看log,到底有没有成功


刚才是用get请求方式,那么post请求方式可以么,试试就知道了,

现在定义一个方法去请求我们服务器

@FormUrlEncoded
@POST("aa/HelloServlet")
Observable<ResponseBody> appendUrlParamByInterceptor(@FieldMap Map<String,String> map);
实现
public void append_url_by_post(View view){
    OkHttpClient httpClient = new OkHttpClient();
    if (BuildConfig.DEBUG) {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        //统一在url后面添加参数
        AppendUrlParamInterceptor appendUrlParamInterceptor = new AppendUrlParamInterceptor();
        httpClient = new OkHttpClient.Builder().addInterceptor(logging).addInterceptor(appendUrlParamInterceptor).build();
    }
    Retrofit retrofit = new Retrofit.Builder()
            .client(httpClient)
            .baseUrl("http://192.168.1.107:8080/")
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build();
    KSService service = retrofit.create(KSService.class);
    Map<String,String> map = new HashMap<>();
    map.put("username","zhouguizhi");
    map.put("pwd","123456");
    Observable<ResponseBody> observable =  service.appendUrlParamByInterceptor(map);
    observable
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Consumer<ResponseBody>(){
                @Override
                public void accept(ResponseBody responseBody) throws Exception {
                    String json = responseBody.string();
                    Log.e(TAG,"请求的结果:"+json);
                }
            });
}
看下我服务端端额控制台信息


然后看下客户端的log:


可以看到是post请求,然后是第一次url请求地址也出来了,拦截了后,我们追加的请求参数也打印出来了,


Header统一添加参数

还是用刚才的那个请求,统一添加请求头自定义拦截器如下:

public class AppendHeaderParamInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();//拿到发送的请求
        Headers headers =  request.headers().newBuilder().add("token","mytoken").build();
        Request newRequest = request.newBuilder().headers(headers).build();
        return chain.proceed(newRequest);
    }
}
实现:
public void append_header(View view){
    OkHttpClient httpClient = new OkHttpClient();
    if (BuildConfig.DEBUG) {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        AppendHeaderParamInterceptor header = new AppendHeaderParamInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        //统一添加请求header
        httpClient = new OkHttpClient.Builder().addInterceptor(logging)
                .addInterceptor(header)
                .build();

    }
    Retrofit retrofit = new Retrofit.Builder()
            .client(httpClient)
            .baseUrl("http://192.168.1.107:8080/")
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build();
    KSService service = retrofit.create(KSService.class);
    Map<String,String> map = new HashMap<>();
    map.put("username","zhouguizhi");
    map.put("pwd","123456");
    Observable<ResponseBody> observable =  service.appendUrlParamByInterceptor(map);
    observable
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Consumer<ResponseBody>(){
                @Override
                public void accept(ResponseBody responseBody) throws Exception {
                    String json = responseBody.string();
                    Log.e(TAG,"请求的结果:"+json);
                }
            });
}
观察我服务端打印的请求header头数据


现在看客户端的log:



无网络统一处理

之前项目无网络都是每个接口手动去判断,如果你做了封装的话还好点,至少只要判断一次就行了,而不是等请求发送出去了以后,回来发现给你一个提示无网络或者什么的,我们在请求之前就统一检查网络并处理

还是和以前一样,自定义拦截器,

public class HandleNoNetInterceptor implements Interceptor {
    private Context content;
    public HandleNoNetInterceptor(Context content) {
        this.content = content;
    }
    @Override
    public Response intercept(Chain chain) throws IOException {
        if(NetUtils.isNetworkAvalible(content)){
            return chain.proceed(chain.request());
        }else{
            Handler handler = new Handler(Looper.getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(content,"网络异常,请检查你的网络",Toast.LENGTH_LONG).show();
                }
            });
            throw new RuntimeException("网络异常了~~~~~");
        }
    }
}
因为我们在发送网络请求的时候,是放在子线程中,所以你不使用handler  post这个toast是无法出来的,如果是使用rxjava,他有个onError在这里可以做针对你的需求做处理,
OkHttpClient httpClient = new OkHttpClient();
if (BuildConfig.DEBUG) {
    HandleNoNetInterceptor handleNoNetInterceptor  =new HandleNoNetInterceptor(this);
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);
    //统一添加请求header
    httpClient = new OkHttpClient.Builder()
            .addInterceptor(logging)
            .addInterceptor(handleNoNetInterceptor)
            .build();

}
Retrofit retrofit = new Retrofit.Builder()
        .client(httpClient)
        .baseUrl("http://192.168.1.107:8080/")
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .build();
KSService service = retrofit.create(KSService.class);
Map<String,String> map = new HashMap<>();
map.put("username","zhouguizhi");
map.put("pwd","123456");
Observable<ResponseBody> observable =  service.appendUrlParamByInterceptor(map);
observable
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Observer<ResponseBody>() {
            @Override
            public void onSubscribe(@NonNull Disposable d) {

            }
            @Override
            public void onNext(@NonNull ResponseBody responseBody) {

            }
            @Override
            public void onError(@NonNull Throwable e) {
                Log.e(TAG,"异常消息:"+e.getLocalizedMessage());
            }
            @Override
            public void onComplete() {

            }
        });
千万别忘记了添加权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />



缓存设置

也是要自定义拦截器的,

public class CacheInterceptor implements Interceptor {
    private Context context;
    public CacheInterceptor(Context context) {
        this.context = context;
    }
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        //这是在没网的时候设置缓存
        if(!NetUtils.isNetworkAvalible(context.getApplicationContext())){
            Request cacheRequest = request.newBuilder()
                    .cacheControl(CacheControl.FORCE_CACHE)
                    .build();
            return chain.proceed(cacheRequest);
        }
        Response cacheResponse = chain.proceed(request);
        long cacheMaxTime = 1*60*60;//缓存1小时
        return cacheResponse.newBuilder()
                .header("Cache-Control", "public, only-if-cached, max-stale="+cacheMaxTime)
                .removeHeader("Pragma")
                .build();
    }
}
我这是请求http://www.qq.com,这个缓存必须要服务器支持,如果服务器不支持的话,是会报错的,

 HTTP 504 Unsatisfiable Request (only-if-cached)我刚开始就遇到了这个问题,网上查了下.

请求的方法

@GET
Observable<ResponseBody> request_qq(@Url String url);
最终的实现
public void cache_set(View view){
    OkHttpClient httpClient = new OkHttpClient();
    if (BuildConfig.DEBUG) {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        long cacheSize = 1024*1024*6;
        Cache cache = new Cache(new File(FileUtil.getPath(),"cache_content"),cacheSize);
        httpClient = new OkHttpClient.Builder().cache(cache)
                .addInterceptor(logging)
                .addInterceptor(new CacheInterceptor(this))
                .build();

    }
    Retrofit retrofit = new Retrofit.Builder()
            .client(httpClient)
            .baseUrl("http://192.168.1.107:8080/")
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build();
    KSService service = retrofit.create(KSService.class);
    Observable<ResponseBody> observable =  service.request_qq("http://www.qq.com");
    observable
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Observer<ResponseBody>() {
                @Override
                public void onSubscribe(@NonNull Disposable d) {

                }
                @Override
                public void onNext(@NonNull ResponseBody responseBody) {
                }
                @Override
                public void onError(@NonNull Throwable e) {
                }
                @Override
                public void onComplete() {

                }
            });
}
查看我缓存的文件,在sd下的cache_content文件:


打开缓存文件;



这是需要服务器支持缓存的,如果你服务器不支持缓存,但是你在没网的时候又想缓存的数据,就要自己搞了,


超时,错误重试机制

这个和okHttp一样的,废话本来底层网络就是用okhttp.

.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15,TimeUnit.SECONDS)
.writeTimeout(15,TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
还有个多文件上传,还没弄,这个后期补上,就写到这里吧.

;