Android开发神器:OkHttp框架源码解析,2024年春招Android面试题

public final class RouteSelector { /**

  • Clients should invoke this method when they encounter a connectivity failure on a connection

  • returned by this route selector.

  • 在StreamAllocation.streamFailed()中添加了routeSelector.connectFailed()逻辑

*/

public void connectFailed(Route failedRoute, IOException failure) { if (failedRoute.proxy().type() != Proxy.Type.DIRECT && address.proxySelector() != null) { // Tell the proxy selector when we fail to connect on a fresh connection.

address.proxySelector().connectFailed(

address.url().uri(), failedRoute.proxy().address(), failure);

}

routeDatabase.failed(failedRoute);

}

}

synchronized void enqueue(AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {

runningAsyncCalls.add(call);

executorService().execute(call);

} else {

readyAsyncCalls.add(call);

}

}

… /** Used by {@code Call#execute} to signal it is in-flight. */

synchronized void executed(RealCall call) {

runningSyncCalls.add(call);

}

ExecutorSevice.execute(AsyncCall)执行代码位于AsyncCall内部复写的execute()方法, 方法内定义一些Callback回调节点运行逻辑,包括用户主动取消执行(使用retryAndFollowUpInterceptor)以及执行请求成功或者失败时的回调方法

final class AsyncCall extends NamedRunnable {

… @Override protected void execute() { boolean signalledCallback = false; 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) { 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);

}

}

}

惰性初始模式(Created Lazily)成员

ExecutorService()

CacheControl

WebSocket

WebSocket 异步非堵塞的web socket接口 (通过Enqueue方法来实现)

OkHttpClient 通过实现 WebSocket.Factory.newWebSocket 接口实现工厂构造, 通常是由 OkHttpClient来构造

WebSocket生命周期:

Connecting状态: 每个websocket的初始状态, 此时Message可能位于入队状态但是还没有被Dispatcher处理

Open状态: WebSocket已经被服务器端接受并且Socket位于完全开放状态, 所有Message入队之后会即刻被处理

Closing状态: WebSocket进入优雅的关闭状态,WebSocket继续处理已入队的Message但拒绝新的Message入队

Closed状态: WebSocket已完成收发Message的过程, 进入完全关闭状态

WebSocket受到网络等各种因素影响, 可能会断路而提前进入关闭流程

Canceled状态: 被动WebSocket失败连接为非优雅的过程, 而主动则是优雅短路过程

RealWebSocket

RealWebSocket管理着Request队列内容所占的空间大小以及关闭Socket之后留给优雅关闭的时间,默认为16M和60秒,在RealWebSocket.connect()方法中RealWebSocket对OkHttpClient以及Request封装成Call的形式,然后通过Call.enqueue()方法定义调用成功和失败时的Callback代码

public void connect(OkHttpClient client) {

client = client.newBuilder()

.eventListener(EventListener.NONE)

.protocols(ONLY_HTTP1)

.build(); final Request request = originalRequest.newBuilder()

.header(“Upgrade”, “websocket”)

.header(“Connection”, “Upgrade”)

.header(“Sec-WebSocket-Key”, key)

.header(“Sec-WebSocket-Version”, “13”)

.build();

call = Internal.instance.newWebSocketCall(client, request);

call.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { try {

checkResponse(response);

} catch (ProtocolException e) {

failWebSocket(e, response);

closeQuietly(response); return;

} // Promote the HTTP streams into web socket streams.

StreamAllocation streamAllocation = Internal.instance.streamAllocation(call);

streamAllocation.noNewStreams(); // Prevent connection pooling!

Streams streams = streamAllocation.connection().newWebSocketStreams(streamAllocation); // Process all web socket messages.

try {

listener.onOpen(RealWebSocket.this, response);

String name = "OkHttp WebSocket " + request.url().redact();

initReaderAndWriter(name, streams);

streamAllocation.connection().socket().setSoTimeout(0);

loopReader();

} catch (Exception e) {

failWebSocket(e, null);

}

} @Override public void onFailure(Call call, IOException e) {

failWebSocket(e, null);

}

});

}

当Call请求被服务端响应的时候就将HTTP流导入到Web Socket流中,并且调用WebSocketListener相对应的状态方法, WebSocketListener状态如下:

onOpen()onMessage()onClosing()onClosed()onFailure()

WebSocket -> RealWebSocket

Connection -> RealConnection

Interceptor -> RealInterceptorChain

Call -> RealCall

ResponseBody -> RealResponseBody

Gzip压缩机制

处理Gzip压缩的代码在BridgeInterceptor中,默认情况下为gzip压缩状态,可以从下面的源码片段中获知。如果header中没有Accept-Encoding,默认自动添加 ,且标记变量transparentGzip为true

// If we add an “Accept-Encoding: gzip” header field we’re responsible for also decompressing

// the transfer stream.

boolean transparentGzip = false; if (userRequest.header(“Accept-Encoding”) == null && userRequest.header(“Range”) == null) {

transparentGzip = true;

requestBuilder.header(“Accept-Encoding”, “gzip”);

}

BridgeInterceptor解压缩的过程调用了okio.GzipSource()方法并调用Okio.buffer()缓存解压过程,源码如下

if (transparentGzip

&& “gzip”.equalsIgnoreCase(networkResponse.header(“Content-Encoding”))

&& HttpHeaders.hasBody(networkResponse)) {

GzipSource responseBody = new GzipSource(networkResponse.body().source());

Headers strippedHeaders = networkResponse.headers().newBuilder()

.removeAll(“Content-Encoding”)

.removeAll(“Content-Length”)

.build();

responseBuilder.headers(strippedHeaders); String contentType = networkResponse.header(“Content-Type”);

responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));

}

RealCall构造方法

在RealCall构造方法上面,早期版本的RealCall构造方法中将EventListener.Factory以及EventListenerFactory.Create()分开处理导致RealCall构造方法非线程安全. 现在版本的RealCall的构造函数使用OkHttpClient.eventListenerFactory().create()

早期版本如下:

final class RealCall implements Call {

RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {

… final EventListener.Factory eventListenerFactory = client.eventListenerFactory(); this.client = client; this.originalRequest = originalRequest; this.forWebSocket = forWebSocket; //重试和跟进拦截器

this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket); // TODO(jwilson): this is unsafe publication and not threadsafe.

// 这是不安全的发布,不是线程安全的。

this.eventListener = eventListenerFactory.create(this);

}

}

现在 OkHttp 3.11.0 的RealCall源代码如下

final class RealCall implements Call { private EventListener eventListener;

… private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) { this.client = client; this.originalRequest = originalRequest; this.forWebSocket = forWebSocket; this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);

} static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) { // Safely publish the Call instance to the EventListener.

RealCall call = new RealCall(client, originalRequest, forWebSocket);

call.eventListener = client.eventListenerFactory().create(call); return call;

}

}

ConnetionPool

连接池能够复用http连接从而减少访问相同目标主机情况下的网络延迟,此类实现管理连接开闭的策略并使用与连接池一一对应的后台线程清理过期的连接。ConnectionPool提供对Deque进行操作的方法分别为put、get、connectionBecameIdle和evictAll几个操作。分别对应放入连接、获取连接、移除连接和移除所有连接操作,这里我们举例put和get操作。

public final class ConnectionPool {

… private static final Executor executor = new ThreadPoolExecutor(0 /* corePoolSize */,

Integer.MAX_VALUE /* maximumPoolSize /, 60L / keepAliveTime /, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory(“OkHttp ConnectionPool”, true)); /* The maximum number of idle connections for each address. */

private final int maxIdleConnections; private final long keepAliveDurationNs; private final Runnable cleanupRunnable = new Runnable() { @Override public void run() { while (true) { long waitNanos = cleanup(System.nanoTime()); if (waitNanos == -1) return; if (waitNanos > 0) { long waitMillis = waitNanos / 1000000L;

waitNanos -= (waitMillis * 1000000L); synchronized (ConnectionPool.this) { try {

ConnectionPool.this.wait(waitMillis, (int) waitNanos);

} catch (InterruptedException ignored) {

}

}

}

}

}

};

}

cleanUpRunnable里面是一个while(true),一个循环包括:

调用一次cleanUp方法进行清理并返回一个long

如果是-1则退出,否则调用wait方法等待这个long值的时间

okhttp是根据StreamAllocation引用计数是否为0来实现自动回收连接的。cleanUpRunnable遍历每一个RealConnection,通过引用数目确定哪些是空闲的,哪些是在使用中,同时找到空闲时间最长的RealConnection。如果空闲数目超过最大空闲数或者空闲时间超过最大空闲时间,则清理掉这个RealConnection并返回0,表示需要立刻再次清理

public final class ConnectionPool {

… void put(RealConnection connection) { assert (Thread.holdsLock(this)); if (!cleanupRunning) {

cleanupRunning = true;

executor.execute(cleanupRunnable);

}

connections.add(connection);

}

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

总结

【Android 详细知识点思维脑图(技能树)】

我个人是做Android开发,已经有十来年了,目前在某创业公司任职CTO兼系统架构师。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

最后,赠与大家一句话,共勉!

)】**

[外链图片转存中…(img-z4Llchhq-1711917144196)]

我个人是做Android开发,已经有十来年了,目前在某创业公司任职CTO兼系统架构师。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

[外链图片转存中…(img-JGXiLPNF-1711917144196)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

最后,赠与大家一句话,共勉!

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值