一,背景
为了彻底搞明白okhttp原理,仿照okhttp自研一个
二,思路
业务上没发出一个request,使用AsyncCall包装起来,然后在网络分发器的作用下,执行具体的每一个Call,这些具体的Call会经过层层的拦截器,最终会调用到CallServiceInterceptor,这个对象里面有一个ConnectionPool,保存着每一个url对应的socket,最终处理的结果返回交给具体的每一个call,然后在回调到业务层
三,相关类图
四,具体代码实现
4.1 request
public class Request {
private Map<String, String> headers;
private String method;
private HttpUrl url;
private RequestBody body;
public Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers;
this.body = builder.body;
}
public String method() {
return method;
}
public HttpUrl url() {
return url;
}
public RequestBody body() {
return body;
}
public Map<String, String> headers() {
return headers;
}
public final static class Builder {
HttpUrl url;
Map<String, String> headers = new HashMap<>();
String method;
RequestBody body;
public Builder url(String url) {
try {
this.url = new HttpUrl(url);
return this;
} catch (MalformedURLException e) {
throw new IllegalStateException("Failed Http Url", e);
}
}
public Builder addHeader(String name, String value) {
headers.put(name, value);
return this;
}
public Builder removeHeader(String name) {
headers.remove(name);
return this;
}
public Builder get() {
method = "GET";
return this;
}
public Builder post(RequestBody body) {
this.body = body;
method = "POST";
return this;
}
public Request build() {
if (url == null) {
throw new IllegalStateException("url == null");
}
if (TextUtils.isEmpty(method)) {
method = "GET";
}
return new Request(this);
}
}
}
4.2 Response
public class Response {
int code;
int contentLength = -1;
Map<String, String> headers = new HashMap<>();
String body;
//保持连接
boolean isKeepAlive;
public Response() {
}
public Response(int code, int contentLength, Map<String, String> headers, String body,
boolean isKeepAlive) {
this.code = code;
this.contentLength = contentLength;
this.headers = headers;
this.body = body;
this.isKeepAlive = isKeepAlive;
}
public int getCode() {
return code;
}
public int getContentLength() {
return contentLength;
}
public Map<String, String> getHeaders() {
return headers;
}
public String getBody() {
return body;
}
public boolean isKeepAlive() {
return isKeepAlive;
}
}
4.3 HttpUrl
public class HttpUrl {
String protocol;
String host;
String file;
int port;
public HttpUrl(String url) throws MalformedURLException {
URL url1 = new URL(url);
host = url1.getHost();
file = url1.getFile();
file = TextUtils.isEmpty(file) ? "/" : file;
protocol = url1.getProtocol();
port = url1.getPort();
port = port == -1 ? url1.getDefaultPort() : port;
}
public String getProtocol() {
return protocol;
}
public String getHost() {
return host;
}
public String getFile() {
return file;
}
public int getPort() {
return port;
}
}
4.4 Call
public class Call {
Request request;
HttpClient client;
//是否执行过
boolean executed;
boolean cancel;
public boolean isCancel() {
return cancel;
}
public Request getRequest() {
return request;
}
public Call(Request request, HttpClient client) {
this.request = request;
this.client = client;
}
public HttpClient getClient() {
return client;
}
public void enqueue(Callback callback) {
synchronized (this) {
if (executed) {
throw new IllegalStateException("已经执行过了,就不要执行");
}
executed = true;
}
//把任务交给调度器调度
client.dispatcher().enqueue(new AsyncCall(callback));
}
/**
* 是否取消
*/
public void cancel() {
cancel = true;
}
/**
* 执行网络请求的线程
*/
class AsyncCall implements Runnable {
private Callback callback;
public AsyncCall(Callback callback) {
this.callback = callback;
}
@Override
public void run() {
//是否回调过
boolean singaledCallbacked = false;
try {
//执行真正的请求
Response response = getResponse();
if (cancel) {
singaledCallbacked = true;
callback.onFailure(Call.this, new IOException("客户端主动执行了cancel"));
} else {
singaledCallbacked = true;
callback.onResponse(Call.this, response);
}
} catch (Exception e) {
// e.printStackTrace();
if (!singaledCallbacked) {
//如果没有回调过
callback.onFailure(Call.this, e);
}
} finally {
//将这个任务从调度器移除
client.dispatcher().finished(this);
}
}
public String host() {
return request.url().getHost();
}
}
/**
* 这里是重点!!!
* @return
*/
private Response getResponse() throws Exception{
//创建拦截器责任链
List<Interceptor> interceptors = new ArrayList();
//重试拦截器
interceptors.add(new RetryInterceptor());
//请求头拦截器
interceptors.add(new HeaderInterceptor());
//缓存拦截器
interceptors.add(new CacheInterceptor());
//连接拦截器
interceptors.add(new ConnectionInterceptor());
//通信拦截器
interceptors.add(new CallServiceInterceptor());
InterceptorChain chain = new InterceptorChain(interceptors, 0, this, null);
return chain.process();
}
}
4.5 InterceptorChain
public class InterceptorChain {
List<Interceptor> interceptors;
int index;
Call call;
HttpConnection httpConnection;
public InterceptorChain(List<Interceptor> interceptors, int index, Call call, HttpConnection connection) {
this.interceptors = interceptors;
this.index = index;
this.call = call;
this.httpConnection = connection;
}
public Response process(HttpConnection httpConnection) throws IOException{
this.httpConnection = httpConnection;
return process();
}
public Response process() throws IOException {
if (index >= interceptors.size()) throw new IOException("Interceptor China index out max length");
//获得拦截器 去执行
Interceptor interceptor = interceptors.get(index);
InterceptorChain next = new InterceptorChain(interceptors, index + 1, call, httpConnection);
Response response = interceptor.intercept(next);
return response;
}
}
4.6 Interceptor
public interface Interceptor {
Response intercept(InterceptorChain chain) throws IOException;
}
4.7 ConnectionPool
public class ConnectionPool {
private static final String TAG = "ConnectionPool";
private static final boolean DEBUG = BuildConfig.DEBUG;
private final long keepAlive;
private boolean cleanrunning;
private Deque<HttpConnection> connections = new ArrayDeque<>();
public ConnectionPool() {
this(1, TimeUnit.MINUTES);
}
public ConnectionPool(long keepAlive, TimeUnit unit) {
this.keepAlive = unit.toMillis(keepAlive);
}
private Runnable cleanupRunable = new Runnable() {
@Override
public void run() {
while (true) {
long waitDuration = cleanup(System.currentTimeMillis());
if (waitDuration == -1) {
return;
}
if (waitDuration > 0) {
synchronized (ConnectionPool.this) {
try {
ConnectionPool.this.wait(waitDuration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
};
private long cleanup(long currentTimeMillis) {
long longgetIdleDuration = -1;
synchronized (this) {
Iterator<HttpConnection> iterator = connections.iterator();
while (iterator.hasNext()) {
HttpConnection connection = iterator.next();
long idleDuration = currentTimeMillis - connection.getLastUseTime();
//超过了最大允许闲置时间
if (idleDuration > keepAlive) {
if (DEBUG) Log.d(TAG, "ConnectionPool cleanup: " + "超出闲置时间,移除连接池");
iterator.remove();
connection.close();
continue;
}
//没有超过闲置时间
//记录 最长的闲置时间
if (longgetIdleDuration < idleDuration) {
longgetIdleDuration = idleDuration;
}
}
//假如keepAlive是10s
//
if (longgetIdleDuration >= 0) {
return keepAlive - longgetIdleDuration;
}
//连接池中没有连接
cleanrunning = false;
return longgetIdleDuration;
}
}
private static final Executor executer = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "Connection Pool");
thread.setDaemon(true);//设置为守护线程,可以理解为跟进程同样的生命周期
return thread;
}
});
public void put(HttpConnection httpConnection) {
//如果没有执行清理线程
if (!cleanrunning) {
cleanrunning = true;
executer.execute(cleanupRunable);
}
connections.add(httpConnection);
}
public synchronized HttpConnection get(String host, int port) {
Iterator<HttpConnection> iterator = connections.iterator();
while (iterator.hasNext()) {
HttpConnection connection = iterator.next();
//如果查找到连接池始终在相同的host和port
if (connection.isSameAddress(host, port)) {
iterator.remove();
return connection;
}
}
return null;
}
}
4.8 CallServiceInterceptor
public class CallServiceInterceptor implements Interceptor {
private static final String TAG = "CallServiceInterceptor";
private static final boolean DEBUG = BuildConfig.DEBUG;
@Override
public Response intercept(InterceptorChain chain) throws IOException {
if (DEBUG) Log.d(TAG, "CallServiceInterceptor intercept: " + "通信拦截器");
HttpConnection connection = chain.httpConnection;
HttpCode httpCode = new HttpCode();
InputStream inputStream = connection.call(httpCode);
String statusLine = httpCode.readLine(inputStream);
Map<String, String> headers = httpCode.readHeaders(inputStream);
int contentLength = -1;
if (headers.containsKey("Content-Length")) {
//如果有content-length,代表可以拿到响应体的字节长度
contentLength = Integer.valueOf(headers.get("Content-Length"));
}
boolean isChunked = false;
if (headers.containsKey("Transfer-Encoding")) {
//如果有有Transfer-Encoding,表示是分块编码,此时没有响应体的长度
isChunked = headers.get("Transfer-Encoding").equalsIgnoreCase("chunked");
}
String body = null;
if (contentLength > 0) {
byte[] bytes = httpCode.readBytes(inputStream, contentLength);
body = new String(bytes);
} else if (isChunked) {
body = httpCode.readChunked(inputStream);
}
String[] status = statusLine.split(" ");
boolean isKeepAlive = false;
if (headers.containsKey("Connection")) {
isKeepAlive = headers.get("Connection").equalsIgnoreCase("Keep-Alive");
}
connection.updateLastUseTime();
return new Response(Integer.valueOf(status[1]), contentLength, headers, body,isKeepAlive);
}
}