自研一个简易版本的OkHTTP

一,背景

为了彻底搞明白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);
    }
}
相关推荐
上官花雨4 小时前
什么是axios?怎么使用axios封装Ajax?
前端·ajax·okhttp
Lucky me.2 天前
本地maven添加jar包
okhttp·maven·jar
少说多做3433 天前
Android 网络请求(二)OKHttp网络通信
android·网络·okhttp
小白学大数据5 天前
使用OkHttp进行HTTPS请求的Kotlin实现
爬虫·python·okhttp·https·kotlin
小白学大数据6 天前
虎扑APP数据采集:JavaScript与AJAX的结合使用
开发语言·javascript·爬虫·ajax·okhttp
柯南二号6 天前
Android okhttp 网络链接各阶段监控
android·网络·okhttp
_Soy_Milk7 天前
动态网页爬取 —— ajax 与 selenium
selenium·ajax·okhttp
陈逸轩*^_^*7 天前
AJAX笔记 (速通精华版)
笔记·ajax·okhttp
Chrison_mu7 天前
Android开发|关于Okhttp发送网络请求
android·网络·okhttp
zpjing~.~8 天前
async 和 await的使用
okhttp