有人说,当初的客户端面试越来越卷了,须要开发者把握的内容也越来越多,从根底的Java根底、Android根底、Android零碎原理、Android第三方库、混合开发、数据结构、算法,无一不问,要想得到一份好的工作机会,的确是这样的。上面是我给大家总结的Android面试题。
1,Android 面试之必问Java根底
2,Android 面试之必问Android根底
3,Android 面试之必问高级知识点
4,Android 面试之必问性能优化
1,HTTP与缓存实践
1.1 HTTP缓存策略
HTTP的缓存机制也是依赖于申请和响应header里的参数类实现的,最终的响应后果是从缓存还是从服务端拉取是有一套残缺的机制的,HTTP的缓存机制的流程如下所示。
HTTP的缓存能够分为两种:强制缓存和比照缓存
1.2 强制缓存
要服务端参加判断是否持续应用缓存,当客户端第一次申请数据是,服务端返回了缓存的过期工夫(Expires与Cache-Control),没有过期就能够持续应用缓存,否则则不实用,无需再向服务端询问。
强制缓存应用的的两个标识:
- Expires:Expires的值为服务端返回的到期工夫,即下一次申请时,申请工夫小于服务端返回的到期工夫,间接应用缓存数据。到期工夫是服务端生成的,客户端和服务端的工夫可能有误差。
- Cache-Control:Expires有个工夫校验的问题,所以HTTP1.1采纳Cache-Control代替Expires。
其中,Cache-Control的取值有如下一些:
- private::客户端能够缓存。
- public::客户端和代理服务器都可缓存。
- max-age=xxx: 缓存的内容将在 xxx 秒后生效
- no-cache::须要应用比照缓存来验证缓存数据。
- no-store:所有内容都不会缓存;强制缓存,比照缓存都不会触发。
1.3 比照缓存
须要服务端参加判断是否持续应用缓存,当客户端第一次申请数据时,服务端会将缓存标识(Last-Modified/If-Modified-Since与Etag/If-None-Match)与数据一起返回给客户端,客户端将两者都备份到缓存中 ,再次申请数据时,客户端将上次备份的缓存标识发送给服务端,服务端依据缓存标识进行判断,如果返回304,则示意告诉客户端能够持续应用缓存。
比照缓存的两个标识:
- Last-Modified:示意资源上次批改的工夫。当客户端发送第一次申请时,服务端返回资源上次批改的工夫,返回格局例子如下:
Last-Modified: Tue, 12 Jan 2016 09:31:27 GMT
。 - If-Modified-Since:服务端接管到客户端发来的资源批改工夫,与本人以后的资源批改工夫进行比照,如果本人的资源批改工夫大于客户端发来的资源批改工夫,则阐明资源做过批改, 则返回200示意须要从新申请资源,否则返回304示意资源没有被批改,能够持续应用缓存。不同于If-Unmodified-Since,If-Modified-Since只能与GET或HEAD一起应用。与组合应用时If-None-Match,将被疏忽,除非服务器不反对If-None-Match。
2,OKHttp
当初支流的Android网络申请框架有OKHttp和Retrofit,不过,Retrofit顶层应用的也是OKHttp。在正式介绍OKHttp之前,来看一下Http常见的一些状态码:
- 100~199:批示信息,示意申请已接管,持续解决。
- 200~299:申请胜利,示意申请已被胜利接管、了解。
- 300~399:重定向,要实现申请必须进行更进一步的操作。
- 400~499:客户端谬误,申请有语法错误或申请无奈实现。
- 500~599:服务器端谬误,服务器未能实现非法的申请。
2.1 OKHttp申请流程
上面是OKHttp外部发动申请的大抵流程图,如下图。
上面是应用OKHttp进行Get申请的代码。
//1.新建OKHttpClient客户端
OkHttpClient client = new OkHttpClient();
//新建一个Request对象
Request request = new Request.Builder()
.url(url)
.build();
//2.Response为OKHttp中的响应
Response response = client.newCall(request).execute();
能够看到,应用OKHttp进行申请时,只须要创立一个OKHttpClient对象和Request对象,而后再调用execute()办法和enqueue()办法即可。其中,execute()办法是同步办法,enqueue()办法是异步办法。
2.2 OKHttpClient
应用OKHttpClient之前,须要先创立一个OKHttpClient客户端,OKHttpClient的构造方法如下。
OkHttpClient client = new OkHttpClient();
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
....
}
能够看到,OkHttpClient应用了建造者模式,Builder外面的可配置参数如下。
public static final class Builder {
Dispatcher dispatcher;// 散发器
@Nullable Proxy proxy;
List<Protocol> protocols;
List<ConnectionSpec> connectionSpecs;// 传输层版本和连贯协定
final List<Interceptor> interceptors = new ArrayList<>();// 拦截器
final List<Interceptor> networkInterceptors = new ArrayList<>();
EventListener.Factory eventListenerFactory;
ProxySelector proxySelector;
CookieJar cookieJar;
@Nullable Cache cache;
@Nullable InternalCache internalCache;// 外部缓存
SocketFactory socketFactory;
@Nullable SSLSocketFactory sslSocketFactory;// 安全套接层socket 工厂,用于HTTPS
@Nullable CertificateChainCleaner certificateChainCleaner;// 验证确认响应证书 实用 HTTPS 申请连贯的主机名。
HostnameVerifier hostnameVerifier;// 验证确认响应证书 实用 HTTPS 申请连贯的主机名。
CertificatePinner certificatePinner;// 证书锁定,应用CertificatePinner来束缚哪些认证机构被信赖。
Authenticator proxyAuthenticator;// 代理身份验证
Authenticator authenticator;// 身份验证
ConnectionPool connectionPool;// 连接池
Dns dns;
boolean followSslRedirects; // 安全套接层重定向
boolean followRedirects;// 本地重定向
boolean retryOnConnectionFailure;// 重试连贯失败
int callTimeout;
int connectTimeout;
int readTimeout;
int writeTimeout;
int pingInterval;
// 这里是默认配置的构建参数
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
...
}
// 这里传入本人配置的构建参数
Builder(OkHttpClient okHttpClient) {
this.dispatcher = okHttpClient.dispatcher;
this.proxy = okHttpClient.proxy;
this.protocols = okHttpClient.protocols;
this.connectionSpecs = okHttpClient.connectionSpecs;
this.interceptors.addAll(okHttpClient.interceptors);
this.networkInterceptors.addAll(okHttpClient.networkInterceptors);
...
}
2.3 同步申请
同步申请应用的是execute()办法,应用形式如下。
Response response = client.newCall(request).execute();
上面是波及到的一些源码。
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@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;
}
@Override public Response execute() throws IOException {
synchronized (this) {
// 每个Call只能执行一次
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
// 告诉dispatcher曾经进入执行状态
client.dispatcher().executed(this);
// 通过一系列的拦截器申请解决和响应解决失去最终的返回后果
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
// 告诉 dispatcher 本人曾经执行结束
client.dispatcher().finished(this);
}
}
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
// 在配置 OkHttpClient 时设置的 interceptors;
interceptors.addAll(client.interceptors());
// 负责失败重试以及重定向
interceptors.add(retryAndFollowUpInterceptor);
// 申请时,对必要的Header进行一些增加,接管响应时,移除必要的Header
interceptors.add(new BridgeInterceptor(client.cookieJar()));
// 负责读取缓存间接返回、更新缓存
interceptors.add(new CacheInterceptor(client.internalCache()));
// 负责和服务器建设连贯
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
// 配置 OkHttpClient 时设置的 networkInterceptors
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);
}
// StreamAllocation 对象,它相当于一个治理类,保护了服务器连贯、并发流
// 和申请之间的关系,该类还会初始化一个 Socket 连贯对象,获取输出/输入流对象。
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
...
// Call the next interceptor in the chain.
// 实例化下一个拦截器对应的RealIterceptorChain对象
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
// 失去以后的拦截器
Interceptor interceptor = interceptors.get(index);
// 调用以后拦截器的intercept()办法,并将下一个拦截器的RealIterceptorChain对象传递上来,最初失去响应
Response response = interceptor.intercept(next);
...
return response;
}
2.4 异步申请
异步申请应用的是enqueue(),依据异步的写法,咱们能够在Builder前面跟很多的参数,如下所示。
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
...
}
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
}
promoteAndExecute();
}
// 正在筹备中的异步申请队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
// 运行中的异步申请
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
// 同步申请
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
// Promotes eligible calls from {@link #readyAsyncCalls} to {@link #runningAsyncCalls} and runs
// them on the executor service. Must not be called with synchronization because executing calls
// can call into user code.
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
// 如果其中的runningAsynCalls不满,且call占用的host小于最大数量,则将call退出到runningAsyncCalls中执行,
// 同时利用线程池执行call;否者将call退出到readyAsyncCalls中。
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());
}
return isRunning;
}
2.5 CacheInterceptor网络申请缓存解决
okHttp的缓存准则是,缓存拦截器会依据申请的信息和缓存的响应的信息来判断是否存在缓存可用,如果有能够应用的缓存,那么就返回该缓存给用户,否则就持续应用责任链模式来从服务器中获取响应。当获取到响应的时候,又会把响应缓存到磁盘下面。波及到的代码有:
@Override
public Response intercept(Chain chain) throws IOException {
// 依据request失去cache中缓存的response
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
// request判断缓存的策略,是否要应用了网络,缓存或两者都应用
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
if (cache != null) {
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();
}
Response networkResponse = null;
try {
// 调用下一个拦截器,决定从网络上来失去response
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());
}
}
// If we have a cache response too, then we're doing a conditional get.
// 如果本地曾经存在cacheResponse,那么让它和网络失去的networkResponse做比拟,决定是否来更新缓存的cacheResponse
if (cacheResponse != null) {
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());
}
}
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.
// 缓存未经缓存过的response
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;
}
2.6 ConnectInterceptor之连接池
ConnectInterceptor连接池拦截器蕴含两个方面的内容,一是网络连接角度发挥作用的网络拦截器,二是从连接池的操作角度发挥作用的拦截器。
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
// HttpCodec是对 HTTP 协定操作的形象,有两个实现:Http1Codec和Http2Codec,顾名思义,它们别离对应 HTTP/1.1 和 HTTP/2 版本的实现。在这个办法的外部实现连接池的复用解决
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
// Returns a connection to host a new stream. This // prefers the existing connection if it exists,
// then the pool, finally building a new connection.
// 调用 streamAllocation 的 newStream() 办法的时候,最终会通过一系列
// 的判断达到 StreamAllocation 中的 findConnection() 办法
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
...
// Attempt to use an already-allocated connection. We need to be careful here because our
// already-allocated connection may have been restricted from creating new streams.
// 尝试应用已调配的连贯,曾经调配的连贯可能曾经被限度创立新的流
releasedConnection = this.connection;
// 开释以后连贯的资源,如果该连贯曾经被限度创立新的流,就返回一个Socket以敞开连贯
toClose = releaseIfNoNewStreams();
if (this.connection != null) {
// We had an already-allocated connection and it's good.
result = this.connection;
releasedConnection = null;
}
if (!reportedAcquired) {
// If the connection was never reported acquired, don't report it as released!
// 如果该连贯从未被标记为取得,不要标记为公布状态,reportedAcquired 通过 acquire() 办法批改
releasedConnection = null;
}
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;
}
}
}
// 敞开连贯
closeQuietly(toClose);
if (releasedConnection != null) {
eventListener.connectionReleased(call, releasedConnection);
}
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result);
}
if (result != null) {
// If we found an already-allocated or pooled connection, we're done.
// 如果曾经从连接池中获取到了一个连贯,就将其返回
return result;
}
// If we need a route selection, make one. This is a blocking operation.
boolean newRouteSelection = false;
if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
newRouteSelection = true;
routeSelection = routeSelector.next();
}
synchronized (connectionPool) {
if (canceled) throw new IOException("Canceled");
if (newRouteSelection) {
// Now that we have a set of IP addresses, make another attempt at getting a connection from
// the pool. This could match due to connection coalescing.
// 依据一系列的 IP地址从连接池中获取一个链接
List<Route> routes = routeSelection.getAll();
for (int i = 0, size = routes.size(); i < size;i++) {
Route route = routes.get(i);
// 从连接池中获取一个连贯
Internal.instance.get(connectionPool, address, this, route);
if (connection != null) {
foundPooledConnection = true;
result = connection;
this.route = route;
break;
}
}
}
if (!foundPooledConnection) {
if (selectedRoute == null) {
selectedRoute = routeSelection.next();
}
// Create a connection and assign it to this allocation immediately. This makes it possible
// for an asynchronous cancel() to interrupt the handshake we're about to do.
// 在连接池中如果没有该连贯,则创立一个新的连贯,并将其调配,这样咱们就能够在握手之前进行终端
route = selectedRoute;
refusedStreamCount = 0;
result = new RealConnection(connectionPool, selectedRoute);
acquire(result, false);
}
}
// If we found a pooled connection on the 2nd time around, we're done.
if (foundPooledConnection) {
// 如果咱们在第二次的时候发现了一个池连贯,那么咱们就将其返回
eventListener.connectionAcquired(call, result);
return result;
}
// Do TCP + TLS handshakes. This is a blocking operation.
// 进行 TCP 和 TLS 握手
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
routeDatabase().connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
reportedAcquired = true;
// Pool the connection.
// 将该连贯放进连接池中
Internal.instance.put(connectionPool, result);
// If another multiplexed connection to the same address was created concurrently, then
// release this connection and acquire that one.
// 如果同时创立了另一个到同一地址的多路复用连贯,开释这个连贯并获取那个连贯
if (result.isMultiplexed()) {
socket = Internal.instance.deduplicate(connectionPool, address, this);
result = connection;
}
}
closeQuietly(socket);
eventListener.connectionAcquired(call, result);
return result;
}
从以上的源码剖析可知:
- 判断以后的连贯是否能够应用:流是否曾经被敞开,并且曾经被限度创立新的流;
- 如果以后的连贯无奈应用,就从连接池中获取一个连贯;
- 连接池中也没有发现可用的连贯,创立一个新的连贯,并进行握手,而后将其放到连接池中。
在从连接池中获取一个连贯的时候,应用了 Internal 的 get() 办法。Internal 有一个动态的实例,会在 OkHttpClient 的动态代码快中被初始化。咱们会在 Internal 的 get() 中调用连接池的 get() 办法来失去一个连贯。并且,从中咱们明确了连贯复用的一个益处就是省去了进行 TCP 和 TLS 握手的一个过程。因为建设连贯自身也是须要耗费一些工夫的,连贯被复用之后能够晋升咱们网络拜访的效率。
接下来详细分析下ConnectionPool是如何实现连贯治理的。
OkHttp 的缓存治理分成两个步骤,一边当咱们创立了一个新的连贯的时候,咱们要把它放进缓存外面;另一边,咱们还要来对缓存进行清理。在 ConnectionPool 中,当咱们向连接池中缓存一个连贯的时候,只有调用双端队列的 add() 办法,将其退出到双端队列即可,而清理连贯缓存的操作则交给线程池来定时执行。
private final Deque<RealConnection> connections = new ArrayDeque<>();
void put(RealConnection connection) {
assert (Thread.holdsLock(this));
if (!cleanupRunning) {
cleanupRunning = true;
// 应用线程池执行清理工作
executor.execute(cleanupRunnable);
}
// 将新建的连贯插入到双端队列中
connections.add(connection);
}
private final Runnable cleanupRunnable = new Runnable() {
@Override public void run() {
while (true) {
// 外部调用 cleanup() 办法来清理有效的连贯
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) {
}
}
}
}
};
long cleanup(long now) {
int inUseConnectionCount = 0;
int idleConnectionCount = 0;
RealConnection longestIdleConnection = null;
long longestIdleDurationNs = Long.MIN_VALUE;
// Find either a connection to evict, or the time that the next eviction is due.
synchronized (this) {
// 遍历所有的连贯
for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
RealConnection connection = i.next();
// If the connection is in use, keep searching.
// 遍历所有的连贯
if (pruneAndGetAllocationCount(connection, now) > 0) {
inUseConnectionCount++;
continue;
}
idleConnectionCount++;
// If the connection is ready to be evicted, we're done.
// 如果找到了一个能够被清理的连贯,会尝试去寻找闲置工夫最久的连贯来开释
long idleDurationNs = now - connection.idleAtNanos;
if (idleDurationNs > longestIdleDurationNs) {
longestIdleDurationNs = idleDurationNs;
longestIdleConnection = connection;
}
}
// maxIdleConnections 示意最大容许的闲置的连贯的数量,keepAliveDurationNs示意连贯容许存活的最长的工夫。
// 默认闲暇连贯最大数目为5个,keepalive 工夫最长为5分钟。
if (longestIdleDurationNs >= this.keepAliveDurationNs
|| idleConnectionCount > this.maxIdleConnections) {
// We've found a connection to evict. Remove it from the list, then close it below (outside
// of the synchronized block).
// 该连贯的时长超出了最大的沉闷时长或者闲置的连贯数量超出了最大容许的范畴,间接移除
connections.remove(longestIdleConnection);
} else if (idleConnectionCount > 0) {
// A connection will be ready to evict soon.
// 闲置的连贯的数量大于0,进展指定的工夫(等会儿会将其清理掉,当初还不是时候)
return keepAliveDurationNs - longestIdleDurationNs;
} else if (inUseConnectionCount > 0) {
// All connections are in use. It'll be at least the keep alive duration 'til we run again.
// 所有的连贯都在应用中,5分钟后再清理
return keepAliveDurationNs;
} else {
// No connections, idle or in use.
// 没有连贯
cleanupRunning = false;
return -1;
}
}
从以上的源码剖析可知,首先会对缓存中的连贯进行遍历,以寻找一个闲置工夫最长的连贯,而后依据该连贯的闲置时长和最大容许的连贯数量等参数来决定是否应该清理该连贯。同时留神下面的办法的返回值是一个工夫,如果闲置工夫最长的连贯依然须要一段时间能力被清理的时候,会返回这段时间的时间差,而后会在这段时间之后再次对连接池进行清理。
3,Retrofit
Retrofit 是一个 RESTful 的 HTTP 网络申请框架的封装,实质上网络申请是 OkHttp 实现的,而 Retrofit 仅负责网络申请接口的封装。客户端应用Retrofit ,实际上是应用 Retrofit 接口层封装申请参数、Header、Url 等信息,之后由 OkHttp 实现后续的申请操作,当服务端返回数据之后,OkHttp 再将原始的后果交给 Retrofit,Retrofit而后依据用户的需要,对后果进行解析。
3.1 根本应用
首先,定义一个HTTP API,用于形容申请,比方上面是一个Get申请。
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
而后,创立一个Retrofit并生成API的实现,返回类型是申请的返回值类型,办法的参数即是申请的参数。
// 1.Retrofit构建过程
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
// 2.创立网络申请接口类实例过程
GitHubService service = retrofit.create(GitHubService.class);
最初,再调用API办法生成Call实现申请。
// 3.生成并执行申请过程
Call<List<Repo>> repos = service.listRepos("octocat");
repos.execute() or repos.enqueue()
下面是一个简略的Get申请的事例,POST申请只须要将API定义改为POST即可。Retrofit的根本应用流程很简洁,然而简洁并不代表简略,Retrofit为了实现这种简洁的应用流程,外部应用了优良的架构设计和大量的设计模式,仔细阅读Retrofit最新版的源码会发现用到大量的设计模式。比方,Retrofit构建过程 会用到建造者模式、工厂办法模式,建网络申请接口实例过程会用到外观模式、代理模式、单例模式、策略模式、装璜模式(建造者模式),生成并执行申请过程
适配器模式(代理模式、装璜模式)。
3.2 源码剖析
3.2.1 Retrofit构建过程
1,Retrofit外围对象解析
首先Retrofit中有一个全局变量十分要害,在V2.5之前的版本,应用的是LinkedHashMap(),它是一个网络申请配置对象,是由网络申请接口中办法注解进行解析后失去的。
public final class Retrofit {
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
...
}
Retrofit应用了建造者模式通过外部类Builder类建设一个Retrofit实例,如下所示。
public static final class Builder {
// 平台类型对象(Platform -> Android)
private final Platform platform;
// 网络申请工厂,默认应用OkHttpCall(工厂办法模式)
private @Nullable okhttp3.Call.Factory callFactory;
// 网络申请的url地址
private @Nullable HttpUrl baseUrl;
// 数据转换器工厂的汇合
private final List<Converter.Factory> converterFactories = new ArrayList<>();
// 网络申请适配器工厂的汇合,默认是ExecutorCallAdapterFactory
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
// 回调办法执行器,在 Android 上默认是封装了 handler 的 MainThreadExecutor, 默认作用是:切换线程(子线程 -> 主线程)
private @Nullable Executor callbackExecutor;
private boolean validateEagerly;
2,Builder外部结构
public static final class Builder {
...
Builder(Platform platform) {
this.platform = platform;
}
public Builder() {
this(Platform.get());
}
...
}
class Platform {
private static final Platform PLATFORM = findPlatform();
static Platform get() {
return PLATFORM;
}
private static Platform findPlatform() {
try {
// 应用JVM加载类的形式判断是否是Android平台
Class.forName("android.os.Build");
if (Build.VERSION.SDK_INT != 0) {
return new Android();
}
} catch (ClassNotFoundException ignored) {
}
try {
// 同时反对Java平台
Class.forName("java.util.Optional");
return new Java8();
} catch (ClassNotFoundException ignored) {
}
return new Platform();
}
static class Android extends Platform {
...
@Override public Executor defaultCallbackExecutor() {
//切换线程(子线程 -> 主线程)
return new MainThreadExecutor();
}
// 创立默认的网络申请适配器工厂,如果是Android7.0或Java8上,则使
// 用了并发包中的CompletableFuture保障了回调的同步
// 在Retrofit中提供了四种CallAdapterFactory(策略模式):
// ExecutorCallAdapterFactory(默认)、GuavaCallAdapterFactory、
// va8CallAdapterFactory、RxJavaCallAdapterFactory
@Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor);
return Build.VERSION.SDK_INT >= 24
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
...
@Override List<? extends Converter.Factory> defaultConverterFactories() {
return Build.VERSION.SDK_INT >= 24
? singletonList(OptionalConverterFactory.INSTANCE)
: Collections.<Converter.Factory>emptyList();
}
...
static class MainThreadExecutor implements Executor {
// 获取Android 主线程的Handler
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
// 在UI线程对网络申请返回数据处理
handler.post(r);
}
}
}
3, 增加baseUrl
baseUrl最根本的性能就是将String类型的url转换为OkHttp的HttpUrl的过程,波及的代码如下。
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
return baseUrl(HttpUrl.get(baseUrl));
}
public Builder baseUrl(HttpUrl baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
List<String> pathSegments = baseUrl.pathSegments();
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
4,build过程
build()次要工作是执行Retrofit对象的创立,波及代码如下。
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
// 默认应用okhttp
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
// Android默认的callbackExecutor
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the defaultCall adapter.
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
// 增加默认适配器工厂在汇合尾部
callAdapterFactories.addAll(platform.defaultCallAdapterFactorisca llbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters thatconsumeall types.
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories();
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
3.2.2 创立网络申请接口实例
Retrofit.create()应用了外观模式和代理模式创立了网络申请的接口实例,创立create()的办法如下。
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
// 判断是否须要提前缓存ServiceMethod对象
eagerlyValidateMethods(service);
}
// 应用动静代理拿到申请接口所有注解配置后,创立网络申请接口实例
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method)) {
loadServiceMethod(method);
}
}
}
而后,咱们再看一下loadServiceMethod()办法。
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
// 解析注解配置失去了ServiceMethod
result = ServiceMethod.parseAnnotations(this, method);
// 能够看到,最终退出到ConcurrentHashMap缓存中
serviceMethodCache.put(method, result);
}
}
return result;
}
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
// 通过RequestFactory解析注解配置(工厂模式、外部应用了建造者模式)
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(method,
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
// 最终是通过HttpServiceMethod构建的申请办法
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
abstract T invoke(Object[] args);
}
3.3 Retrofit流程图
Retrofit最新的版本是2.9.0,曾经大半年没有更新了。Retrofit尽管只是一个RESTful 的HTTP 网络申请框架的封装库。然而,它外部通过 大量的设计模式 封装了 OkHttp,让使用者感到它十分简洁、易懂。它外部次要是用动静代理的形式,动静将网络申请接口的注解解析成HTTP申请,最初执行申请的过程。Retrofit残缺的流程如下图所示。
4,Glide
4.1 根本应用
作为一个Android图片加载框架,Glide具备性能全、性能高,应用简略等长处。应用上面这一行代码就能够实现图片的加载与展现。
Glide.with(context).load(url).into(iv);
除此之外,咱们还能够在图片的加载过程中指定一个占位图。
Glide.with(this)
.load(url)
.placeholder(R.drawable.noimage)
.into(iv);
4.2 源码剖析
上面是一个残缺的Glide框架的架构图。
4.2.1 with(context)
咱们应用Glide,都是从Glide.with()办法开始的,源码如下。
public static RequestManager with(Context context) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(context);
}
public static RequestManager with(Activity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
public static RequestManager with(android.app.Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
public static RequestManager with(Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
能够看到,with()办法有很多,但内容基本一致,都是通过RequestManagerRetriever.get()获取RequestManagerRetriever对象retriever,而后通过retriever.get(context)获取一个RequestManager对象并返回。这些with()办法要害的不同在于传入的参数不同,能够是Context、Activity、Fragment等等。
之所以Glide在加载图片的时候要绑定with(context)办法中传入的context的生命周期,如果传入的是Activity,那么在这个Activity销毁的时候Glide会进行图片的加载。这样做的益处在于:防止了耗费多余的资源,也防止了在Activity销毁之后加载图片从而导致空指针问题。
接下来,咱们看一下RequestManagerRetriever类的源码。
public class RequestManagerRetriever implements Handler.Callback {
//饿汉式创立单例
private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();
//返回单例对象
public static RequestManagerRetriever get() {
return INSTANCE;
}
//依据传入的参数,获取不同的RequestManager
public RequestManager get(Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
//省略无关代码......
}
下面的代码是一个饿汉式单例模式,外围是依据传入的参数获取不同的RequestManager。接下来,咱们看一下getApplicationManager办法。
private RequestManager getApplicationManager(Context context) {
// 返回一个单例
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
applicationManager = new RequestManager(context.getApplicationContext(),
new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
}
}
}
return applicationManager;
}
getApplicationManager(Context context)通过双检查单例模式创立并返回applicationManager。如果传入的context是Activity时,操作如下。
public RequestManager get(Activity activity) {
//如果不在主线程或者Android SDK的版本低于HONEYCOMB,传入的还是Application类型的context
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return get(activity.getApplicationContext());
} else {
//判断以后activity是否被销毁
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
//通过fragmentGet(activity, fm)获取RequestManager
return fragmentGet(activity, fm);
}
}
如果传入的context是Activity时,操作如下。
public RequestManager get(Activity activity) {
//如果不在主线程或者Android SDK的版本低于HONEYCOMB,传入的还是Application类型的context
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return get(activity.getApplicationContext());
} else {
//判断以后activity是否被销毁
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
//通过fragmentGet(activity, fm)获取RequestManager
return fragmentGet(activity, fm);
}
}
上面是with()办法的一个简略的总结。
- 通过RequestManagerRetriever.get()获取RequestManagerRetriever单例对象。
- 通过retriever.get(context)获取RequestManager,在get(context)办法中通过对context类型的判断做不同的解决。
- 当context是Application,通过getApplicationManager(Context context)创立并返回一个RequestManager对象。
- 当context是Activity,通过fragmentGet(activity, fm)在以后activity创立并增加一个没有界面的fragment,从而实现图片加载与activity的生命周期相绑定,之后创立并返回一个RequestManager对象。
4.2.2 load(url)
load(url)次要是用于加载网络的图片,如下所示。
Glide.with(context)
.load(url)
.placeholder(R.drawable.place_image)
.error(R.drawable.error_image)
.into(imageView);
load(url)办法的源码如下。
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
load()办法波及fromString()、load(string)两个办法,接下来看下这两个办法。
fromString()
public DrawableTypeRequest<String> fromString() {
return loadGeneric(String.class);
}
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
ModelLoader<T, InputStream> streamModelLoader =
Glide.buildStreamModelLoader(modelClass, context);
ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
Glide.buildFileDescriptorModelLoader(modelClass, context);
if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
+ " which there is a registered ModelLoader, if you are using a custom model, you must first call"
+ " Glide#register with a ModelLoaderFactory for your custom model class");
}
//这句是外围,实质是创立并返回了一个DrawableTypeRequest
return optionsApplier.apply(
new DrawableTypeRequest<T>(modelClass, streamModelLoader,
fileDescriptorModelLoader, context,
glide, requestTracker, lifecycle, optionsApplier));
}
loadGeneric()办法实质是创立并返回一个DrawableTypeRequest,Drawable类型的申请。
load(string)
load(string)办法的代码如下。
@Override
public DrawableRequestBuilder<ModelType> load(ModelType model) {
super.load(model);
return this;
}
此办法首先调用DrawableRequestBuilder的父类GenericRequestBuilder的load()办法,而后返回本身。接下来,看一下DrawableRequestBuilder父类中的load()办法,如下所示。
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {
this.model = model;
isModelSet = true;
return this;
}
DrawableRequestBuilder的父类是GenericRequestBuilder,从名字中咱们也能够看进去,前者是Drawable申请的构建者,后者是通用的申请构建者,他们是子父关系。
通过下面的剖析咱们能够晓得,在Glide.with(context).load(url)之后会返回一个DrawableTypeRequest的对象,它的父类是DrawableRequestBuilder,DrawableRequestBuilder的父类是GenericRequestBuilder,咱们写的placeHolder()、error()等等相干图片申请配置的办法都定义在GenericRequestBuilder中。
4.2.3 into(imageView)
Glide中的前两部是创立了一个Request,这个Request能够了解为对图片加载的配置申请,须要留神的是仅仅是创立了一个申请,而没有去执行。只有当调用into()办法时,这个申请才会真正的被执行。into(imageView)的源码如下。
public Target<GlideDrawable> into(ImageView view) {
return super.into(view);
}
发现它调用的是父类GenericRequestBuilder的into()办法,那咱们持续看GenericRequestBuilder中的into()办法。
public Target<TranscodeType> into(ImageView view) {
//确保在主线程
Util.assertMainThread();
//确保view不为空
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}
//对ScaleType进行配置
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
//外围
return into(glide.buildImageViewTarget(view, transcodeClass));
}
能够看到,下面的办法就是into()的外围代码,它定义在GenericRequestBuilder这个通用的申请构建者中。办法的外围是最初一行:into(glide.buildImageViewTarget(view, transcodeClass)),首先通过glide.buildImageViewTarget(view, transcodeClass)创立出一个Target类型的对象,而后把这个target传入GenericRequestBuilder中的into()办法中。
对于buildImageViewTarget(view, transcodeClass)办法,咱们就不再赘述了。
6,EventBus
6.1 根本应用
6.1.1 基本概念
EventBus是一种用于Android的事件公布-订阅的事件总线。它简化了应用程序内各个组件之间进行通信的复杂度,尤其是碎片之间进行通信的问题,能够防止因为应用播送通信而带来的诸多不便。
EventBus由三个角色形成:Publisher、Event和Subscriber。
- Event:事件,它能够是任意类型,EventBus会依据事件类型进行全局的告诉。
- Subscriber:事件订阅者,在EventBus 3.0之前咱们必须定义以onEvent结尾的那几个办法,别离是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,而在3.0之后事件处理的办法名能够随便取,不过须要加上注解@subscribe,并且指定线程模型,默认是POSTING。
- Publisher:事件的发布者,能够在任意线程里公布事件。个别状况下,应用EventBus.getDefault()就能够失去一个EventBus对象,而后再调用post(Object)办法即可。
EventBus是一种典型的事件公布-订阅模式,事件由发布者通过EvenentBus传递给订阅者,总体框架如下。
EventBus提供了四种线程模型,别离是:
- POSTING:默认,示意事件处理函数的线程跟公布事件的线程在同一个线程。
- MAIN:示意事件处理函数的线程在主线程(UI)线程,因而在这里不能进行耗时操作。
- BACKGROUND:示意事件处理函数的线程在后盾线程,因而不能进行UI操作。如果公布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后盾线程,如果果公布事件的线程是在后盾线程,那么事件处理函数就应用该线程。
- ASYNC:示意无论事件公布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。
6.1.2 根本应用
EventBus应用流程上分为3步。首先,定义一个事件类。
public class SayHelloEvent {
private String message;
public void sayHellow(String message) {
this.message = message;
}
}
而后,筹备一个事件的订阅者,为了避免事件带来的性能耗费问题,还须要在onStop生命周期中登记事件的订阅。
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(SayHelloEvent event) {
String message = event.getMessage();
...
}
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
最初,在须要发送事件的中央调用EventBus.getDefault().post办法发送事件。
EventBus.getDefault().post(new SayHelloEvent("Hello,EventBus!!!"));
6.2 源码剖析
6.2.1 EventBus.getDefault().register(this)
首先,咱们从获取EventBus实例的办法getDefault()开发看EventBus。
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
能够看到,在getDefault()中应用了双重校验并加锁的单例模式来创立EventBus实例,而后咱们看一下构造方法。
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
在EventBus的默认构造方法中又调用了它的另一个有参构造方法,将一个类型为EventBusBuilder的DEFAULT_BUILDER对象传递进去了。再看一下EventBusBuilder的构造方法。
public class EventBusBuilder {
...
EventBusBuilder() {
}
...
}
外面根本什么也没做,持续查看EventBus的这个有参构造方法。
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private final Map<Class<?>, Object> stickyEvents;
EventBus(EventBusBuilder builder) {
...
// 1
subscriptionsByEventType = new HashMap<>();
// 2
typesBySubscriber = new HashMap<>();
// 3
stickyEvents = new ConcurrentHashMap<>();
// 4
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
...
// 5
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
// 从builder取中一些列订阅相干信息进行赋值
...
// 6
executorService = builder.executorService;
}
在正文1处,创立了一个subscriptionsByEventType对象,能够看到它是一个类型为HashMap的subscriptionsByEventType对象,并且其key为 Event 类型,value为 Subscription链表。这里的Subscription是一个订阅信息对象,它外面保留了两个重要的字段,一个是类型为 Object 的 subscriber,该字段即为注册的对象(在 Android 中时通常是 Activity对象);另一个是 类型为SubscriberMethod 的 subscriberMethod,它就是被@Subscribe注解的那个订阅办法,外面保留了一个重要的字段eventType,它是 Class<?> 类型的,代表了 Event 的类型。在正文2处,新建了一个类型为 Map 的typesBySubscriber对象,它的key为subscriber对象,value为subscriber对象中所有的 Event 类型链表,日常应用中仅用于判断某个对象是否注册过。在正文3处新建了一个类型为ConcurrentHashMap的stickyEvents对象,它是专用于粘性事件处理的一个字段,key为事件的Class对象,value为以后的事件。
可能有的同学还不晓得粘性事件,所谓粘性事件是绝对一般事件来说的。一般事件是先注册,而后发送事件能力收到;而粘性事件,在发送事件之后再订阅该事件也能收到。并且,粘性事件会保留在内存中,每次进入都会去内存中查找获取最新的粘性事件,除非你手动解除注册。
而后,咱们看正文5这行代码新建了一个subscriberMethodFinder对象,这是从EventBus中抽离出的订阅办法查问的一个对象。在正文6处,从builder中取出了一个默认的线程池对象,它由Executors的newCachedThreadPool()办法创立,它是一个有则用、无则创立、无数量下限的线程池。
接下来,咱们再看一下EventBus的regist()办法。
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
// 1
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
// 2
subscribe(subscriber, subscriberMethod);
}
}
}
下面代码的次要作用是,依据以后注册类获取 subscriberMethods这个订阅办法列表,而后应用了加强for循环令subsciber对象 对 subscriberMethods 中每个 SubscriberMethod 进行订阅。
更深层次的代码,咱们就不做剖析了,regist()次要实现了如下一些事件。
- 依据单例设计模式创立一个EventBus对象,同时创立一个EventBus.Builder对象对EventBus进行初始化,其中有三个比拟重要的汇合和一个SubscriberMethodFinder对象。
- 调用register办法,首先通过反射获取到订阅者的Class对象。
- 通过SubscriberMethodFinder对象获取订阅者中所有订阅的事件汇合,它先从缓存中获取,如果缓存中有,间接返回;如果缓存中没有,通过反射的形式去遍历订阅者外部被注解的办法,将这些办法放入到汇合中进行返回。
- 遍历第三步获取的汇合,将订阅者和事件进行绑定。
- 在绑定之后会判断绑定的事件是否是粘性事件,如果是粘性事件,间接调用postToSubscription办法,将之前发送的粘性事件发送给订阅者。其实这也很好了解,在讲粘性事件时说过,如果在粘性事件发送之前注册的订阅者,当发送粘性事件时,会接管到该事件;如果是粘性事件发送之后注册的订阅者,同样也能接管到事件。
6.2.2 EventBus.getDefault().post()
post()办法的代码如下。
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List <Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
此办法的作用是,获取以后线程的事件汇合,将要发送的事件退出到汇合中。而后通过循环,只有事件汇合中还有事件,就始终发送。这里的currentPostingThreadState 是一个 ThreadLocal 类型的对象,外面存储了 PostingThreadState,而 PostingThreadState 中蕴含了一个 eventQueue 和其余一些标记位,相干的源码如下。
private final ThreadLocal <PostingThreadState> currentPostingThreadState = new ThreadLocal <PostingThreadState> () {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
final static class PostingThreadState {
final List <Object> eventQueue = new ArrayList<>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
而后,咱们看一下postSingleEvent() 办法,代码如下。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |=
postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
...
}
}
下面代码的作用是,获取事件的Class对象,找到以后的event的所有父类和实现的接口的class汇合。遍历这个汇合,调用发送单个事件的办法进行发送。
能够看到,下面的代码首先取出 Event 的 class 类型,接着会对 eventInheritance 标记位 判断,它默认为true,如果设为 true 的话,它会在发射事件的时候判断是否须要发射父类事件,设为 false,可能进步一些性能。接着,调用lookupAllEventTypes() 办法,它的作用就是取出 Event 及其父类和接口的 class 列表,当然反复取的话会影响性能,所以它也做了一个 eventTypesCache 的缓存,这样就不必反复调用 getSuperclass() 办法。最初,调用postSingleEventForEventType()办法。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class <?> eventClass) {
CopyOnWriteArrayList <Subscription> subscriptions;
synchronized(this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription: subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
依据事件获取所有订阅它的订阅者汇合,遍历汇合,将事件发送给订阅者。办法最初又调用了postToSubscription()办法。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknow thread mode: " + subscription.subscriberMethod.threadMode);
}
}
能够看出,通过threadMode 来判断在哪个线程中去执行订阅音讯。
- POSTING:执行 invokeSubscriber() 办法,外部间接采纳反射调用。
- MAIN:首先去判断以后是否在 UI 线程,如果是的话则间接反射调用,否则调用mainThreadPoster的enqueue()办法,即把以后的办法退出到队列之中,而后通过 handler 去发送一个音讯,在 handler 的 handleMessage 中去执行办法。
- MAIN_ORDERED:与MAIN相似,不过是确保是程序执行的。
- BACKGROUND:判断以后是否在 UI 线程,如果不是的话则间接反射调用,是的话通过backgroundPoster的enqueue()办法 将办法退出到后盾的一个队列,最初通过线程池去执行。留神,backgroundPoster在 Executor的execute()办法 上增加了 synchronized关键字 并设立 了管制标记flag,保障任一时间只且仅能有一个工作会被线程池执行。
- ASYNC:逻辑实现相似于BACKGROUND,将工作退出到后盾的一个队列,最终由Eventbus 中的一个线程池去调用,这里的线程池与 BACKGROUND 逻辑中的线程池用的是同一个,即应用Executors的newCachedThreadPool()办法创立的线程池,它是一个有则用、无则创立、无数量下限的线程池。不同于backgroundPoster的保障任一时间只且仅能有一个工作会被线程池执行的个性,这里asyncPoster则是异步运行的,能够同时接管多个工作。
能够看到,EventBus.getDefault().post()次要做了如下一些事件:
- 获取以后线程的事件汇合,将要发送的事件退出到汇合中。
- 通过循环,只有事件汇合中还有事件,就始终发送。
- 获取事件的Class对象,找到以后的event的所有父类和实现的接口的class汇合。遍历这个汇合,调用发送单个事件的办法进行发送。
- 依据事件获取所有订阅它的订阅者汇合,遍历汇合,将事件发送给订阅者。
- 发送给订阅者时,依据订阅办法的线程模式调用订阅办法,如果须要线程切换,则切换线程进行调用;否则,间接调用。
6.2.3 EventBus.getDefault().unregister(this)
后面曾经介绍了订阅者注册和音讯的发送,接下来咱们再来看一下音讯的解绑。
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
}
}
能够看到,在unsubscribeByEventType() 办法中对 subscriptionsByEventType 移除了该 subscriber 的所有订阅信息。最初,在移除了注册对象和其对应的所有 Event 事件链表。
6.2.4 EventBus.getDefault.postSticky()
如果想要发射 sticky 事件,须要通过 EventBus的postSticky() 办法,如下所示。
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
post(event);
}
下面代码的次要作用是先将该事件放入StickyEvents 中,而后再应用post()办法发送事件。后面咱们在剖析register()办法的时候,有一段粘性事件的剖析。
if (subscriberMethod.sticky) {
Object stickyEvent = stickyEvents.get(eventType);
if (stickyEvent != null) {
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
能够看到,在注册事件时,相熟会判断以后事件是否是 sticky 事件,如果是则从 stickyEvents 中拿出该事件并执行 postToSubscription() 办法。
发表回复