OKHTTP
一.为什么要用okhttp
支持HTTP2/SPDY,允许所有同一个主机地址的请求共享同一个Socket连接,减少了三次握手和四次挥手的过程,效率更高
二.一些类的简单介绍
okHttpClient:构建者模式,可以设置拦截器、分发器等参数。
Request:封装的网络请求,请求实体类
Call:exectute()方法执行RealCall的execute方法client.dispatcher().enqueue(new AsyncCall(responseCallback));利用dispatcher调度器enqueueAsyncCall,并通过回调(Callback)获取服务器返回的结果
Dispather:调度器,持有线程池,和三个任务队列,拿到请求封装类,去执行call,拿到执行结果Respose。
Respose:respose.body().byteStream(),拿到分发器中的请求结果。
CallBack:将请求的结果回调给调用者。
三.基本使用
public class OkHttpUtils {
private final static String url = "https://www.httpbin.org/";
private static OkHttpUtils instance;
private OkHttpClient okHttpClient = new OkHttpClient();
private OkHttpUtils() {
}
public static OkHttpUtils getInstance() {
if (null == instance) {
synchronized (OkHttpUtils.class) {
if (null == instance) {
instance = new OkHttpUtils();
}
}
}
return instance;
}
//同步get请求
public void synGet() {
Request request = new Request.Builder().url(url + "get?username=123&password=123").build();
try {
Response response = okHttpClient.newCall(request)
.execute();
System.out.println("threadName:" + Thread.currentThread().getName());
System.out.println(Objects.requireNonNull(response.body().string()));
} catch (IOException | NullPointerException e) {
e.printStackTrace();
}
}
//异步get请求
public void aSynGet() {
Request request = new Request.Builder().url(url + "get?username=123&password=123").get().build();
startCall(request);
}
// form表单 post请求 contentType application/x-www-form-urlencoded,multipart/form-data:需要在表单中进行文件上传时,就需要使用该格式
public void postForm() {
FormBody formBody = new FormBody.Builder().add("use", "abc")
.add("pwd", "123").build();
Request request = new Request.Builder().url(url + "post").post(formBody).build();
startCall(request);
}
//post请求上传二进制文件,只上传单个文件 contentType可查看菜鸟教程https://www.runoob.com/http/http-content-type.html
public void postOctStream() {
try {
RequestBody streamBody = RequestBody.create(MediaType.parse("application/octet-stream"), "123".getBytes("utf-8"));
UploadRequestBody uploadRequestBody = new UploadRequestBody(new UploadRequestBody.ProgressListener() {
@Override
public void progress(String progress, long contentLength, long currentWrite) {
System.out.println("progress:" + progress + "contentLength:" + contentLength + ",currentWrite:" + currentWrite);
}
}, streamBody);
Request request = new Request.Builder().url(url + "post").post(uploadRequestBody).build();
startCall(request);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
//post多类型上传
public void postMultiPart() {
try {
MultipartBody multipartBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("username", "abc")
.addFormDataPart("pwd", "123")
.addFormDataPart("file", "fileName",
RequestBody.create(MediaType.parse("application/octet-stream"), "123".getBytes("utf-8"))).build();
Request request = new Request.Builder().url(url + "post").post(multipartBody).build();
startCall(request);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
//下载
public void downloadImage(ImageView imageView) {
Request request = new Request.Builder().get().url("https://img0.baidu.com/it/u=1501084209,93021381&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500")
.build();
startCall(request, imageView);
}
/**
* 普通拦截器和网络拦截器
*
*/
public void addInterceptor() {
okHttpClient.newBuilder()
.addInterceptor(new Interceptor() {
@Override
public Response intercept( Chain chain) throws IOException {
//前置处理 添加请求头等等
Request request = chain.request().newBuilder()
.addHeader("os", "android")
.addHeader("version", "1.o")
.build();
Response response = chain.proceed(request);
//后置处理
return chain.proceed(request);
}
}).addNetworkInterceptor(new Interceptor() {
@Override
public Response intercept( Chain chain) throws IOException {
System.out.println("version>>>>>" + chain.request().header("version"));
return chain.proceed(chain.request());
}
}).build();
}
/**
* 默认情况下okHttp的缓存是关闭的,需要我们手动开启的
*
* http://mushuichuan.com/2016/03/01/okhttpcache/
*/
public void addCache() {
okHttpClient.newBuilder()
.cache(new Cache(new File("path/cache"), 100))
.build();
}
public void addCookieJar() {
final Map> cookiesMap = new HashMap<>();
okHttpClient.newBuilder()
.cookieJar(new CookieJar() {
@Override
public void saveFromResponse( HttpUrl httpUrl, List list) {
//返回服务器的cookie 存到本地
cookiesMap.put(httpUrl.host(), list);
}
@Override
public List loadForRequest( HttpUrl httpUrl) {
List cookies = cookiesMap.get(httpUrl.host());
return cookies == null ? new ArrayList() : cookies;
}
})
.build();
}
private void startCall(Request request) {
startCall(request, null);
}
//开始请求
private void startCall(final Request request, final ImageView imageView) {
try {
okHttpClient.newCall(request)
.enqueue(new Callback() {
@Override
public void onFailure( Call call, IOException e) {
System.out.println("onFailure:" + e.getMessage());
}
@Override
public void onResponse( Call call, Response response) throws IOException {
System.out.println("threadName:" + Thread.currentThread().getName());
if (null != imageView) {
InputStream inputStream = response.body().byteStream();
File file = new File(imageView.getContext().getFilesDir().getPath() + File.separator + "a.png");
if (!file.exists()) {
System.out.println("file:" + file.getPath());
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
float sum = 0l;
long total = response.body().contentLength();
byte buff[] = new byte[128];
int length;
while ((length = inputStream.read(buff)) != -1) {
fos.write(buff, 0, length);
sum += length;
float speed = sum / total * 100;
String format = (new DecimalFormat("0.00")).format(speed);
String format1 = String.format("%.2f", speed);
System.out.println("speed:" + speed + ",sum:" + sum + ",total:" + total + ",format:" + format + ",format1:" + format1);
}
fos.flush();
final Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
imageView.post(new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(bitmap);
}
});
inputStream.close();
} else {
/**
* 如果先body的string方法后调用byteStream流已经被关闭了,会导致io异常
*/
System.out.println(Objects.requireNonNull(response.body().string()));
}
}
});
} catch (NullPointerException e) {
e.printStackTrace();
}
}
//上传进度
private static class UploadRequestBody extends RequestBody {
private ProgressListener progressListener;
private RequestBody requestBody;
public UploadRequestBody(ProgressListener progressListener, RequestBody requestBody) {
this.progressListener = progressListener;
this.requestBody = requestBody;
}
@Override
public MediaType contentType() {
return requestBody.contentType();
}
@Override
public void writeTo( BufferedSink bufferedSink) throws IOException {
System.out.println(">>>>>");
CountSink countSink = new CountSink(bufferedSink);
BufferedSink buffer = Okio.buffer(countSink);
requestBody.writeTo(buffer);
buffer.flush();
}
@Override
public long contentLength() throws IOException {
return requestBody.contentLength();
}
class CountSink extends ForwardingSink {
long byteWrite;
public CountSink(Sink sink) {
super(sink);
}
/**
* @param source
* @param byteCount 缓冲区大小
* @throws IOException
*/
@Override
public void write( Buffer source, long byteCount) throws IOException {
super.write(source, byteCount);
System.out.println("write>>>>>");
byteWrite += byteCount;
float speed = byteWrite / contentLength() * 100;
String format = (new DecimalFormat("0.00")).format(speed);
progressListener.progress(format, contentLength(), byteWrite);
}
}
public interface ProgressListener {
void progress(String progress, long contentLength, long currentWrite);
}
}
四.请求流程的简单分析
1.Dispather中异步执行和同步执行
异步执行:根据当前call请求数量是否大于64和同一个host请求是否大于5来决定加入到异步等待队列还是执行队列
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
//线程池执行call
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
同步执行:直接将call加入到同步运行队列
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
2.Call的执行过程
Call是个抽象类,它的请求在实现类RealCall中执行,异步请求执行,将请求包装成AsyncCall,并添加到分发器的请求队列中,AsyncCall是RealCall的内部类,它继承自NameRunnable,当线程池执行这个AsyncCall时,会执行他的run方法,而它的run方法就是调用AsyncCall的execute方法,请求执行完后,调用dispatcher分发器的移除运行队列中的这个call。同步执行则是直接把他加入到运行队列中,并阻塞当前线程,直到有结果或异常。
RealCall.java
//同步执行
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
//加入到runningSyncCalls队列
client.dispatcher().executed(this);
//获取返回结果
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
//移除同步执行队列
client.dispatcher().finished(this);
}
}
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
//加入到分发器的队列中
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
Dispatcher.java
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
//执行call
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
//异步执行完成
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
//同步执行完成
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private void finished(Deque calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
//AsyncCall是RealCall的内部类
final class AsyncCall extends NamedRunnable {
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
NamedRunnable.java
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
3.通过拦截器获取请求结果的过程
从上面的call执行过程我们可以看到,它是由分发器通过请求队列和线程池来集中处理的,无论是同步还是异步,请求的结果通过 Response response = getResponseWithInterceptorChain();这个方法返回,那我们就对这个方法进行简单的分析。
Response getResponseWithInterceptorChain() throws IOException {
List interceptors = new ArrayList<>();
//你在new一个okHttpClient加入进去的拦截器,比如日志拦截器等
interceptors.addAll(client.interceptors());
// 在配置 OkHttpClient 时设置的 interceptors;
interceptors.add(retryAndFollowUpInterceptor);
// 请求时,对必要的Header进行一些添加,接收响应时,移除必要的Header
interceptors.add(new BridgeInterceptor(client.cookieJar()));
// 负责读取缓存直接返回、更新缓存
interceptors.add(new CacheInterceptor(client.internalCache()));
// 负责和服务器建立连接
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
//网络拦截器
interceptors.addAll(client.networkInterceptors());
}
// 负责向服务器发送请求数据、从服务器读取响应数据
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
最后用一张图来对执行过程做个总结
共有 0 条评论