腾讯面试官:说一下Android网络知识和框架?

68 阅读10分钟

} //创建一个新线程执行HttpClient网络请求 Thread getThread = new Thread(new LoginGetThread()); getThread.start();

class LoginGetThread implements Runnable{ public void run(){ login(username,password,0); } }

1.2 HttpURLConnection

(1)简介

HttpURLConnection是一种多用途、轻量极的HTTP客户端,使用它来进行HTTP操作可以适用于大多数的应用程序。虽然HttpURLConnection的API提供的比较简单,但是同时这也使得我们可以更加容易地去使用和扩展它。 Android2.2前有个重大BUG:调用close()函数会影响连接池,导致链接复用失效。 比如说对一个可读的InputStream调用close()方法时,就有可能会导致连接池失效了。那么我们通常的解决办法就是直接禁用掉连接池的功能:

private void disableConnectionReuseIfNecessary() { // 这是一个2.2版本之前的bug if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) { System.setProperty("http.keepAlive", "false"); } }

Android2.2后默认开启了gzip压缩、提高https性能(2.3)与缓冲机制(4.0)

(2)实现

1、创建一个UrlConnManager类,然后里面提供getHttpURLConnection()方法用于配置默认的参数并返回HttpURLConnection:

public static HttpURLConnection getHttpURLConnection(String url){ HttpURLConnection mHttpURLConnection=null; try { URL mUrl=new URL(url); mHttpURLConnection=(HttpURLConnection)mUrl.openConnection(); //设置链接超时时间 mHttpURLConnection.setConnectTimeout(15000); //设置读取超时时间 mHttpURLConnection.setReadTimeout(15000); //设置请求参数 mHttpURLConnection.setRequestMethod("POST"); //添加Header mHttpURLConnection.setRequestProperty("Connection","Keep-Alive"); //接收输入流 mHttpURLConnection.setDoInput(true); //传递参数时需要开启 mHttpURLConnection.setDoOutput(true); } catch (IOException e) { e.printStackTrace(); } return mHttpURLConnection ; }

发送POST请求,所以在UrlConnManager类中再写一个postParams()方法用来组织一下请求参数并将请求参数写入到输出流中

public static void postParams(OutputStream output,ListparamsList) throws IOException{ StringBuilder mStringBuilder=new StringBuilder(); for (NameValuePair pair:paramsList){ if(!TextUtils.isEmpty(mStringBuilder)){ mStringBuilder.append("&"); } mStringBuilder.append(URLEncoder.encode(pair.getName(),"UTF-8")); mStringBuilder.append("="); mStringBuilder.append(URLEncoder.encode(pair.getValue(),"UTF-8")); } BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(output,"UTF-8")); writer.write(mStringBuilder.toString()); writer.flush(); writer.close(); }

接下来我们添加请求参数,调用postParams()方法将请求的参数组织好传给HttpURLConnection的输出流,请求连接并处理返回的结果

private void useHttpUrlConnectionPost(String url) { InputStream mInputStream = null; HttpURLConnection mHttpURLConnection = UrlConnManager.getHttpURLConnection(url); try { List postParams = new ArrayList<>(); //要传递的参数 postParams.add(new BasicNameValuePair("username", "moon")); postParams.add(new BasicNameValuePair("password", "123")); UrlConnManager.postParams(mHttpURLConnection.getOutputStream(), postParams); mHttpURLConnection.connect(); mInputStream = mHttpURLConnection.getInputStream(); int code = mHttpURLConnection.getResponseCode(); String respose = converStreamToString(mInputStream); Log.i("wangshu", "请求状态码:" + code + "\n请求结果:\n" + respose); mInputStream.close(); } catch (IOException e) { e.printStackTrace(); } }

最后开启线程请求网络

private void useHttpUrlConnectionGetThread() { new Thread(new Runnable() { @Override public void run() { useHttpUrlConnectionPost("www.baidu.com"); } }).start(); }

1.3 对比

Android2.2前不建议使用HttpURLConnection,Android4.4后,底层实现被OkHttp替换 Android5.0后HttpClient被官方弃用 所以,在Android 2.2版本以及之前的版本使用HttpClient是较好的选择,而在Android 2.3版本及以后,HttpURLConnection则是最佳的选择,它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。另外在Android 6.0版本中,HttpClient库被移除了,HttpURLConnection则是以后我们唯一的选择。

二.主流网络请求库

2.1 简介

网络请求开源库是一个将 网络请求+异步+数据处理 封装好的类库(网络请求是Android网络请求原生方法HttpClient或HttpURLConnection,异步包括多线程、线程池,数据处理包括序列化和反序列化) 使用网络请求库后,实现网络请求的需求同时不需要考虑:异步请求、线程池、缓存等等;降低开发难度,缩短开发周期,使用方便

2.2 对比(Android-Async-Http、Volley、OkHttp、Retrofit)

在这里插入图片描述

在这里插入图片描述

三.okHttp

3.1 简介

(1)支持http2/SPDY黑科技,共享同一个Socket来处理同一个服务器的所有请求(同一域名的所有请求stream共享同一个tcp连接),解决了HOL Blocking SPDY(读作“SPeeDY”)是Google开发的基于TCP的应用层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。SPDY是对HTTP协议的加强。新协议的功能包括数据流的多路复用、支持服务器推送技术、请求优先级、HTTP报头压缩以及强制使用SSL传输协议。 (2)socket自动选择最好路线,并支持自动重连 (3)拥有自动维护的socket连接池,减少握手次数,减少请求延时 (4)拥有队列线程池,轻松写并发 (5)拥有Interceptors(拦截器)轻松处理请求与响应(透明GZIP压缩,转换从而减少数据流量) (6)基于Headers的缓存策略减少重复的网络请求

3.2 使用步骤

(1)添加okHttp和okIo

(Okio是一款轻量级IO框架,是著名网络框架OkHttp的基石。Okio结合了java.io和java.nio,提供阻塞IO和非阻塞IO的功能,同时也对缓存等底层结构做了优化,能让你更轻快的获得、存储和处理数据。)

(2)创建OkHttpClient对象

(3)get/post请求数据

(4)get/post调用

OkHttpClient client = new OkHttpClient.Builder().build(); Request request = new Request.Builder(). url("github.com/cozing"). build(); Call call = client.newCall(request); try { //1.同步请求调用的方法是call.execute(),内部采用的是线程阻塞(一直等待直到线程返回结果)方式直接将结果返回到Response Response response = call.execute(); //2.异步请求调用的方法是call.enqueue(Callback callback),该方法需要传入一个Callback等待结果回调的接口 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.w("cozing", "请求失败"); }

@Override public void onResponse(Call call, Response response) throws IOException { Log.w("cozing", "请求成功"); } }); } catch (IOException e) { e.printStackTrace(); }

3.3 源码解读

(一)整体框架

在这里插入图片描述

(二)工作原理

2.1 OkHttpClient 构造

定义一个OkHttpClient,OkHttpClient构造函数及其配置如下。

mOkHttpClient = new OkHttpClient.Builder() .addInterceptor(loggingInterceptor) .retryOnConnectionFailure(true) .connectTimeout(TIME_OUT, TimeUnit.SECONDS) .readTimeout(TIME_OUT, TimeUnit.SECONDS) .build();

了解OkHttpClient属性

final Dispatcher dispatcher;//调度器 final @Nullable Proxy proxy;//代理 final List protocols;//协议 final List connectionSpecs;//传输层版本和连接协议 final List interceptors;//拦截器 final List networkInterceptors;//网络拦截器 final EventListener.Factory eventListenerFactory; final ProxySelector proxySelector;//代理选择器 final CookieJar cookieJar;//cookie final @Nullable Cache cache;//cache 缓存 final @Nullable InternalCache internalCache;//内部缓存 final SocketFactory socketFactory;//socket 工厂 final @Nullable SSLSocketFactory sslSocketFactory;//安全套层socket工厂 用于https final @Nullable CertificateChainCleaner certificateChainCleaner;//验证确认响应书,适用HTTPS 请求连接的主机名 final HostnameVerifier hostnameVerifier;//主机名字确认 final CertificatePinner certificatePinner;//证书链 final Authenticator proxyAuthenticator;//代理身份验证 final Authenticator authenticator;//本地省份验证 final ConnectionPool connectionPool;//链接池 复用连接 final Dns dns; //域名 final boolean followSslRedirects;//安全套接层重定向 final boolean followRedirects;//本地重定向 final boolean retryOnConnectionFailure;//重试连接失败 final int connectTimeout;//连接超时 final int readTimeout;//读取超时 final int writeTimeout;//写入超时

这里OkHttpClient的构造采用了建造者模式。

2.2 OkHttpClient 请求网络

String run(String url) throws IOException { Request request = new Request.Builder() .url(url) .build();

Response response = client.newCall(request).execute(); return response.body().string(); }

定义一个请求Request,Request中包含客户请求的参数:url、method、headers、requestBody和tag,也采用了建造者模式。 实际请求网络的是Call接口,OkHttp实现了Call.Factory接口,其真正的实现类是RealCall,OkHttp将真正的请求交给了RealCall,RealCall实现了Call中方法完成请求。这里采用了简单工厂模式。

interface Factory { Call newCall(Request request); } ... @Override public Call newCall(Request request) { return RealCall.newRealCall(this, request, false /* for web socket */); }

RealCall中主要方法有

  • 同步请求:client.newCall(request).execute
  • 异步请求:client.newCall(request).enqueue(常用)

异步请求 RealCall.enqueue()

RealCall.java

@Override public void enqueue(Callback responseCallback) { //TODO 不能重复执行 synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); eventListener.callStart(this); //TODO 交给 dispatcher调度器 进行调度 client.dispatcher().enqueue(new AsyncCall(responseCallback)); }

  1. synchronized (this) 确保每个call只能被执行一次不能重复执行
  2. Dispatcher 调度器 将 Call 加入队列,并通过线程池执行 Call 利用dispatcher调度器,来进行实际的执行client.dispatcher().enqueue(new AsyncCall(responseCallback));,在上面的OkHttpClient.Builder可以看出 已经初始化了Dispatcher。 Dispatcher的属性和方法

//TODO 同时能进行的最大请求数 private int maxRequests = 64; //TODO 同时请求的相同HOST的最大个数 SCHEME :// HOST [ ":" PORT ] [ PATH [ "?" QUERY ]] //TODO 如 restapi.amap.com restapi.amap.com - host private int maxRequestsPerHost = 5; /**

  • Ready async calls in the order they'll be run.
  • TODO 双端队列,支持首尾两端 双向开口可进可出,方便移除
  • 异步等待队列

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

/**

  • Running asynchronous calls. Includes canceled calls that haven't finished yet.
  • TODO 正在进行的异步队列 */ private final Deque runningAsyncCalls = new ArrayDeque<>();

Dispatcher管理两个异步请求队列,可对多个并发网络请求进行处理。 Dispatcher.enqueue方法用于执行异步请求,实现如下

//TODO 执行异步请求 synchronized void enqueue(AsyncCall call) { //TODO 同时请求不能超过并发数(64,可配置调度器调整) //TODO okhttp会使用共享主机即 地址相同的会共享socket //TODO 同一个host最多允许5条线程通知执行请求 if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { //TODO 加入运行队列 并交给线程池执行 runningAsyncCalls.add(call); //TODO AsyncCall 是一个runnable,放到线程池中去执行,查看其execute实现 executorService().execute(call); } else { //TODO 加入等候队列 readyAsyncCalls.add(call); } }

可见Dispatcher将Call加入队列中(若同时请求数未超过最大值,则加入运行队列,放到线程池中执行;否则加入等待队列),然后通过线程池执行call。 executorService() 本质上是一个线程池执行方法,用于创建一个线程池

public synchronized ExecutorService executorService() { if (executorService == null) { //TODO 线程池的相关概念 需要理解 //TODO 核心线程 最大线程 非核心线程闲置60秒回收 任务队列 executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false)); } return executorService; }

  1. 在 ConnectionPool 线程池中执行请求 AsyncCall 异步请求中Call实际上为AsyncCall,继承自NamedRunnable

final class AsyncCall extends NamedRunnable

NamedRunnable

public abstract class NamedRunnable implements Runnable { protected final String name;

public NamedRunnable(String format, Object... args) { this.name = Util.format(format, args); }

@Override public final void run() { String oldName = Thread.currentThread().getName(); Thread.currentThread().setName(name); try { execute(); } finally { Thread.currentThread().setName(oldName); } }

protected abstract void execute(); }

因此 AsyncCall 其实就是一个 Runnable,线程池实际上就是执行了execute()。 AsyncCall的execute()

final class AsyncCall extends NamedRunnable { @Override protected void execute() { boolean signalledCallback = false; try { //TODO 责任链模式 //TODO 拦截器链 执行请求 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 { //TODO 移除队列 client.dispatcher().finished(this); } } }

从上述代码可以看出真正执行请求的是getResponseWithInterceptorChain(); 然后通过回调将Response返回给用户。

  1. 通过拦截器链 RealInterceptorChain 通过 责任链模式 真正执行网络请求 真正的执行网络请求和返回响应结果:getResponseWithInterceptorChain()

//TODO 核心代码 开始真正的执行网络请求 Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. //TODO 责任链 List interceptors = new ArrayList<>(); //TODO 在配置okhttpClient 时设置的intercept 由用户自己设置 interceptors.addAll(client.interceptors()); //TODO 负责处理失败后的重试与重定向 interceptors.add(retryAndFollowUpInterceptor); //TODO 负责把用户构造的请求转换为发送到服务器的请求 、把服务器返回的响应转换为用户友好的响应 处理 配置请求头等信息 //TODO 从应用程序代码到网络代码的桥梁。首先,它根据用户请求构建网络请求。然后它继续呼叫网络。最后,它根据网络响应构建用户响应。 interceptors.add(new BridgeInterceptor(client.cookieJar())); //TODO 处理 缓存配置 根据条件(存在响应缓存并被设置为不变的或者响应在有效期内)返回缓存响应 //TODO 设置请求头(If-None-Match、If-Modified-Since等) 服务器可能返回304(未修改) //TODO 可配置用户自己设置的缓存拦截器 interceptors.add(new CacheInterceptor(client.internalCache())); //TODO 连接服务器 负责和服务器建立连接 这里才是真正的请求网络 interceptors.add(new ConnectInterceptor(client)); if (!forWebSocket) { //TODO 配置okhttpClient 时设置的networkInterceptors //TODO 返回观察单个网络请求和响应的不可变拦截器列表。 interceptors.addAll(client.networkInterceptors()); } //TODO 执行流操作(写出请求体、获得响应数据) 负责向服务器发送请求数据、从服务器读取响应数据 //TODO 进行http请求报文的封装与请求报文的解析 interceptors.add(new CallServerInterceptor(forWebSocket));

//TODO 创建责任链 Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest, this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis());

//TODO 执行责任链 return chain.proceed(originalRequest); }

一共执行6种拦截器,一种是用户自定义拦截器client.interceptors(),另外五种是OkHttp自带拦截器:

  • RetryAndFollowUpInterceptor,重试那些失败或者redirect的请求。
  • BridgeInterceptor,请求之前对响应头做了一些检查,并添加一些头,然后在请求之后对响应做一些处理(gzip解压or设置cookie)。
  • CacheInterceptor,根据用户是否有设置cache,如果有的话,则从用户的cache中获取当前请求的缓存。
  • ConnectInterceptor,复用连接池中的连接,如果没有就与服务器建立新的socket连接。
  • CallServerInterceptor,负责发送请求和获取响应。

从上述代码中,可以看出都实现了Interceptor接口,这是okhttp最核心的部分,采用责任链的模式来使每个功能分开,每个Interceptor自行完成自己的任务,并且将不属于自己的任务交给下一个,简化了各自的责任和逻辑。

责任链模式是OkHttp中最核心的设计模式。

我们着重分析一下,okhttp的设计实现,如何通过责任链来进行传递返回数据的。上述代码中可以看出interceptors,是传递到了RealInterceptorChain该类实现了Interceptor.Chain,并且执行了chain.proceed(originalRequest)。核心代码就是chain.proceed() 通过该方法进行责任链的执行。

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException { if (index >= interceptors.size()) throw new AssertionError(); calls++; //TODO 获取下一个拦截链,即链中的拦截器集合index+1 RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request, call, eventListener, connectTimeout, readTimeout, writeTimeout); //TODO 执行当前的拦截器-如果在配置okhttpClient,时没有设置intercept默认是先执行:retryAndFollowUpInterceptor 拦截器 Interceptor interceptor = interceptors.get(index); //TODO 执行下一个拦截器 Response response = interceptor.intercept(next); return response; }

从上述代码,我们可以知道,获取下一个(index+1)RealInterceptorChain 责任链,然后 执行当前(index)责任链interceptors.get(index),最后返回下一个责任链的Response。 其实就是按责任链顺序递归执行了拦截器 这样设计的一个好处就是,责任链中每个拦截器都会执行chain.proceed()方法之前的代码,等责任链最后一个拦截器执行完毕后会返回最终的响应数据,而chain.proceed() 方法会得到最终的响应数据,这时就会执行每个拦截器的chain.proceed()方法之后的代码,其实就是对响应数据的一些操作。执行过程如下图: 在这里插入图片描述

CacheInterceptor 缓存拦截器就是很好的证明,我们来通过CacheInterceptor 缓存拦截器来进行分析,大家就会明白了。 CacheInterceptor 的实现如下: 首先我们先分析上部分代码当没有网络的情况下是如何处理获取缓存的。

@Override public Response intercept(Chain chain) throws IOException { //TODO 获取request对应缓存的Response 如果用户没有配置缓存拦截器 cacheCandidate == null Response cacheCandidate = cache != null ? cache.get(chain.request()) : null;

//TODO 执行响应缓存策略 long now = System.currentTimeMillis(); CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get(); //TODO 如果networkRequest == null 则说明不使用网络请求 Request networkRequest = strategy.networkRequest; //TODO 获取缓存中(CacheStrategy)的Response Response cacheResponse = strategy.cacheResponse;

if (cache != null) { cache.trackResponse(strategy); } //TODO 缓存无效 关闭资源 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. //TODO networkRequest == null 不实用网路请求 且没有缓存 cacheResponse == null 返回失败 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(); }

//TODO 不使用网络请求 且存在缓存 直接返回响应 // If we don't need the network, we're done. if (networkRequest == null) { return cacheResponse.newBuilder() .cacheResponse(stripBody(cacheResponse)) .build(); } }

上述的代码,主要做了几件事: 如果用户自己配置了缓存拦截器,cacheCandidate = cache.Response 获取用户自己存储的Response,否则 cacheCandidate = null;同时从CacheStrategy 获取cacheResponse 和 networkRequest 如果cacheCandidate != null 而 cacheResponse == null 说明缓存无效清除cacheCandidate缓存。 如果networkRequest == null 说明没有网络,cacheResponse == null 没有缓存,返回失败的信息,责任链此时也就终止,不会在往下继续执行。 如果networkRequest == null 说明没有网络,cacheResponse != null 有缓存,返回缓存的信息,责任链此时也就终止,不会在往下继续执行。 上部分代码,其实就是没有网络的时候的处理。 那么下部分代码肯定是,有网络的时候处理

//TODO 执行下一个拦截器 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()); } }

//TODO 网络请求 回来 更新缓存 // If we have a cache response too, then we're doing a conditional get. //TODO 如果存在缓存 更新 if (cacheResponse != null) { //TODO 304响应码 自从上次请求后,请求需要响应的内容未发生改变 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()); } } //TODO 缓存Response 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. 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; }

下部分代码主要做了这几件事: 执行下一个拦截器,也就是请求网络 责任链执行完毕后,会返回最终响应数据,如果缓存存在更新缓存,如果缓存不存在加入到缓存中去。 这样就体现出了,责任链这样实现的好处了,当责任链执行完毕,如果拦截器想要拿到最终的数据做其他的逻辑处理等,这样就不用在做其他的调用方法逻辑了,直接在当前的拦截器就可以拿到最终的数据。 这也是okhttp设计的最优雅最核心的功能。

  1. 执行调度器完成方法,移除队列 AsyncCall的execute()中(见第3步)中finally 执行了client.dispatcher().finished(this); 通过调度器移除队列,并且判断是否存在等待队列,如果存在,检查执行队列是否达到最大值,如果没有将等待队列变为执行队列。这样也就确保了等待队列被执行。

private void finished(Deque calls, T call, boolean promoteCalls) { int runningCallsCount; Runnable idleCallback; synchronized (this) { //TODO calls 移除队列 if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!"); //TODO 检查是否为异步请求,检查等候的队列 readyAsyncCalls,如果存在等候队列,则将等候队列加入执行队列 if (promoteCalls) promoteCalls(); //TODO 运行队列的数量 runningCallsCount = runningCallsCount(); idleCallback = this.idleCallback; } //闲置调用 if (runningCallsCount == 0 && idleCallback != null) { idleCallback.run(); } }

private void promoteCalls() { //TODO 检查 运行队列 与 等待队列 if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity. if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

//TODO 将等待队列加入到运行队列中 for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) { AsyncCall call = i.next(); //TODO 相同host的请求没有达到最大,加入运行队列 if (runningCallsForHost(call) < maxRequestsPerHost) { i.remove(); runningAsyncCalls.add(call); executorService().execute(call); }

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

同步请求 RealCall.excute()

如何做好面试突击,规划学习方向?

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

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

image

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

image

点击:《Android架构视频+BAT面试专题PDF+学习笔记》即可免费获取~

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