2024年安卓最新Android高工面试被问OKHTTP内核解析,我慌了!,字节跳动面试自我介绍

重要知识点

下面是有几位Android行业大佬对应上方技术点整理的一些进阶资料。

高级进阶篇——高级UI,自定义View(部分展示)

UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!

  • 面试题部分合集

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

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

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

  1. }

  2. OkHttpClient(Builder builder) {

  3. this.dispatcher = builder.dispatcher;

  4. this.proxy = builder.proxy;

  5. this.protocols = builder.protocols;

  6. this.connectionSpecs = builder.connectionSpecs;

  7. this.interceptors = Util.immutableList(builder.interceptors);

  8. this.networkInterceptors = Util.immutableList(builder.networkInterceptors);

  9. this.eventListenerFactory = builder.eventListenerFactory;

  10. this.proxySelector = builder.proxySelector;

  11. this.cookieJar = builder.cookieJar;

  12. this.cache = builder.cache;

  13. this.internalCache = builder.internalCache;

  14. this.socketFactory = builder.socketFactory;

  15. boolean isTLS = false;

  16. ......

  17. this.hostnameVerifier = builder.hostnameVerifier;

  18. this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(

  19. certificateChainCleaner);

  20. this.proxyAuthenticator = builder.proxyAuthenticator;

  21. this.authenticator = builder.authenticator;

  22. this.connectionPool = builder.connectionPool;

  23. this.dns = builder.dns;

  24. this.followSslRedirects = builder.followSslRedirects;

  25. this.followRedirects = builder.followRedirects;

  26. this.retryOnConnectionFailure = builder.retryOnConnectionFailure;

  27. this.connectTimeout = builder.connectTimeout;

  28. this.readTimeout = builder.readTimeout;

  29. this.writeTimeout = builder.writeTimeout;

  30. this.pingInterval = builder.pingInterval;

  31. }

  32. }

第二步:接下来发起 HTTP 请求

  1. Request request = new Request.Builder().url("url").build();

  2. okHttpClient.newCall(request).enqueue(new Callback() {

  3. @Override

  4. public void onFailure(Call call, IOException e) {

  5. }

  6. @Override

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

  8. }

  9. });

第二步:代码流程分析:

  1. Request request = new Request.Builder().url("url").build();

初始化构建者模式和请求对象,并且用URL替换Web套接字URL。

  1. public final class Request {

  2. public Builder() {

  3. this.method = "GET";

  4. this.headers = new Headers.Builder();

  5. }

  6. public Builder url(String url) {

  7. ......

  8. // Silently replace web socket URLs with HTTP URLs.

  9. if (url.regionMatches(true, 0, "ws:", 0, 3)) {

  10. url = "http:" + url.substring(3);

  11. } else if (url.regionMatches(true, 0, "wss:", 0, 4)) {

  12. url = "https:" + url.substring(4);

  13. }

  14. HttpUrl parsed = HttpUrl.parse(url);

  15. ......

  16. return url(parsed);

  17. }

  18. public Request build() {

  19. ......

  20. return new Request(this);

  21. }

  22. }

第三步:方法解析:

  1. okHttpClient.newCall(request).enqueue(new Callback() {

  2. @Override

  3. public void onFailure(Call call, IOException e) {

  4. }

  5. @Override

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

  7. }

  8. });

源码分析:

  1. public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {

  2. @Override

  3. public Call newCall(Request request) {

  4. return new RealCall(this, request, false /* for web socket */);

  5. }

  6. }

RealCall实现了Call.Factory接口创建了一个RealCall的实例,而RealCall是Call接口的实现。

异步请求的执行流程

  1. final class RealCall implements Call {

  2. @Override

  3. public void enqueue(Callback responseCallback) {

  4. synchronized (this) {

  5. if (executed) throw new IllegalStateException("Already Executed");

  6. executed = true;

  7. }

  8. captureCallStackTrace();

  9. client.dispatcher().enqueue(new AsyncCall(responseCallback));

  10. }

  11. }

由以上源码得知:

1) 检查这个 call 是否已经被执行了,每个 call 只能被执行一次,如果想要一个完全一样的 call,可以利用 call#clone 方法进行克隆。

2)利用 client.dispatcher().enqueue(this) 来进行实际执行,dispatcher 是刚才看到的 OkHttpClient.Builder 的成员之一

3)AsyncCall是RealCall的一个内部类并且继承NamedRunnable,那么首先看NamedRunnable类是什么样的,如下:

  1. public abstract class NamedRunnable implements Runnable {

  2. ......

  3. @Override

  4. public final void run() {

  5. ......

  6. try {

  7. execute();

  8. }

  9. ......

  10. }

  11. protected abstract void execute();

  12. }

可以看到NamedRunnable实现了Runnbale接口并且是个抽象类,其抽象方法是execute(),该方法是在run方法中被调用的,这也就意味着NamedRunnable是一个任务,并且其子类应该实现execute方法。下面再看AsyncCall的实现:

  1. final class AsyncCall extends NamedRunnable {

  2. private final Callback responseCallback;

  3. AsyncCall(Callback responseCallback) {

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

  5. this.responseCallback = responseCallback;

  6. }

  7. ......

  8. final class RealCall implements Call {

  9. @Override protected void execute() {

  10. boolean signalledCallback = false;

  11. try {

  12. Response response = getResponseWithInterceptorChain();

  13. if (retryAndFollowUpInterceptor.isCanceled()) {

  14. signalledCallback = true;

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

  16. } else {

  17. signalledCallback = true;

  18. responseCallback.onResponse(RealCall.this, response);

  19. }

  20. } catch (IOException e) {

  21. ......

  22. responseCallback.onFailure(RealCall.this, e);

  23. } finally {

  24. client.dispatcher().finished(this);

  25. }

  26. }

AsyncCall实现了execute方法,首先是调用getResponseWithInterceptorChain()方法获取响应,然后获取成功后,就调用回调的onReponse方法,如果失败,就调用回调的onFailure方法。最后,调用Dispatcher的finished方法。

关键代码:

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

responseCallback.onResponse(RealCall.this, response);

走完这两句代码会进行回调到刚刚我们初始化Okhttp的地方,如下:

  1. okHttpClient.newCall(request).enqueue(new Callback() {

  2. @Override

  3. public void onFailure(Call call, IOException e) {

  4. }

  5. @Override

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

  7. }

  8. });

核心重点类Dispatcher线程池介绍

  1. public final class Dispatcher {

  2. /** 最大并发请求数为64 */

  3. private int maxRequests = 64;

  4. /** 每个主机最大请求数为5 */

  5. private int maxRequestsPerHost = 5;

  6. /** 线程池 */

  7. private ExecutorService executorService;

  8. /** 准备执行的请求 */

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

  10. /** 正在执行的异步请求,包含已经取消但未执行完的请求 */

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

  12. /** 正在执行的同步请求,包含已经取消单未执行完的请求 */

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

在OkHttp,使用如下构造了单例线程池

  1. public synchronized ExecutorService executorService() {

  2. if (executorService == null) {

  3. executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,

  4. new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));

  5. }

  6. return executorService;

  7. }

构造一个线程池ExecutorService:

  1. executorService = new ThreadPoolExecutor(

  2. //corePoolSize 最小并发线程数,如果是0的话,空闲一段时间后所有线程将全部被销毁

  3. 0,

  4. //maximumPoolSize: 最大线程数,当任务进来时可以扩充的线程最大值,当大于了这个值就会根据丢弃处理机制来处理

  5. Integer.MAX_VALUE,

  6. //keepAliveTime: 当线程数大于corePoolSize时,多余的空闲线程的最大存活时间

  7. 60,

  8. //单位秒

  9. TimeUnit.SECONDS,

  10. //工作队列,先进先出

  11. new SynchronousQueue<Runnable>(),

  12. //单个线程的工厂

  13. Util.threadFactory("OkHttp Dispatcher", false));

可以看出,在Okhttp中,构建了一个核心为[0, Integer.MAX_VALUE]的线程池,它不保留任何最小线程数,随时创建更多的线程数,当线程空闲时只能活60秒,它使用了一个不存储元素的阻塞工作队列,一个叫做”OkHttp Dispatcher”的线程工厂。

也就是说,在实际运行中,当收到10个并发请求时,线程池会创建十个线程,当工作完成后,线程池会在60s后相继关闭所有线程。

  1. synchronized void enqueue(AsyncCall call) {

  2. if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {

  3. runningAsyncCalls.add(call);

  4. executorService().execute(call);

  5. } else {

  6. readyAsyncCalls.add(call);

  7. }

  8. }

从上述源码分析,如果当前还能执行一个并发请求,则加入 runningAsyncCalls ,立即执行,否则加入 readyAsyncCalls 队列。

Dispatcher线程池总结

1)调度线程池Disptcher实现了高并发,低阻塞的实现 2)采用Deque作为缓存,先进先出的顺序执行 3)任务在try/finally中调用了finished函数,控制任务队列的执行顺序,而不是采用锁,减少了编码复杂性提高性能

这里是分析OkHttp源码,并不详细讲线程池原理,如对线程池不了解请参考如下链接

点我,线程池原理,在文章性能优化最后有视频对线程池原理讲解

  1. try {

  2. Response response = getResponseWithInterceptorChain();

  3. if (retryAndFollowUpInterceptor.isCanceled()) {

  4. signalledCallback = true;

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

  6. } else {

  7. signalledCallback = true;

  8. responseCallback.onResponse(RealCall.this, response);

  9. }

  10. } finally {

  11. client.dispatcher().finished(this);

  12. }

当任务执行完成后,无论是否有异常,finally代码段总会被执行,也就是会调用Dispatcher的finished函数

  1. void finished(AsyncCall call) {

  2. finished(runningAsyncCalls, call, true);

  3. }

从上面的代码可以看出,第一个参数传入的是正在运行的异步队列,第三个参数为true,下面再看有是三个参数的finished方法:

  1. private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {

  2. int runningCallsCount;

  3. Runnable idleCallback;

  4. synchronized (this) {

  5. if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");

  6. if (promoteCalls) promoteCalls();

  7. runningCallsCount = runningCallsCount();

  8. idleCallback = this.idleCallback;

  9. }

  10. if (runningCallsCount == 0 && idleCallback != null) {

  11. idleCallback.run();

  12. }

  13. }

打开源码,发现它将正在运行的任务Call从队列runningAsyncCalls中移除后,获取运行数量判断是否进入了Idle状态,接着执行promoteCalls()函数,下面是promoteCalls()方法:

  1. private void promoteCalls() {

  2. if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.

  3. if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

  4. for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {

  5. AsyncCall call = i.next();

  6. if (runningCallsForHost(call) < maxRequestsPerHost) {

  7. i.remove();

  8. runningAsyncCalls.add(call);

  9. executorService().execute(call);

  10. }

  11. if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.

  12. }

  13. }

主要就是遍历等待队列,并且需要满足同一主机的请求小于maxRequestsPerHost时,就移到运行队列中并交给线程池运行。就主动的把缓存队列向前走了一步,而没有使用互斥锁等复杂编码

核心重点getResponseWithInterceptorChain方法

  1. Response getResponseWithInterceptorChain() throws IOException {

  2. // Build a full stack of interceptors.

  3. List<Interceptor> interceptors = new ArrayList<>();

  4. interceptors.addAll(client.interceptors());

  5. interceptors.add(retryAndFollowUpInterceptor);

  6. interceptors.add(new BridgeInterceptor(client.cookieJar()));

  7. interceptors.add(new CacheInterceptor(client.internalCache()));

  8. interceptors.add(new ConnectInterceptor(client));

  9. if (!forWebSocket) {

  10. interceptors.addAll(client.networkInterceptors());

  11. }

  12. interceptors.add(new CallServerInterceptor(forWebSocket));

  13. Interceptor.Chain chain = new RealInterceptorChain(

  14. interceptors, null, null, null, 0, originalRequest);

  15. return chain.proceed(originalRequest);

  16. }

1)在配置 OkHttpClient 时设置的 interceptors; 2)负责失败重试以及重定向的 RetryAndFollowUpInterceptor; 3)负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor; 4)负责读取缓存直接返回、更新缓存的 CacheInterceptor; 5)负责和服务器建立连接的 ConnectInterceptor; 6)配置 OkHttpClient 时设置的 networkInterceptors; 7)负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor。

OkHttp的这种拦截器链采用的是责任链模式,这样的好处是将请求的发送和处理分开,并且可以动态添加中间的处理方实现对请求的处理、短路等操作。

从上述源码得知,不管okhttp有多少拦截器最后都会走,如下方法:

  1. Interceptor.Chain chain = new RealInterceptorChain(

  2. interceptors, null, null, null, 0, originalRequest);

  3. return chain.proceed(originalRequest);

从方法名字基本可以猜到是干嘛的,调用 chain.proceed(originalRequest); 将request传递进来,从拦截器链里拿到返回结果。那么拦截器Interceptor是干嘛的,Chain是干嘛的呢?继续往下看RealInterceptorChain

RealInterceptorChain类

下面是RealInterceptorChain的定义,该类实现了Chain接口,在getResponseWithInterceptorChain调用时好几个参数都传的null。

  1. public final class RealInterceptorChain implements Interceptor.Chain {

  2. public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,

  3. HttpCodec httpCodec, RealConnection connection, int index, Request request) {

  4. this.interceptors = interceptors;

  5. this.connection = connection;

  6. this.streamAllocation = streamAllocation;

  7. this.httpCodec = httpCodec;

  8. this.index = index;

  9. this.request = request;

  10. }

  11. ......

  12. @Override

  13. public Response proceed(Request request) throws IOException {

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

  15. }

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

  17. RealConnection connection) throws IOException {

  18. if (index >= interceptors.size()) throw new AssertionError();

  19. calls++;

  20. ......

  21. // Call the next interceptor in the chain.

  22. RealInterceptorChain next = new RealInterceptorChain(

  23. interceptors, streamAllocation, httpCodec, connection, index + 1, request);

  24. Interceptor interceptor = interceptors.get(index);

  25. Response response = interceptor.intercept(next);

  26. ......

  27. return response;

  28. }

  29. protected abstract void execute();

  30. }

主要看proceed方法,proceed方法中判断index(此时为0)是否大于或者等于client.interceptors(List )的大小。由于httpStream为null,所以首先创建next拦截器链,主需要把索引置为index+1即可;然后获取第一个拦截器,调用其intercept方法。

Interceptor 代码如下:

  1. public interface Interceptor {

最后

感觉现在好多人都在说什么安卓快凉了,工作越来越难找了。又是说什么程序员中年危机啥的,为啥我这年近30的老农根本没有这种感觉,反倒觉得那些贩卖焦虑的都是瞎j8扯谈。当然,职业危机意识确实是要有的,但根本没到那种草木皆兵的地步好吗?

Android凉了都是弱者的借口和说辞。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

所以,最后这里放上我耗时两个月,将自己8年Android开发的知识笔记整理成的Android开发者必知必会系统学习资料笔记,上述知识点在笔记中都有详细的解读,里面还包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

以上全套学习笔记面试宝典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。

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

不驰于空想,不骛于虚声。不忘初心,方得始终。

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

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

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

turn response;`

  1. }

  2. protected abstract void execute();

  3. }

主要看proceed方法,proceed方法中判断index(此时为0)是否大于或者等于client.interceptors(List )的大小。由于httpStream为null,所以首先创建next拦截器链,主需要把索引置为index+1即可;然后获取第一个拦截器,调用其intercept方法。

Interceptor 代码如下:

  1. public interface Interceptor {

最后

感觉现在好多人都在说什么安卓快凉了,工作越来越难找了。又是说什么程序员中年危机啥的,为啥我这年近30的老农根本没有这种感觉,反倒觉得那些贩卖焦虑的都是瞎j8扯谈。当然,职业危机意识确实是要有的,但根本没到那种草木皆兵的地步好吗?

Android凉了都是弱者的借口和说辞。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

所以,最后这里放上我耗时两个月,将自己8年Android开发的知识笔记整理成的Android开发者必知必会系统学习资料笔记,上述知识点在笔记中都有详细的解读,里面还包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

[外链图片转存中…(img-504gKRvd-1715814029951)]

以上全套学习笔记面试宝典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。

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

不驰于空想,不骛于虚声。不忘初心,方得始终。

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

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值