一.拦截器一般作用
<1> URL重定向。
<2> 请求体数据加密。
<3> HEAD动态添加。
<4> 请求日志抓取。
二.基础讲解
<1> 创建新类实现Interceptor接口
public class CustomInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
return null;
}
}
这样,一个拦截器就算完成,具体的实现都在intercept方法中通过操作chain对象来实现。
<2> 添加(注册)拦截器
有两种方法将上面的拦截器添加到OKhttp中。
方法1:addInterceptor方法
private RxAndroidOkhttp(){
//创建okhttp对象 以及连接,读,取超时时间
mOkHttpClient=new OkHttpClient.Builder()
.connectTimeout(DataConstant.nettimeout, TimeUnit.SECONDS)//连接时间
.readTimeout(DataConstant.nettimeout,TimeUnit.SECONDS)//读时间
.writeTimeout(DataConstant.nettimeout,TimeUnit.SECONDS)//写时间
.addInterceptor(new CustomInterceptor())//添加拦截器
.build();
}
方法2:addNetworkInterceptor方法
private RxAndroidOkhttp(){
//创建okhttp对象 以及连接,读,取超时时间
mOkHttpClient=new OkHttpClient.Builder()
.connectTimeout(DataConstant.nettimeout, TimeUnit.SECONDS)//连接时间
.readTimeout(DataConstant.nettimeout,TimeUnit.SECONDS)//读时间
.writeTimeout(DataConstant.nettimeout,TimeUnit.SECONDS)//写时间
.addNetworkInterceptor(new CustomInterceptor())//添加拦截器
.build();
}
三.拦截器具体使用之 URL重定向
代码
package com.wjn.okhttpmvpdemo.mode.Interceptor;
import com.wjn.okhttpmvpdemo.MyApplication;
import com.wjn.okhttpmvpdemo.mode.constant.DataConstant;
import com.wjn.okhttpmvpdemo.mode.constant.StringConstant;
import com.wjn.okhttpmvpdemo.mode.utils.BooleanUtils;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import rx.Observable;
import rx.Subscriber;
/**
* Created by wjn on 2017/11/22.
* OkHttp框架 封装方法
*/
public class InterceptorRxAndroidOkhttp {
private OkHttpClient mOkHttpClient = null;//OkHttpClient 对象
private static InterceptorRxAndroidOkhttp mRxAndroidOkhttp = null;//RxAndroidOkhttp 对象
/**
* 构造方法私有化
*/
private InterceptorRxAndroidOkhttp() {
//创建okhttp对象 以及连接,读,取超时时间
mOkHttpClient = new OkHttpClient.Builder()
.connectTimeout(DataConstant.nettimeout, TimeUnit.SECONDS)//连接时间
.readTimeout(DataConstant.nettimeout, TimeUnit.SECONDS)//读时间
.writeTimeout(DataConstant.nettimeout, TimeUnit.SECONDS)//写时间
.addInterceptor(new CustomInterceptor())//添加拦截器
.build();
}
/**
* 获取此单例类对象的方法
*/
public static InterceptorRxAndroidOkhttp getInstance() {
if (null == mRxAndroidOkhttp) {//单例对象为空
synchronized (InterceptorRxAndroidOkhttp.class) {
mRxAndroidOkhttp = new InterceptorRxAndroidOkhttp();
}
}
return mRxAndroidOkhttp;
}
/**
* get请求方法
*/
/**
* get请求方法
*/
public Observable<String> get(final String url) {
//创建被观察者
Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
final Subscriber mSubscriber = subscriber;
//没有取消订阅的时候
if (!mSubscriber.isUnsubscribed()) {
//get请求
Request request = new Request.Builder()
.url(url)
.get()
.build();
if (null != mOkHttpClient) {
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//通知订阅者的错误信息
mSubscriber.onError(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (null != response) {//response 不为空
if (response.isSuccessful()) {//response 请求成功
//通知订阅者的成功信息
mSubscriber.onNext(response.body().string());
} else {//response 请求失败
//通知订阅者的错误信息
IOException IOExceptionx = new IOException();
mSubscriber.onError(IOExceptionx);
}
} else {//response 为空
//通知订阅者的错误信息
IOException IOExceptionx = new IOException();
mSubscriber.onError(IOExceptionx);
}
//通知完毕
mSubscriber.onCompleted();
}
});
}
}
}
});
return observable;
}
}
package com.wjn.okhttpmvpdemo.mode.Interceptor;
import android.util.Log;
import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
public class CustomInterceptor implements Interceptor {
private String newHost = "://api.svipmovie.com";
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
HttpUrl url = request.url();
String scheme = url.scheme();
String host = url.host();
String path = url.encodedPath();
String query = url.encodedQuery();
Log.d("TAG","原url----:"+url.toString());
Log.d("TAG","---------------分解原url---------------------");
Log.d("TAG","scheme----:"+scheme);
Log.d("TAG","host----:"+host);
Log.d("TAG","path----:"+path);
Log.d("TAG","query----:"+query);
//全部参数Key
Set<String> collection = url.queryParameterNames();
Iterator<String> iterable = collection.iterator();
String key = "";
while (iterable.hasNext()) {
key = key + iterable.next() + "###";
}
Log.d("TAG","key:"+key);
//全部参数Value
Set<String> collections = url.queryParameterNames();
Iterator<String> iterables = collections.iterator();
String value = "";
while (iterables.hasNext()) {
value = value + url.queryParameter(iterables.next()) + "###";
}
Log.d("TAG","value:"+value);
StringBuffer sb = new StringBuffer();
String newUrl = sb.append(scheme).append(newHost).append(path).append("?").append(query).toString();
Log.d("TAG","新url----:"+newUrl);
Request.Builder builder = request.newBuilder().url(newUrl);
return chain.proceed(builder.build());
}
}
package com.wjn.okhttpmvpdemo.view.impl.activity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import com.wjn.okhttpmvpdemo.R;
import com.wjn.okhttpmvpdemo.mode.Interceptor.InterceptorRxAndroidOkhttp;
import com.wjn.okhttpmvpdemo.mode.utils.ui.StatusBarUtil;
import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
public class InterceptorActivity extends AppCompatActivity {
private TextView textView;
private InterceptorRxAndroidOkhttp interceptorRxAndroidOkhttp = null;//InterceptorRxAndroidOkhttp
private Observable<String> mObservable = null;//get post 方式请求的Observable对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_interceptor);
//根据状态栏颜色来决定 状态栏背景 用黑色还是白色 true:是否修改状态栏字体颜色
StatusBarUtil.setStatusBarMode(this, true, false, R.color.baise);
textView = findViewById(R.id.activity_interceptor_textview1);
interceptorRxAndroidOkhttp = InterceptorRxAndroidOkhttp.getInstance();
OkhttpTest("http://127.0.0.1/front/columns/getVideoList.do?catalogId=402834815584e463015584e539330016&pnum=1");
}
/**
* 测试
*/
private void OkhttpTest(final String geturl) {
if (null != interceptorRxAndroidOkhttp) {
//observable定义被观察者
mObservable = interceptorRxAndroidOkhttp.get(geturl);
if (null != mObservable) {
//定义观察者
Subscriber<String> mSubscriber = new Subscriber<String>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(String s) {
textView.setText(s);
}
};
/**
* 订阅者关联被观察者
* Schedulers.io()说明是输入输出的计划任务
* AndroidSchedulers.mainThread()说明订阅者是中ui主线程中执行
* */
mObservable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(mSubscriber);
}
}
}
}
结果
成功将url重定向成新的url。
即
原Url
http://127.0.0.1/front/columns/getVideoList.do?catalogId=402834815584e463015584e539330016&pnum=1
重定向后Url
说明
在编写代码时时常会有几位同事写接口,一般都是先在同事的本地接口测试。所以接口中的Host就会是同事本地的。可是项目上线时,需要统一改成服务器上的真实Host。如果把接口Host写成工具类改起来还好说,如果没有那就要一个一个的该。OKhttp框架拦截器(Interceptor)重定向就解决了这一问题。
四.拦截器具体使用之 请求体数据加密
既然要对请求体加密,那肯定要知道请求体在哪里,然后才能加密,其实都一样不论是加密url里面的query内容还是加密body体里面的都一样,只要拿到了对应的数据我们想怎么做怎么。
如上 重定向Url时
package com.wjn.okhttpmvpdemo.mode.Interceptor;
import android.util.Log;
import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
public class CustomInterceptor implements Interceptor {
private String newHost = "://api.svipmovie.com";
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
HttpUrl url = request.url();
String scheme = url.scheme();
String host = url.host();
String path = url.encodedPath();
String query = url.encodedQuery();
Log.d("TAG","原url----:"+url.toString());
Log.d("TAG","---------------分解原url---------------------");
Log.d("TAG","scheme----:"+scheme);
Log.d("TAG","host----:"+host);
Log.d("TAG","path----:"+path);
Log.d("TAG","query----:"+query);
//全部参数Key
Set<String> collection = url.queryParameterNames();
Iterator<String> iterable = collection.iterator();
String key = "";
while (iterable.hasNext()) {
key = key + iterable.next() + "###";
}
Log.d("TAG","key:"+key);
//全部参数Value
Set<String> collections = url.queryParameterNames();
Iterator<String> iterables = collections.iterator();
String value = "";
while (iterables.hasNext()) {
value = value + url.queryParameter(iterables.next()) + "###";
}
Log.d("TAG","value:"+value);
StringBuffer sb = new StringBuffer();
String newUrl = sb.append(scheme).append(newHost).append(path).append("?").append(query).toString();
Log.d("TAG","新url----:"+newUrl);
Request.Builder builder = request.newBuilder().url(newUrl);
return chain.proceed(builder.build());
}
}
既然获取了query也解析出每一个传值的Key Value 就可以将每一个Key对应的Value进行加密。比如
catalogId=“加密后的字符串”
pnum=“加密后的字符串”
新query:catalogId=“加密后的字符串”&pnum=“加密后的字符串”。然后服务器再按照相应的Key得到加密后的Value,然后按照约定的规则解密即可。
Android 加密详解:
https://blog.csdn.net/weixin_37730482/article/category/8490468
五.拦截器具体使用之 HEAD动态添加
package com.wjn.okhttpmvpdemo.mode.Interceptor;
import com.wjn.okhttpmvpdemo.MyApplication;
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
public class CustomInterceptors implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
String token = MyApplication.getOkhttpCookie();
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.addHeader("cookie", token);
Request request = requestBuilder.build();
return chain.proceed(request);
}
}
六.拦截器具体使用之 请求日志抓取
<1> 添加Gradle配置
implementation 'io.reactivex:rxjava:1.3.2'
implementation 'io.reactivex:rxandroid:1.2.1'
implementation 'com.squareup.okhttp3:okhttp:3.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.6.0' //抓取日志额外添加
<2> 自定义HttpLog类
package com.wjn.okhttpmvpdemo.mode.Interceptor;
import android.util.Log;
import okhttp3.logging.HttpLoggingInterceptor;
public class HttpLog implements HttpLoggingInterceptor.Logger {
@Override
public void log(String message) {
Log.d("TAG", "message----:"+message);
}
}
<3> 添加抓取日志的拦截器
/**
* 构造方法私有化
*/
private InterceptorRxAndroidOkhttp() {
Interceptor logInterceptor = new HttpLoggingInterceptor(new HttpLog()).setLevel(HttpLoggingInterceptor.Level.BODY);//抓取日志拦截器对象
//创建okhttp对象 以及连接,读,取超时时间
mOkHttpClient = new OkHttpClient.Builder()
.connectTimeout(DataConstant.nettimeout, TimeUnit.SECONDS)//连接时间
.readTimeout(DataConstant.nettimeout, TimeUnit.SECONDS)//读时间
.writeTimeout(DataConstant.nettimeout, TimeUnit.SECONDS)//写时间
.addInterceptor(logInterceptor)//添加抓取日志的拦截器
.build();
}
<4> 效果
message----:--> GET http://api.svipmovie.com/front/columns/getVideoList.do?catalogId=402834815584e463015584e539330016&pnum=1 http/1.1
message----:cookie: KLBRSID=5d3bae4968490d4e7b52d2fd1d9d601b|1555576213|1555576213
message----:--> END GET
message----:<-- 200 http://api.svipmovie.com/front/columns/getVideoList.do?catalogId=402834815584e463015584e539330016&pnum=1 (59ms)
message----:Server: CLOUD ELB 1.0.0
message----:Date: Thu, 18 Apr 2019 08:30:22 GMT
message----:Content-Type: application/json;charset=UTF-8
message----:Transfer-Encoding: chunked
message----:Connection: keep-alive
message----:Vary: Accept-Encoding
message----:Access-Control-Allow-Headers: Content-Type
message----:Access-Control-Allow-Methods: GET,POST,OPTIONS
message----:Access-Control-Allow-Credentials: true
message----:Set-Cookie: KLBRSID=5d3bae4968490d4e7b52d2fd1d9d601b|1555576222|1555576213; Path=/
message----:
message----:{"msg":"成功","ret":{"adv":{"imgURL":"","dataId":"","htmlURL":"","shareURL":"","title":""},"pnum":1,"totalRecords":14,"bannerList":[],"records":30,"list":[{"airTime":0,"duration":"01:26:06","loadtype":"video","score":0,"angleIcon":"","dataId":"3_e3622954bfd3404d957211028bfb2a71","description":"《动物世界》首映发布会","loadURL":"http://api.svipmovie.com/front/videoDetailApi/videoDetail.do?mediaId=3_e3622954bfd3404d957211028bfb2a71","shareURL":"","pic":"http://phonemovie.ks3-cn-beijing.ksyun.com/image/2018/07/02/1530521662049090886.jpg","title":"《动物世界》首映发布会","roomId":""},{"airTime":0,"duration":"01:24:36","loadtype":"video","score":0,"angleIcon":"","dataId":"3_12ec6b94308a4213b74f8dca13facd91","description":"《幸福马上来》发布会","loadURL":"http://api.svipmovie.com/front/videoDetailApi/videoDetail.do?mediaId=3_12ec6b94308a4213b74f8dca13facd91","shareURL":"","pic":"http://phonemovie.ks3-cn-beijing.ksyun.com/image/2018/07/02/1530521535713073392.jpg","title":"《幸福马上来》发布会","roomId":""},{"airTime":0,"duration":"00:36:18","loadtype":"video","score":0,"angleIcon":"","dataId":"3_7d19f30f6875451b896a158cf9f1092b","description":"《幸福马上来》首映礼红毯","loadURL":"http://api.svipmovie.com/front/videoDetailApi/videoDetail.do?mediaId=3_7d19f30f6875451b896a158cf9f1092b","shareURL":"","pic":"http://phonemovie.ks3-cn-beijing.ksyun.com/image/2018/07/02/1530521497847016506.jpg","title":"《幸福马上来》首映礼红毯","roomId":""},{"airTime":0,"duration":"01:32:54","loadtype":"video","score":0,"angleIcon":"","dataId":"3_1c76a331e4dd46a2b40c76ff7cb0483c","description":"第十届两岸电影展开幕式","loadURL":"http://api.svipmovie.com/front/videoDetailApi/videoDetail.do?mediaId=3_1c76a331e4dd46a2b40c76ff7cb0483c","shareURL":"","pic":"http://phonemovie.ks3-cn-beijing.ksyun.com/image/2018/07/02/1530521210716057912.jpg","title":"第十届两岸电影展开幕式","roomId":""},{"airTime":0,"duration":"01:46:36","loadtype":"video","score":0,"angleIcon":"","dataId":"3_f164502cdbd94889bb82715cd4679250","description":"《江湖儿女》发布会","loadURL":"http://api.svipmovie.com/front/videoDetailApi/videoDetail.do?mediaId=3_f164502cdbd94889bb82715cd4679250","shareURL":"","pic":"http://phonemovie.ks3-cn-beijing.ksyun.com/image/2018/07/02/1530521132032061519.jpg","title":"《江湖儿女》发布会","roomId":""},{"airTime":0,"duration":"01:21:25","loadtype":"video","score":0,"angleIcon":"","dataId":"3_8cfca4605d764ecc92bfda4cde703a91","description":"《超时空同居》发布会","loadURL":"http://api.svipmovie.com/front/videoDetailApi/videoDetail.do?mediaId=3_8cfca4605d764ecc92bfda4cde703a91","shareURL":"","pic":"http://phonemovie.ks3-cn-beijing.ksyun.com/image/2018/05/15/1526378518433029467.jpg","title":"《超时空同居》发布会","roomId":""},{"airTime":0,"duration":"00:28:18","loadtype":"video","score":0,"angleIcon":"","dataId":"3_83365dd74f7840fb9b691bb392bcae14","description":"电影《路过未来》首映礼","loadURL":"http://api.svipmovie.com/front/videoDetailApi/videoDetail.do?mediaId=3_83365dd74f7840fb9b691bb392bcae14","shareURL":"","pic":"http://phonemovie.ks3-cn-beijing.ksyun.com/image/2018/05/15/1526378488136090900.jpg","title":"电影《路过未来》首映礼","roomId":""},{"airTime":0,"duration":"00:42:06","loadtype":"video","score":0,"angleIcon":"","dataId":"3_5bd93420e18c4442853d30d1126d017e","description":"《市长夫人的秘密》首映会","loadURL":"http://api.svipmovie.com/front/videoDetailApi/videoDetail.do?mediaId=3_5bd93420e18c4442853d30d1126d017e","shareURL":"","pic":"http://phonemovie.ks3-cn-beijing.ksyun.com/image/2018/05/15/1526354059484052494.jpg","title":"《市长夫人的秘密》首映会","roomId":""},{"airTime":0,"duration":"00:49:18","loadtype":"video","score":0,"angleIcon":"","dataId":"3_587358b201f4440db10d852ad518585b","description":"电影《泄密者》发布会","loadURL":"http://api.svipmovie.com/front/videoDetailApi/videoDetail.do?mediaId=3_587358b201f4440db10
message----:<-- END HTTP (6608-byte body)
<5> 注意
HttpLoggingInterceptor的日志级别,一共有四个
分别为:NONE BASIC HEAD BODY
(1) NONO:不打印任何日志
(2) BASIC:请求行,响应行
-->请求方式 请求地址 http/1.1 body长度
<-- 服务器返回状态 地址 时间 长度
(3) HEAD:
请求行,请求头 ,响应行
(4) BODY:
请求行,请求头,请求体,响应行,响应体,
由上可以看出BODY级别是最全的
附:Interceptor GitHub链接