2024年Android最全Android第三方库源码解析:OKHttp(1),2024年最新我总结了24家大厂100份面试题

文末

那么对于想坚持程序员这行的真的就一点希望都没有吗?
其实不然,在互联网的大浪淘沙之下,留下的永远是最优秀的,我们考虑的不是哪个行业差哪个行业难,就逃避掉这些,无论哪个行业,都会有他的问题,但是无论哪个行业都会有站在最顶端的那群人。我们要做的就是努力提升自己,让自己站在最顶端,学历不够那就去读,知识不够那就去学。人之所以为人,不就是有解决问题的能力吗?挡住自己的由于只有自己。
Android希望=技能+面试

  • 技能
  • 面试技巧+面试题

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

}

// StreamAllocation 对象,它相当于一个管理类,维护了服务器连接、并发流

// 和请求之间的关系,该类还会初始化一个 Socket 连接对象,获取输入/输出流对象。

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,

RealConnection connection) throws IOException {

...



// Call the next interceptor in the chain.

// 实例化下一个拦截器对应的RealIterceptorChain对象

RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,

    connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,

    writeTimeout);

// 得到当前的拦截器

Interceptor interceptor = interceptors.get(index);

// 调用当前拦截器的intercept()方法,并将下一个拦截器的RealIterceptorChain对象传递下去,最后得到响应

Response response = interceptor.intercept(next);



...



return response;

}

复制代码




#### []( )3.异步请求的流程



Request request = new Request.Builder()

.url("http://publicobject.com/helloworld.txt")

.build();

client.newCall(request).enqueue(new Callback() {

@Override 

public void onFailure(Call call, IOException e) {

  e.printStackTrace();

}



@Override 

public void onResponse(Call call, Response response) throws IOException {

    ...

}

void enqueue(AsyncCall call) {

synchronized (this) {

    readyAsyncCalls.add(call);

}

promoteAndExecute();

}

// 正在准备中的异步请求队列

private final Deque readyAsyncCalls = new ArrayDeque<>();

// 运行中的异步请求

private final Deque runningAsyncCalls = new ArrayDeque<>();

// 同步请求

private final Deque runningSyncCalls = new ArrayDeque<>();

// Promotes eligible calls from {@link #readyAsyncCalls} to {@link #runningAsyncCalls} and runs

// them on the executor service. Must not be called with synchronization because executing calls

// can call into user code.

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();



    // 如果其中的runningAsynCalls不满,且call占用的host小于最大数量,则将call加入到runningAsyncCalls中执行,

    // 同时利用线程池执行call;否者将call加入到readyAsyncCalls中。

    if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.

    if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.



    i.remove();

    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;

}

复制代码




最后,我们在看看AsynCall的代码。



final class AsyncCall extends NamedRunnable {

private final Callback responseCallback;



AsyncCall(Callback responseCallback) {

  super("OkHttp %s", redactedUrl());

  this.responseCallback = responseCallback;

}



String host() {

  return originalRequest.url().host();

}



Request request() {

  return originalRequest;

}



RealCall get() {

  return RealCall.this;

}



/**

 * Attempt to enqueue this async call on {@code    executorService}. This will attempt to clean up

 * if the executor has been shut down by reporting    the call as failed.

 */

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

    eventListener.callFailed(RealCall.this, ioException);

    responseCallback.onFailure(RealCall.this, ioException);

  } finally {

    if (!success) {

      client.dispatcher().finished(this); // This call is no longer running!

    }

  }

}



@Override protected void execute() {

  boolean signalledCallback = false;

  timeout.enter();

  try {

    // 跟同步执行一样,最后都会调用到这里

    Response response = getResponseWithInterceptorChain();

    if (retryAndFollowUpInterceptor.isCanceled()) {

      signalledCallback = true;

      responseCallback.onFailure(RealCall.this, new   IOException("Canceled"));

    } else {

      signalledCallback = true;

      responseCallback.onResponse(RealCall.this,   response);

    }

  } catch (IOException e) {

    e = timeoutExit(e);

    if (signalledCallback) {

      // Do not signal the callback twice!

      Platform.get().log(INFO, "Callback failure   for " + toLoggableString(), e);

    } else {

      eventListener.callFailed(RealCall.this, e);

      responseCallback.onFailure(RealCall.this, e);

    }

  } finally {

    client.dispatcher().finished(this);

  }

}

}

复制代码




从上面的源码可以知道,拦截链的处理OKHttp帮我们默认做了五步拦截处理,其中RetryAndFollowUpInterceptor、BridgeInterceptor、CallServerInterceptor内部的源码很简洁易懂,此处不再多说,下面将对OKHttp最为核心的两部分:缓存处理和连接处理(连接池)进行讲解。



### []( )二、网络请求缓存处理之CacheInterceptor



@Override public Response intercept(Chain chain) throws IOException {

// 根据request得到cache中缓存的response

Response cacheCandidate = cache != null

    ? cache.get(chain.request())

    : null;



long now = System.currentTimeMillis();



// request判断缓存的策略,是否要使用了网络,缓存或两者都使用

CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(),     cacheCandidate).get();

Request networkRequest = strategy.networkRequest;

Response cacheResponse = strategy.cacheResponse;



if (cache != null) {

  cache.trackResponse(strategy);

}



if (cacheCandidate != null && cacheResponse == null) {

  closeQuietly(cacheCandidate.body()); // The cache   candidate wasn't applicable. Close it.

}



// If we're forbidden from using the network and the cache is insufficient, fail.

if (networkRequest == null && cacheResponse == null) {

  return new Response.Builder()

      .request(chain.request())

      .protocol(Protocol.HTTP_1_1)

      .code(504)

      .message("Unsatisfiable Request (only-if-cached)")

      .body(Util.EMPTY_RESPONSE)

      .sentRequestAtMillis(-1L)

      .receivedResponseAtMillis(System.currentTimeMillis())

      .build();

}



// If we don't need the network, we're done.

if (networkRequest == null) {

  return cacheResponse.newBuilder()

      .cacheResponse(stripBody(cacheResponse))

      .build();

}



Response networkResponse = null;

try {

    // 调用下一个拦截器,决定从网络上来得到response

    networkResponse = chain.proceed(networkRequest);

} finally {

    // If we're crashing on I/O or otherwise,   don't leak the cache body.

    if (networkResponse == null && cacheCandidate != null) {

      closeQuietly(cacheCandidate.body());

    }

}



// If we have a cache response too, then we're doing a conditional get.

// 如果本地已经存在cacheResponse,那么让它和网络得到的networkResponse做比较,决定是否来更新缓存的cacheResponse

if (cacheResponse != null) {

    if (networkResponse.code() == HTTP_NOT_MODIFIED)   {

      Response response = cacheResponse.newBuilder()

              .headers(combine(cacheResponse.headers(), networkResponse.headers()))

              .sentRequestAtMillis(networkResponse.sentRequestAtMillis())

              .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())

              .cacheResponse(stripBody(cacheResponse))

              .networkResponse(stripBody(networkResponse))

          .build();

      networkResponse.body().close();



      // Update the cache after combining headers but before stripping the

      // Content-Encoding header (as performed by initContentStream()).

      cache.trackConditionalCacheHit();

      cache.update(cacheResponse, response);

      return response;

    } else {

      closeQuietly(cacheResponse.body());

    }

}



Response response = networkResponse.newBuilder()

    .cacheResponse(stripBody(cacheResponse))

    .networkResponse(stripBody(networkResponse))

    .build();



if (cache != null) {

  if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response,   networkRequest)) {

    // Offer this request to the cache.

    // 缓存未经缓存过的response

    CacheRequest cacheRequest = cache.put(response);

    return cacheWritingResponse(cacheRequest, response);

  }



  if (HttpMethod.invalidatesCache(networkRequest.method())) {

    try {

      cache.remove(networkRequest);

    } catch (IOException ignored) {

      // The cache cannot be written.

    }

  }

}



return response;

}

复制代码




缓存拦截器会根据请求的信息和缓存的响应的信息来判断是否存在缓存可用,如果有可以使用的缓存,那么就返回该缓存给用户,否则就继续使用责任链模式来从服务器中获取响应。当获取到响应的时候,又会把响应缓存到磁盘上面。



### []( )三、ConnectInterceptor之连接池



@Override public Response intercept(Chain chain) throws IOException {

RealInterceptorChain realChain = (RealInterceptorChain) chain;

Request request = realChain.request();

StreamAllocation streamAllocation = realChain.streamAllocation();



// We need the network to satisfy this request.     Possibly for validating a conditional GET.

boolean doExtensiveHealthChecks = !request.method().equals("GET");

// HttpCodec是对 HTTP 协议操作的抽象,有两个实现:Http1Codec和Http2Codec,顾名思义,它们分别对应 HTTP/1.1 和 HTTP/2 版本的实现。在这个方法的内部实现连接池的复用处理

HttpCodec httpCodec = streamAllocation.newStream(client, chain,     doExtensiveHealthChecks);

RealConnection connection = streamAllocation.connection();



return realChain.proceed(request, streamAllocation, httpCodec, connection);

}

// Returns a connection to host a new stream. This // prefers the existing connection if it exists,

// then the pool, finally building a new connection.

// 调用 streamAllocation 的 newStream() 方法的时候,最终会经过一系列

// 的判断到达 StreamAllocation 中的 findConnection() 方法

private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,

int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {

  ...



  // Attempt to use an already-allocated connection. We need to be careful here because our

  // already-allocated connection may have been restricted from creating new streams.

  // 尝试使用已分配的连接,已经分配的连接可能已经被限制创建新的流

  releasedConnection = this.connection;

  // 释放当前连接的资源,如果该连接已经被限制创建新的流,就返回一个Socket以关闭连接

  toClose = releaseIfNoNewStreams();

  if (this.connection != null) {

    // We had an already-allocated connection and it's good.

    result = this.connection;

    releasedConnection = null;

  }

  if (!reportedAcquired) {

    // If the connection was never reported acquired, don't report it as released!

    // 如果该连接从未被标记为获得,不要标记为发布状态,reportedAcquired 通过 acquire()   方法修改

    releasedConnection = null;

  }



  if (result == null) {

    // Attempt to get a connection from the pool.

    // 尝试供连接池中获取一个连接

    Internal.instance.get(connectionPool, address, this, null);

    if (connection != null) {

      foundPooledConnection = true;

      result = connection;

    } else {

      selectedRoute = route;

    }

  }

}

// 关闭连接

closeQuietly(toClose);



if (releasedConnection != null) {

  eventListener.connectionReleased(call, releasedConnection);

}

if (foundPooledConnection) {

  eventListener.connectionAcquired(call, result);

}

if (result != null) {

  // If we found an already-allocated or pooled connection, we're done.

  // 如果已经从连接池中获取到了一个连接,就将其返回

  return result;

}



// If we need a route selection, make one. This   is a blocking operation.

boolean newRouteSelection = false;

if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {

  newRouteSelection = true;

  routeSelection = routeSelector.next();

}



synchronized (connectionPool) {

  if (canceled) throw new IOException("Canceled");



  if (newRouteSelection) {

    // Now that we have a set of IP addresses,   make another attempt at getting a   connection from

    // the pool. This could match due to   connection coalescing.

     // 根据一系列的 IP地址从连接池中获取一个链接

    List<Route> routes = routeSelection.getAll();

    for (int i = 0, size = routes.size(); i < size;i++) {

      Route route = routes.get(i);

      // 从连接池中获取一个连接

      Internal.instance.get(connectionPool, address, this, route);

      if (connection != null) {

        foundPooledConnection = true;

        result = connection;

        this.route = route;

        break;

      }

    }

  }



  if (!foundPooledConnection) {

    if (selectedRoute == null) {

      selectedRoute = routeSelection.next();

    }



    // Create a connection and assign it to this allocation immediately. This makes it   possible

    // for an asynchronous cancel() to interrupt the handshake we're about to do.

    // 在连接池中如果没有该连接,则创建一个新的连接,并将其分配,这样我们就可以在握手之前进行终端

    route = selectedRoute;

    refusedStreamCount = 0;

    result = new RealConnection(connectionPool, selectedRoute);

    acquire(result, false);

  }

}

// If we found a pooled connection on the 2nd time around, we're done.

if (foundPooledConnection) {

// 如果我们在第二次的时候发现了一个池连接,那么我们就将其返回

  eventListener.connectionAcquired(call, result);

  return result;

}



// Do TCP + TLS handshakes. This is a blocking     operation.

 // 进行 TCP 和 TLS 握手

result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,

  connectionRetryEnabled, call, eventListener);

routeDatabase().connected(result.route());



Socket socket = null;

synchronized (connectionPool) {

  reportedAcquired = true;



  // Pool the connection.

  // 将该连接放进连接池中

  Internal.instance.put(connectionPool, result);



  // If another multiplexed connection to the same   address was created concurrently, then

  // release this connection and acquire that one.

  // 如果同时创建了另一个到同一地址的多路复用连接,释放这个连接并获取那个连接

  if (result.isMultiplexed()) {

    socket = Internal.instance.deduplicate(connectionPool, address, this);

    result = connection;

  }

}

closeQuietly(socket);



eventListener.connectionAcquired(call, result);

return result;

}

复制代码




从以上的源码分析可知:



*   判断当前的连接是否可以使用:流是否已经被关闭,并且已经被限制创建新的流;

*   如果当前的连接无法使用,就从连接池中获取一个连接;

*   连接池中也没有发现可用的连接,创建一个新的连接,并进行握手,然后将其放到连接池中。



在从连接池中获取一个连接的时候,使用了 Internal 的 get() 方法。Internal 有一个静态的实例,会在 OkHttpClient 的静态代码快中被初始化。我们会在 Internal 的 get() 中调用连接池的 get() 方法来得到一个连接。并且,从中我们明白了连接复用的一个好处就是省去了进行 TCP 和 TLS 握手的一个过程。因为建立连接本身也是需要消耗一些时间的,连接被复用之后可以提升我们网络访问的效率。




### 尾声

最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

**进阶学习视频**

![](https://img-blog.csdnimg.cn/img_convert/35900ddeeb2fef337ae441df889db80d.webp?x-oss-process=image/format,png)

**附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题** (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

![](https://img-blog.csdnimg.cn/img_convert/8bfd0038036df09a45f4ae03dd54952b.webp?x-oss-process=image/format,png)



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

过程。因为建立连接本身也是需要消耗一些时间的,连接被复用之后可以提升我们网络访问的效率。




### 尾声

最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

**进阶学习视频**

[外链图片转存中...(img-s1IjsuWA-1715618077875)]

**附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题** (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中...(img-tNYqZpRH-1715618077875)]



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值