一,背景
为了彻底搞明白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;}@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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和portif (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;@Overridepublic 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);}
}