OkHttp3-只会用是不够的,还得懂这些,高级安卓面试题

简介: OkHttp3-只会用是不够的,还得懂这些,高级安卓面试题

这就是一个标准的构建者模式,将http请求的一些配置封装到client对象中。

2. 构建Request请求对象
public final class Request {
final HttpUrl url;
final String method;
final Headers headers;
final @Nullable RequestBody body;
final Map<Class<?>, Object> tags;
public static class Builder {
@Nullable HttpUrl url;
String method;
Headers.Builder headers;
@Nullable RequestBody body;
public Builder() {
this.method = “GET”;
this.headers = new Headers.Builder();
}
Builder(Request request) {
this.url = request.url;
this.method = request.method;
this.body = request.body;
this.tags = request.tags.isEmpty()
? Collections.emptyMap()
: new LinkedHashMap<>(request.tags);
this.headers = request.headers.newBuilder();
}
}
}

这也是一个建造者模式把请求的urlmethodheader全部封装到Request对象中。

3. 创建Call对象

先来看看Call,这是一个接口,定义了一些request()enqueue(Callback responseCallback)execute()等方法,其实现类是RealCall,先搁置一边。回过头来看client.newCall(request)跟进代码,查看newCall(Request request)方法。

OkHttpClient类:

@Override   public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
RealCall类:
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;
}

果然,这里就只是跟进传进来的Request和当前的client对象创建了一个RealCall对象,也就是说使用方法中的第三步(Call call = client.newCall(request))执行完成后,得到了一个ReallCall对象,接下来到了第四步。

4. 发起请求

先看execute = call.execute()请求

a).同步请求execute()
@Override
public Response execute() throws IOException {
synchronized (this) {
//一个请求只能执行一次
if (executed) throw new IllegalStateException(“Already Executed”);
executed = true;
}
captureCallStackTrace();
timeout.enter();
//http请求调用的生命周期
eventListener.callStart(this);
try {
client.dispatcher().executed(this);//注释1
Response result = getResponseWithInterceptorChain();//注释2
if (result == null) throw new IOException(“Canceled”);
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
关键代码为标出来的注释1和注释2两处,先看注释1,看下client.dispatcher()返回的Dispatcher对象:
/**
* Policy on when async requests are executed.
*
* 
Each dispatcher uses an {@link ExecutorService} to run calls internally. If you supply your
* own executor, it should be able to run {@linkplain #getMaxRequests the configured maximum} number
* of calls concurrently.
/public final class Dispatcher {
//最大请求数
private int maxRequests = 64;
//每一个主机的最大请求数
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/* Executes calls. Created lazily. /
//线程池
private @Nullable ExecutorService executorService;
/* Ready async calls in the order they’ll be run. /
//准备执行的异步请求队列
private final Deque readyAsyncCalls = new ArrayDeque<>();
/* Running asynchronous calls. Includes canceled calls that haven’t finished yet. /
//正在执行的异步请求队列
private final Deque runningAsyncCalls = new ArrayDeque<>();
/* Running synchronous calls. Includes canceled calls that haven’t finished yet. /
//正在执行的同步请求队列
private final Deque runningSyncCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
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();
}
/* Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
//…代码略…
}
这是一个调度器,内部维护着最大请求数,每个主机最大请求数等参数,最重要的是维护着三个队列,分别是“将要执行的异步请求队列”、“正在执行的异步请求队列”和“正在执行的同步执行队列”。之前的代码段中注释1处调用dispatcher.executed(this)方法,我们看到这个方法只是把当前的realCall实例加入到了请求队列中。接下来看注释2处的代码Response result = getResponseWithInterceptorChain(),看下这个方法:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List interceptors = new ArrayList<>();
//用户自定义的拦截器
interceptors.addAll(client.interceptors());
//错误重连、重定向拦截器
interceptors.add(retryAndFollowUpInterceptor);
//请求配置拦截器
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, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
在这个方法中,将用户自定义的一些拦截器和默认的拦截器封装到一个list中,然后创建RealInterceptorChain对象并执行proceed(originalRequest)方法,接下来将是重点。看一下这个方法
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
//…省去异常处理…
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request, call,eventListener, connectTimeout, readTimeout,writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
//…省去异常处理…
return response;
}
关键代码只有三行,这里会遍历调用拦截器列表中的拦截器,并调用每一个拦截器的intercept(RealInterceptorChain chain)方法,先看这里的第一个拦截器RetryAndFollowUpInterceptor的intercept(Chain chain)方法
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
//…省略部分代码…
int followUpCount = 0;
Response priorResponse = null;
while (true) {
//…省略异常处理代码…
Response response;
boolean releaseConnection = true;
try {
//注释3
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
throw e.getFirstConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
} finally {
// We’re throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release(true);
}
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
相关文章
|
21小时前
|
JSON 安全 调度
Android面试题之Kotlin协程一文搞定
本文介绍了协程的基础知识,强调它是轻量级线程,用于处理耗时任务而不阻塞主线程,确保主线程安全。协程特点包括使异步逻辑同步化,并允许函数挂起和恢复。挂起函数由`suspend`关键字标识,只能在协程内部调用。挂起与阻塞的主要区别在于挂起不会导致主线程ANR。 结构化并发和协程作用域(如`CoroutineScope`、`GlobalScope`、`MainScope`等)提供了任务管理,文章还探讨了并发、启动模式、协程取消、超时任务以及资源释放等主题。
10 0
|
22小时前
|
存储 Java 调度
Android面试题之Kotlin协程到底是什么?它是线程吗?
本文探讨了协程与线程的区别,指出协程并非线程,而是轻量级的线程替代。协程轻量体现在它们共享调用栈,内存占用少,仅需几个KB。协程切换发生在用户态,避免了昂贵的内核态切换。在Kotlin中,协程通过Continuation对象实现上下文保存,允许高效并发执行,而不会像线程那样消耗大量资源。通过`runBlocking`和`launch`示例展示了协程的非阻塞挂起特性。总结来说,协程的轻量主要源于内存占用少、切换开销低和高并发能力。
7 0
|
1天前
|
安全 Android开发 Kotlin
Android面试题之Kotlin的几种常见的类
这篇文章探讨了Kotlin编程语言中的初始化顺序、延迟初始化、惰性初始化、`lateinit`与`by lazy`的区别、初始化注意事项、继承、嵌套类、数据类、单例类和枚举类的使用,以及密封类的概念。文中通过示例代码详细解释了各种特性,并提醒读者关注初始化顺序和线程安全问题。同时,鼓励读者关注作者的公众号“AntDream”获取更多相关文章。
10 1
|
1天前
|
存储 Java 调度
Android面试题之Kotlin 协程的挂起、执行和恢复过程
了解Kotlin协程的挂起、执行和恢复机制。挂起时,状态和上下文(局部变量、调用栈、调度器等)被保存;挂起点通过`Continuation`对象处理,释放线程控制权。当恢复条件满足,调度器重新分配线程,调用`resumeWith`恢复执行。关注公众号“AntDream”获取更多并发知识。
6 2
|
1天前
|
Java Linux Android开发
Android面试题之说说系统的启动流程(总结)
这篇文章概述了Android系统的启动流程,从Boot Rom到Zygote进程和SystemServer的启动。init进程作为用户级别的第一个进程,负责创建文件目录、初始化服务并启动Zygote。Zygote通过预加载资源和创建Socket服务,使用fork函数生成SystemServer进程。fork过程中,子进程继承父进程大部分信息但具有独立的进程ID。Zygote预加载资源以减少后续进程的启动时间,而SystemServer启动众多服务并最终开启Launcher应用。文中还讨论了为何从Zygote而非init或SystemServer fork新进程的原因。
7 2
|
27天前
|
消息中间件 安全 关系型数据库
100天!教你逐步突破,快速掌握成为 Python 高手!,安卓面试题及答案
100天!教你逐步突破,快速掌握成为 Python 高手!,安卓面试题及答案
|
27天前
|
XML 前端开发 Android开发
Android架构设计——MVC,滴滴 战略 面试
Android架构设计——MVC,滴滴 战略 面试
|
27天前
|
Android开发 异构计算 前端开发
Android显示原理,安卓自定义view面试
Android显示原理,安卓自定义view面试
|
27天前
|
算法 前端开发 Android开发
Android文字基线Baseline算法的使用讲解,Android开发面试题
Android文字基线Baseline算法的使用讲解,Android开发面试题
Android文字基线Baseline算法的使用讲解,Android开发面试题
|
27天前
|
存储 Android开发
Android插件化-Broadcast篇,2024年最新安卓面试自我介绍
Android插件化-Broadcast篇,2024年最新安卓面试自我介绍