自研一个简易版本的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);
    }
}
相关推荐
洛克大航海21 小时前
Ajax基本使用
java·javascript·ajax·okhttp
whltaoin7 天前
Java 网络请求 Jar 包选型指南:从基础到实战
java·http·okhttp·网络请求·retrofit
华农第一蒟蒻8 天前
谈谈跨域问题
java·后端·nginx·安全·okhttp·c5全栈
一直向钱9 天前
android 基于okhttp的socket封装
android·okhttp
linuxxx1109 天前
ajax回调钩子的使用简介
okhttp
一直向钱11 天前
android 基于okhttp 封装一个websocket管理模块,方便开发和使用
android·websocket·okhttp
linuxxx11011 天前
ajax() 回调函数参数详解
前端·ajax·okhttp
linuxxx11014 天前
ajax与jQuery是什么关系?
ajax·okhttp·jquery
耀耀_很无聊16 天前
12_OkHttp初体验
okhttp
heeheeai16 天前
okhttp使用指南
okhttp·kotlin·教程