乐趣区

关于android:Android-面试之开源库分析

有人说,当初的客户端面试越来越卷了,须要开发者把握的内容也越来越多,从根底的 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()次要实现了如下一些事件。

  1. 依据单例设计模式创立一个 EventBus 对象,同时创立一个 EventBus.Builder 对象对 EventBus 进行初始化,其中有三个比拟重要的汇合和一个 SubscriberMethodFinder 对象。
  2. 调用 register 办法, 首先通过反射获取到订阅者的 Class 对象。
  3. 通过 SubscriberMethodFinder 对象获取订阅者中所有订阅的事件汇合, 它先从缓存中获取,如果缓存中有,间接返回;如果缓存中没有,通过反射的形式去遍历订阅者外部被注解的办法,将这些办法放入到汇合中进行返回。
  4. 遍历第三步获取的汇合,将订阅者和事件进行绑定。
  5. 在绑定之后会判断绑定的事件是否是粘性事件,如果是粘性事件,间接调用 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()次要做了如下一些事件:

  1. 获取以后线程的事件汇合,将要发送的事件退出到汇合中。
  1. 通过循环,只有事件汇合中还有事件,就始终发送。
  2. 获取事件的 Class 对象,找到以后的 event 的所有父类和实现的接口的 class 汇合。遍历这个汇合,调用发送单个事件的办法进行发送。
  3. 依据事件获取所有订阅它的订阅者汇合,遍历汇合,将事件发送给订阅者。
  4. 发送给订阅者时,依据订阅办法的线程模式调用订阅办法,如果须要线程切换,则切换线程进行调用;否则,间接调用。

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() 办法。

退出移动版