开源框架(一): OKHttp源码浅析 请求流程

OKHttp 原理解析

OkHttp是当下Android使用最频繁的网络请求框架,由Square公司开源。Google在Android4.4以后开始将源码中 的HttpURLConnection底层实现替换为OKHttp,同时现在流行的Retrofit框架底层同样是使用OKHttp的。

优点:

1.谷歌官方在6.0以后再android sdk已经移除了httpclient,加入了okhttp.

2.okhttp支持SPDY(是谷歌基于TCP的应用层协议,用于最小化网络延迟,提升网络速度,是对http的一种增强),允许所有的访问统一主机的请求共享一个socket。

3.利用连接池,复用Socket,减少请求延迟

4.支持GZIP压缩, 减少数据流

5.响应缓存减少重复的请求,利用响应缓存来避免重复的网络请求。

即便是网络出现问题时,okhttp依然起作用.它将从常见的链接问题当中恢复.如果你的服务器有多个IP地址,当第一个失败时,okhttp会自动尝试连接其他的地址。

1.0 使用流程

http请求主要是GetPost。二者都分为的同步和异步请求。

[可以参考这篇文章](OkHttp基本使用流程总结 - 简书 (jianshu.com))

1. OkhttpClient mClient = new OkhttpClient()
2. Call call = mClient.newCall(Request request));
			  // GET请求
            Request request = builder.url(url)//请求地址
                         .get()//请求方法,不写默认GET方法
                         .build();
               // POST 请求
            Request request = builder.url(url)
                         .Post(RequestBody requestBody)//这个就是请求体
                         .build();
                }
3. call.enqueue(Callback calBack)//异步执行
3. Response res = call.excute();//同步阻塞
	// Call 的 execute 代表了同步请求,而 enqueue 则代表异步请求。两者唯一区别在于一个会直接发起网络请求,而另一个使用OkHttp内置的线程池来进行。这就涉及到OkHttp的任务分发器。
另一个使用OkHttp内置的线程池来进行。这就涉及到OkHttp的任务分发器。
4. new Callback() {
        @Override public void onFailure(Request request, Throwable throwable) {
              throwable.printStackTrace();
                }
        @Override public void onResponse(Response response) throws IOException {
				// response.body()是ResponseBody类代表响应体。							
   				responseBody.string();//获得字符串的表达形式。
				responseBody.bytes();//获得字节数组的表达形式, 这两种形式都会把文档加入到内存。
				responseBody.charStream();
            	responseBody.byteStream();//返回流来处理。
            }
}

完整的流程图如下:

img

1.1 浅析设计模式

建造者模式:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

OkHttpClient 和 Request 的创建可以使用它为我们提供的 Builder (建造者模式)。实例化 OKHttpClient和Request的时候,因为有太多的属性需要设置,而且开发者的需求组合千变万化,使用建造 者模式可以让用户不需要关心这个类的内部细节,配置好后,建造者会帮助我们按部就班的初始化表示对象

门面模式:将整个系统的复杂性给隐藏起来,将子系统接口通过一个客户端 OkHttpClient统一暴露出来。

OkHttpClient 中全是一些配置,比如代理的配置、ssl证书的配置等。

责任链设计模式 : 对象行为型模式, 为请求创建了一个接收对象的责任链,在处理请求的时候执行过滤,各司其职

OKHttp 五大拦截器就就是责任链设计模式,各司其职,一层一层向下传递。

Demo


public static void main(String[] args) {
        List<Interceptor> interceptors = new ArrayList<>();
        interceptors.add(new RetryAndFollowUpInterceptor());
        interceptors.add(new BridgeInterceptor());
        interceptors.add(new CacheInterceptor());
        interceptors.add(new ConnectInterceptor());
        interceptors.add(new CallServerInterceptor());

        //链条对象  index
        Chain chain = new Chain(interceptors, 0);
        System.out.println(chain.processd("Http请求"));

    }
// 链条的Process方法
      public String processd(String request) {
        if (index >= interceptors.size()) {
            throw new AssertionError();
        }
        // index +1 过度到下一个拦截器
        Chain chain = new Chain(interceptors, index + 1, request);
        Interceptor interceptor = interceptors.get(index);// 取出拦截器
        return interceptor.intercept(chain);
    }  
// 拦截器的intercept方法
    @Override
    public String intercept(Chain chain) {
        //可以在执行下一个拦截器之前,做自己的事情
        System.out.println("开始执行重试重定向拦截器");
        // 执行下一个拦截器
        String result = chain.processd(chain.request + "==>经过重试重定向拦截器");
        //获得结果后,加一些自己的东西
        System.out.println("结束执行重试重定向拦截器");
        return result + "==>经过重试重定向拦截器";
    }

1.2 分发器Dispatcher

execute 使用OkHttp内置的线程池来进行。这就涉及到OkHttp的任务分发器。

//异步请求同时存在的最大请求
private int maxRequests = 64;
//异步请求同一域名同时存在的最大请求
private int maxRequestsPerHost = 5;
//闲置任务(没有请求时可执行一些任务,由使用者设置)
private @Nullable Runnable idleCallback;
//异步请求使用的线程池
private @Nullable ExecutorService executorService;
//异步请求等待执行队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//异步请求正在执行队列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//同步请求正在执行队列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

当正在执行的任务未超过最大限制64,同时 runningCallsForHost(call) < maxRequestsPerHost 同一Host的请求 不超过5个,则会添加到正在执行队列,同时提交给线程池。否则先加入等待队列。 加入等待队列后,就需要等待有空闲名额才开始执行。每次执行完 一个请求后,都会调用分发器的 finished 方法

synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
	runningAsyncCalls.add(call);
	executorService().execute(call);
} else {
	readyAsyncCalls.add(call);
	}
}

异步请求

public Response execute() throws IOException {
	synchronized (this) {
	if (executed) throw new IllegalStateException("Already 	Executed");
		executed = true;
	}
	captureCallStackTrace();
	eventListener.callStart(this);
	try {
		//调用分发器
		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);
	}
}

异步请求的后续同时是调用 getResponseWithInterceptorChain() 来执行请求,异步请求会把一个 AsyncCall 提交给分发器。 AsyncCall 实际上是一个 Runnable 的子类,使用线程启动一个 Runnable 时会执行 run 方法,在 AsyncCall 中被 重定向到 execute 方法:

同步请求

@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));
}

可以看到无论是同步还是异步请求实际上真正执行请求的工作都在 getResponseWithInterceptorChain() 中。这个 方法就是整个OkHttp的核心:拦截器责任链。

请求完成

//异步请求调用
void finished(AsyncCall call) {
	finished(runningAsyncCalls, call, true);
}
//同步请求调用
void finished(RealCall call) {
	finished(runningSyncCalls, call, false);
}

private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
	int runningCallsCount;
	Runnable idleCallback;
	synchronized (this) {
		//不管异步还是同步,执行完后都要从队列移除(runningSyncCalls/runningAsyncCalls)
	if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
	if (promoteCalls) promoteCalls();
		//异步任务和同步任务正在执行的和
		runningCallsCount = runningCallsCount();
		idleCallback = this.idleCallback;
        // 有异步任务才会存在限制与等待,所以在执行完了移除正在执行队列中的元素后,异步任务结束会执行 promoteCalls() 。很显然这个方法肯定会重新调配请求。
	}
		// 没有任务执行执行闲置任务
		if (runningCallsCount == 0 && idleCallback != null) {
		idleCallback.run();
	}
}

有异步任务才会存在限制与等待,所以在执行完了移除正在执行队列中的元素后,异步任务结束会执行 promoteCalls() 。很显然这个方法肯定会重新调配请求。

private void promoteCalls() {
	//如果任务满了直接返回
	if (runningAsyncCalls.size() >= maxRequests) return;
	//没有等待执行的任务,返回
	if (readyAsyncCalls.isEmpty()) return;
	//遍历等待执行队列
	for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
	AsyncCall call = i.next();
	//等待任务想要执行,还需要满足:这个等待任务请求的Host不能已经存在5个了
	if (runningCallsForHost(call) < maxRequestsPerHost) {
		i.remove();
		runningAsyncCalls.add(call);
		executorService().execute(call);
	}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}

满足条件下,会把等待队列中的任务移动到 runningAsyncCalls 并交给线程池执行。

posted @ 2021-05-04 14:42  AronJudge  阅读(280)  评论(0编辑  收藏  举报