您当前的位置:首页 > Bug笔记

OkHttp必须弄清楚的几个原理性问题

时间:2022-09-05 19:26:05 阅读数:10,275人阅读
版权声明:转载请注明出处,谢谢!
—— 真正的勇士,敢于直面惨淡的人生。 ——鲁讯

前言

okhttp是目前很火的网络请求框架,Android4.4开始HttpURLConnection的底层就是采用okhttp实现的,其Github地址:https://github.com/square/okhttp

来自官方说明:

		OkHttp is an HTTP client that’s efficient by default:

			HTTP/2 support allows all requests to the same host to share a socket.
			Connection pooling reduces request latency (if HTTP/2 isn’t available).
			Transparent GZIP shrinks download sizes.
			Response caching avoids the network completely for repeat requests.
	  

总结一下,OkHttp支持http2,当然需要你请求的服务端支持才行,针对http1.x,OkHttp采用了连接池降低网络延迟,内部实现gzip透明传输,使用者无需关注,支持http协议上的缓存用于避免重复网络请求。

主流程分析

OkHttp支持同步和异步两种方式请求网络。这里需要注意一下,回调的线程并不是UI线程。同步和异步只是使用方式不同,但其原理都是一样的,最终会走到相同的逻辑,因此这里就直接从异步方式开始分析了,newCall方法会返回一个RealCall对象,看其enqueue方法:

		@Override public void enqueue(Callback responseCallback) {
			synchronized (this) {
			  if (executed) throw new IllegalStateException("Already Executed");
			  executed = true;
			}
			transmitter.callStart();
			client.dispatcher().enqueue(new AsyncCall(responseCallback));
		}
	  

这里有个Dispatcher,顾名思义它就是专门分发和执行请求的,看它的enqueue方法:

		void enqueue(AsyncCall call) {
			synchronized (this) {
			  readyAsyncCalls.add(call);

			  // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
			  // the same host.
			  if (!call.get().forWebSocket) {
				AsyncCall existingCall = findExistingCallWithHost(call.host());
				if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
			  }
			}
			promoteAndExecute();
		}
	  

把call添加到readyAsyncCalls列表中,看promoteAndExecute方法:

		private boolean promoteAndExecute() {
			assert (!Thread.holdsLock(this));

			List<AsyncCall> executableCalls = new ArrayList<>();
			boolean isRunning;
			synchronized (this) {
			  for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
				AsyncCall asyncCall = i.next();

				if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
				if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.

				i.remove();
				asyncCall.callsPerHost().incrementAndGet();
				executableCalls.add(asyncCall);
				runningAsyncCalls.add(asyncCall);
			  }
			  isRunning = runningCallsCount() > 0;
			}

			for (int i = 0, size = executableCalls.size(); i < size; i++) {
			  AsyncCall asyncCall = executableCalls.get(i);
			  asyncCall.executeOn(executorService());
			}

			return isRunning;
		}
	  

把call搬到runningAsyncCalls中,遍历列表,对每个call调用executeOn方法:

		void executeOn(ExecutorService executorService) {
		  assert (!Thread.holdsLock(client.dispatcher()));
		  boolean success = false;
		  try {
			executorService.execute(this);
			success = true;
		  } catch (RejectedExecutionException e) {
			InterruptedIOException ioException = new InterruptedIOException("executor rejected");
			ioException.initCause(e);
			transmitter.noMoreExchanges(ioException);
			responseCallback.onFailure(RealCall.this, ioException);
		  } finally {
			if (!success) {
			  client.dispatcher().finished(this); // This call is no longer running!
			}
		  }
		}
	  

看AsyncCall的execute方法:

		@Override protected void execute() {
		  boolean signalledCallback = false;
		  transmitter.timeoutEnter();
		  try {
			Response response = getResponseWithInterceptorChain();
			responseCallback.onResponse(RealCall.this, response);
		  ......
		}
	  

来到getResponseWithInterceptorChain方法,该方法内部会执行所有具体的处理逻辑,执行结束后,返回一个最终的response,然后回调给外部传入的callback,看看getResponseWithInterceptorChain方法:

		Response getResponseWithInterceptorChain() throws IOException {
			// Build a full stack of interceptors.
			List interceptors = new ArrayList<>();
			interceptors.addAll(client.interceptors());
			interceptors.add(new RetryAndFollowUpInterceptor(client));
			interceptors.add(new BridgeInterceptor(client.cookieJar()));
			interceptors.add(new CacheInterceptor(client.internalCache()));
			interceptors.add(new ConnectInterceptor(client));
			if (!forWebSocket) {
			  interceptors.addAll(client.networkInterceptors());
			}
			interceptors.add(new CallServerInterceptor(forWebSocket));

			Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
				originalRequest, this, client.connectTimeoutMillis(),
				client.readTimeoutMillis(), client.writeTimeoutMillis());

			boolean calledNoMoreExchanges = false;
			try {
			  Response response = chain.proceed(originalRequest);
			  if (transmitter.isCanceled()) {
				closeQuietly(response);
				throw new IOException("Canceled");
			  }
			  return response;
			} catch (IOException e) {
			  calledNoMoreExchanges = true;
			  throw transmitter.noMoreExchanges(e);
			} finally {
			  if (!calledNoMoreExchanges) {
				transmitter.noMoreExchanges(null);
			  }
			}
		  }
	  

可以看到,这里添加了一系列的拦截器,构成拦截器链,请求会沿着这条链依次调用其intercept方法,每个拦截器都做自己该做的工作,最终完成请求,返回最终的response对象。

简单说下链式调用的实现方法:创建一个RealInterceptorChain,传入所有的interceptors,和当前index(从0开始),然后调用RealInterceptorChain的process方法,该方法里,获取到对应的interceptor,然后调用intercept方法,而在intercept方法中,会执行具体的处理逻辑,然后创建一个RealInterceptorChain,传入所有的interceptors,和当前index+1,继续调用RealInterceptorChain的process方法,如此重复直到index超过interceptors个数为止。其实这种实现方式跟Task实现链式调用很类似,整个调用过程会创建一系列的中间对象。

继续回到okhttp,这里其实是一种责任链设计模式,它的优点有:

  • 可以降低逻辑的耦合,相互独立的逻辑写到自己的拦截器中,也无需关注其它拦截器所做的事情。
  • 扩展性强,可以添加新的拦截器。

当然它也有缺点:

  • 因为调用链路长,而且存在嵌套,遇到问题排查其它比较麻烦。

对于OkHttp,我们可以添加自己的拦截器:

	  OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
		builder.addInterceptor(new Interceptor() {
			@Override
			public Response intercept(Chain chain) throws IOException {
				// TODO 自定义逻辑

				return chain.proceed(chain.request());
			}
		});
	  

来到这里,OkHttp的主流程就分析完了,至于具体的缓存逻辑,连接池逻辑,网络请求这些,都是在对应的拦截器里面实现的。

------转载请注明出处,感谢您对原创作者的支持------

有偿提供技术支持、Bug修复、项目外包、毕业设计、大小作业

Android学习小站

Q Q:1095817610

微信:jx-helu

邮箱:1095817610@qq.com

添加请备注"Android学习小站"