2024年安卓最新Android开发神器:OkHttp框架源码解析,准备面试了很紧张怎么办

作者2013年从java开发,转做Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

参与过不少面试,也当面试官 面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我整理了一份阿里P7级别的最系统的Android开发主流技术,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括阿里,以及字节跳动,腾讯,华为,小米,等一线互联网公司主流架构技术。如果你想深入系统学习Android开发,成为一名合格的高级工程师,可以收藏一下这些Android进阶技术选型

我搜集整理过这几年阿里,以及腾讯,字节跳动,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

Java语言与原理;
大厂,小厂。Android面试先看你熟不熟悉Java语言

高级UI与自定义view;
自定义view,Android开发的基本功。

性能调优;
数据结构算法,设计模式。都是这里面的关键基础和重点需要熟练的。

NDK开发;
未来的方向,高薪必会。

前沿技术;
组件化,热升级,热修复,框架设计

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

我在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多,CodeChina上可见;

当然,想要深入学习并掌握这些能力,并不简单。关于如何学习,做程序员这一行什么工作强度大家都懂,但是不管工作多忙,每周也要雷打不动的抽出 2 小时用来学习。

不出半年,你就能看出变化!

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

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

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

bufferedRequestBody.close();

realChain.eventListener()

.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);

} else if (!connection.isMultiplexed()) { // If the “Expect: 100-continue” expectation wasn’t met, prevent the HTTP/1 connection

// from being reused. Otherwise we’re still obligated to transmit the request body to

// leave the connection in a consistent state.

streamAllocation.noNewStreams();

}

}

}

}

CacheInterceptor;

public final class CacheInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException {

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

Request networkRequest = strategy.networkRequest;

Response cacheResponse = strategy.cacheResponse; if (cache != null) { /**

  • Track an HTTP response being satisfied with {@code cacheStrategy}.

  • 主要是跟踪networkRequest次数以及对应Cache的hitcount

*/

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

} //在chain.proceed()调用下一个拦截器

Response networkResponse = null; try {

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

}

} //处理response并返回

… return response;

}

}

OkHttpClient

OkHttpClient托管着所有HTTP调用, 每个Client均拥有自己的连接池和线程池

实现抽象类Internal的方法,这是Internel抽象类唯一的实现,方法与CacheInterceptor控制Http的Header.Lenient区域和StreamAlloction从连接池中获取连接有关

private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {

… synchronized (connectionPool) {

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

}

}

} return result;

RouteDatabase && RouteSeletor

RouteDatabase是记录连接失败的连接路径的黑名单,从而OkHttp可以从失败中学习并且倾向于选择其他可用的路径,RouteSeletor通过RouteDatabase.shouldPostpone(route)方法可获知此路径是否近期曾连接失败,RouteSelector部分源码如下:

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

总结

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的Android开发中高级必知必会核心笔记,共计2968页PDF、58w字,囊括Android开发648个知识点,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。

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

2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。

虽然面试失败了,但我也不会放弃入职字节跳动的决心的!建议大家面试之前都要有充分的准备,顺顺利利的拿到自己心仪的offer。

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

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

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

期多花了不少精力),包知识脉络 + 诸多细节。

[外链图片转存中…(img-4SSgvkK5-1715809011812)]

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

2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。

虽然面试失败了,但我也不会放弃入职字节跳动的决心的!建议大家面试之前都要有充分的准备,顺顺利利的拿到自己心仪的offer。

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值