文章目录
OkHttp
简介
- OkHttp是一个类似HttpUrlConnection的一个框架
- 网络请求速度更快,支持HTTP/2技术,提升网络请求速度
- 更省流量,通过GZIP对数据进行压缩并结合缓存机制,从而压缩下载数据的大小、并且在一定的时效内缓存请求的数据,减少短时间内多次请求的流量消耗
基本用法
添加依赖
implementation 'com.squareup.okhttp3:okhttp:3.13.1'
设置权限
<uses-permission android:name="android.permission.INTERNET" />
Get同步请求
String url = "https://wanandroid.com/wxarticle/chapters/json";
final OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder().url(url).build();
Call call = client.newCall(request);
new Thread(() -> {
Response response = null;
try {
response = call.execute();
if (response.code() == 200) {
//response.body().string() 是获取返回的结果,只能调用一次
String result = response.body().string();
Log.e(TAG, result);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
Get异步请求
String url = "https://wanandroid.com/wxarticle/chapters/json";
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String result = response.body().string();
Log.e(TAG, result);
}
});
Post同步请求
String url = "https://www.wanandroid.com/user/login";
final OkHttpClient client = new OkHttpClient();
FormBody body = new FormBody.Builder()
.add("username", "Tom")
.add("password", "****")
.build();
final Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Call call = client.newCall(request);
new Thread(() -> {
Response response = null;
try {
response = call.execute();
if (response.code() == 200) {
String result = response.body().string();
Log.e(TAG, result);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
Post异步请求
String url = "https://www.wanandroid.com/user/login";
OkHttpClient client = new OkHttpClient();
FormBody body = new FormBody.Builder()
.add("username", "Tom")
.add("password", "****")
.build();
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) {
try {
String result = response.body().string();
Log.e(TAG, result);
} catch (IOException e) {
e.printStackTrace();
}
}
});
中止请求
call.cancel();
文件下载
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
String url = "https://www.wanandroid.com/blogimgs/904dd433-1611-41f9-ad69-9df82aed6dad.png";
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) {
InputStream is = response.body().byteStream();
long total = response.body().contentLength();
File dirFile = new File(Environment.getExternalStorageDirectory(), "test");
if (!dirFile.exists()) {
dirFile.mkdirs();
}
File file = new File(dirFile, "image.png");
BufferedInputStream bis;
FileOutputStream fos;
BufferedOutputStream bos;
try {
bis = new BufferedInputStream(is);
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
int len = 0;
int current = 0;
byte[] buffer = new byte[1024];
Log.e(TAG, "start: " + total);
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
current += len;
Log.e(TAG, "progress: " + current);
}
bos.flush();
Log.e(TAG, "end");
} catch (IOException e) {
e.printStackTrace();
}
}
});
OkHttp拦截器
- 拦截器是OkHttp中提供的一种强大机制,它可以实现网络监听、请求以及响应重写、请求失败重试等功能
- 拦截器分2个:APP层拦截器(Application Interceptor)、网络请求层拦截器(Network Interceptor),执行流程:Application Interception --> Network Interceptor --> Application Interception 。
- APP层拦截器:是在请求执行刚开始时拦截
- 网络请求层拦截器:在连接网络之前拦截
拦截器基本使用
//APP层拦截器
Interceptor appInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
HttpUrl url = request.url();
String s = url.url().toString();
Log.e(TAG, "Application Interceptor 开始");
Response response = chain.proceed(request);
Log.e(TAG, "Application Interceptor 结束");
return response;
}
};
//网络请求层拦截器
Interceptor networkInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Log.e(TAG, "Network Interceptor 开始");
Response response = chain.proceed(request);
Log.e(TAG, "Network Interceptor 结束");
return response;
}
};
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.cache(new Cache(getExternalCacheDir(), 500 * 1024 * 1024))
.addInterceptor(appInterceptor)
.addNetworkInterceptor(networkInterceptor)
.build();
输出信息:
appInterceptor 开始
networkInterceptor 开始
networkInterceptor 结束
appInterceptor 结束
接口返回信息......
添加日志拦截器
添加日志框架
implementation 'com.orhanobut:logger:2.2.0'
初始化日志框架
private void initLogger() {
FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
.showThreadInfo(false)
.methodCount(0)
.methodOffset(7)
//.logStrategy(customLog) //缓存策略,
.tag("TAG") //全局tag
.build();
Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy) {
@Override
public boolean isLoggable(int priority, String tag) {
return BuildConfig.DEBUG;
}
});
}
日志拦截器
public class LogInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long start = System.currentTimeMillis();
Response response = chain.proceed(chain.request());
long end = System.currentTimeMillis();
MediaType mediaType = response.body().contentType();
String content = response.body().string();
Logger.wtf("----------Request Start----------------");
Logger.e("| " + request.toString());
Logger.d("| headers: " + getHttpHeaders(request.headers()));
Logger.d("| urlParams: " + getHttpUrlParams(request.url()));
Logger.d("| bodyParams: " + getHttpBodyParams(request));
Logger.json(content);
Logger.e(content);
Logger.wtf("----------Request End:" + (end - start) + "毫秒----------");
return response.newBuilder()
.body(ResponseBody.create(mediaType, content))
.build();
}
/**
* 获取header参数
*/
private Map<String, String> getHttpHeaders(Headers headers) {
Map<String, String> paramMap = new HashMap<>();
if (headers != null && headers.size() > 0) {
for (int i = 0; i < headers.size(); i++) {
paramMap.put(headers.name(i), headers.value(i));
}
}
return paramMap;
}
/**
* 获取url参数
*/
private Map<String, String> getHttpUrlParams(HttpUrl httpUrl) {
Set<String> paramsSet = httpUrl.queryParameterNames();
Map<String, String> paramMap = new HashMap<>();
if (paramsSet != null && paramsSet.size() > 0) {
for (String s : paramsSet) {
paramMap.put(s, httpUrl.queryParameter(s));
}
}
return paramMap;
}
/**
* 获取请求体参数
*/
private Map<String, String> getHttpBodyParams(Request request) {
Map<String, String> paramMap = new HashMap<>();
if (request.body() instanceof FormBody) {
FormBody body = (FormBody) request.body();
for (int i = 0; i < body.size(); i++) {
paramMap.put(body.encodedName(i), body.encodedValue(i));
}
}
return paramMap;
}
}
使用
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new LogInterceptor())
.build();
添加公共参数的拦截器
public class HttpParamInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
//添加公共header
Request request = chain.request().newBuilder()
.addHeader("header1", "headerValue1")
.addHeader("header2", "headerValue2")
.build();
if (request.method().equalsIgnoreCase(GET) || request.method().equalsIgnoreCase(HEAD)) {
//添加公共url参数
HttpUrl httpUrl = request.url().newBuilder()
.addQueryParameter("url1", "urlValue1")
.addQueryParameter("url2", "urlValue2")
.build();
request = request.newBuilder().url(httpUrl).build();
} else {
//添加公共请求体参数
RequestBody requestBody = request.body();
if (requestBody instanceof FormBody) {
FormBody formBody = (FormBody) requestBody;
FormBody.Builder builder = new FormBody.Builder();
for (int i = 0; i < formBody.size(); i++) {
builder.addEncoded(formBody.encodedName(i), formBody.encodedValue(i));
}
FormBody newFormBody = builder
.addEncoded("body1", "bodyValue1")
.addEncoded("body2", "bodyValue2")
.build();
request = request.newBuilder().post(newFormBody).build();
} else if (requestBody instanceof MultipartBody) {
}
}
return chain.proceed(request);
}
}
添加缓存拦截器
Cache-Control说明
public 所有内容都将被缓存(客户端和代理服务器都可缓存)
private 内容只缓存到私有缓存中(仅客户端可以缓存,代理服务器不可缓存)
no-cache no-cache是会被缓存的,只不过每次在向客户端(浏览器)提供响应数据时,缓存都要向服务器评估缓存响应的有效性。
no-store 所有内容都不会被缓存到缓存或 Internet 临时文件中
max-age=xxx (xxx is numeric) 缓存的内容将在 xxx 秒后失效, 这个选项只在HTTP 1.1可用, 并如果和Last-Modified一起使用时, 优先级较高
max-stale和max-age一样,只能设置在请求头里面。
同时设置max-stale和max-age,缓存失效的时间按最长的算。(这个其实不用纠结)
CacheControl.FORCE_CACHE
强制使用缓存,如果没有缓存数据,则抛出504(only-if-cached) CacheControl.FORCE_NETWORK
强制使用网络,不使用任何缓存.
public class HttpCacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// 无网络时,使用本地Cache
if (!isNetworkConnected()) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
}
Response response = chain.proceed(request);
if (isNetworkConnected()) {
//有网的时候读接口上的@Headers里的配置,你可以在这里进行统一的设置
String cacheControl = request.cacheControl().toString();
return response.newBuilder()
.header("Cache-Control", cacheControl)
.removeHeader("Pragma")
.build();
} else {
// 无网络时,设置超时为4周
int maxStale = 60 * 60 * 24 * 28;
return response.newBuilder()
//这里的设置的是我们的没有网络的缓存时间,想设置多少就是多少。
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.removeHeader("Pragma")
.build();
}
}
}
File cacheFile = new File(getExternalCacheDir(), "test");
int cacheSize = 10 * 1024 * 1024;
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new HttpCacheInterceptor())
.cache(new Cache(cacheFile, cacheSize))
.build();
添加失败重连拦截器
OkHttp只会在RouteException
连接异常(如:无网络、连接失败)和IOException
IO异常(如:连接成功后请求阶段发生异常)判断是否需要重试,OkHttp默认最大重试次数为20,我们一般不会设置这么多次,这是可以自定义拦截器。
public class RetryInterceptor implements Interceptor {
//最大重试次数
private int maxRetry;
RetryInterceptor(int maxRetry) {
this.maxRetry = maxRetry;
}
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
Request request = chain.request();
Response response = null;
int count = 0;
while (count < maxRetry) {
try {
//发起网络请求
response = chain.proceed(request);
// 得到结果跳出循环
break;
} catch (Exception e) {
count ++;
response = null;
}
}
if(response == null){
throw Exception
}
return response;
}
}