OkHttp 3.11.0版本提供了EventListener接口,可以让调用者接收一系列网络请求过程中的事件,例如DNS解析、TSL/SSL连接、Response接收等。通过继承此接口,调用者可以监视整个应用中网络请求次数、流量大小、耗时情况。
使用方法如下:
public class HttpEventListener extends EventListener {
/**
* 自定义EventListener工厂
*/
public static final Factory FACTORY = new Factory() {
final AtomicLong nextCallId = new AtomicLong(1L);
@Override
public EventListener create(Call call) {
long callId = nextCallId.getAndIncrement();
return new HttpEventListener(callId, call.request().url(), System.nanoTime());
}
};
/**
* 每次请求的标识
*/
private final long callId;
/**
* 每次请求的开始时间,单位纳秒
*/
private final long callStartNanos;
private StringBuilder sbLog;
public HttpEventListener(long callId, HttpUrl url, long callStartNanos) {
this.callId = callId;
this.callStartNanos = callStartNanos;
this.sbLog = new StringBuilder(url.toString()).append(" ").append(callId).append(":");
}
private void recordEventLog(String name) {
long elapseNanos = System.nanoTime() - callStartNanos;
sbLog.append(String.format(Locale.CHINA, "%.3f-%s", elapseNanos / 1000000000d, name)).append(";");
if (name.equalsIgnoreCase("callEnd") || name.equalsIgnoreCase("callFailed")) {
//打印出每个步骤的时间点
NearLogger.i(sbLog.toString());
}
}
@Override
public void callStart(Call call) {
super.callStart(call);
recordEventLog("callStart");
}
@Override
public void dnsStart(Call call, String domainName) {
super.dnsStart(call, domainName);
recordEventLog("dnsStart");
}
@Override
public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {
super.dnsEnd(call, domainName, inetAddressList);
recordEventLog("dnsEnd");
}
@Override
public void connectStart(Call call, InetSocketAddress inetSocketAddress, Proxy proxy) {
super.connectStart(call, inetSocketAddress, proxy);
recordEventLog("connectStart");
}
@Override
public void secureConnectStart(Call call) {
super.secureConnectStart(call);
recordEventLog("secureConnectStart");
}
@Override
public void secureConnectEnd(Call call, @Nullable Handshake handshake) {
super.secureConnectEnd(call, handshake);
recordEventLog("secureConnectEnd");
}
@Override
public void connectEnd(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, @Nullable Protocol protocol) {
super.connectEnd(call, inetSocketAddress, proxy, protocol);
recordEventLog("connectEnd");
}
@Override
public void connectFailed(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, @Nullable Protocol protocol, IOException ioe) {
super.connectFailed(call, inetSocketAddress, proxy, protocol, ioe);
recordEventLog("connectFailed");
}
@Override
public void connectionAcquired(Call call, Connection connection) {
super.connectionAcquired(call, connection);
recordEventLog("connectionAcquired");
}
@Override
public void connectionReleased(Call call, Connection connection) {
super.connectionReleased(call, connection);
recordEventLog("connectionReleased");
}
@Override
public void requestHeadersStart(Call call) {
super.requestHeadersStart(call);
recordEventLog("requestHeadersStart");
}
@Override
public void requestHeadersEnd(Call call, Request request) {
super.requestHeadersEnd(call, request);
recordEventLog("requestHeadersEnd");
}
@Override
public void requestBodyStart(Call call) {
super.requestBodyStart(call);
recordEventLog("requestBodyStart");
}
@Override
public void requestBodyEnd(Call call, long byteCount) {
super.requestBodyEnd(call, byteCount);
recordEventLog("requestBodyEnd");
}
@Override
public void responseHeadersStart(Call call) {
super.responseHeadersStart(call);
recordEventLog("responseHeadersStart");
}
@Override
public void responseHeadersEnd(Call call, Response response) {
super.responseHeadersEnd(call, response);
recordEventLog("responseHeadersEnd");
}
@Override
public void responseBodyStart(Call call) {
super.responseBodyStart(call);
recordEventLog("responseBodyStart");
}
@Override
public void responseBodyEnd(Call call, long byteCount) {
super.responseBodyEnd(call, byteCount);
recordEventLog("responseBodyEnd");
}
@Override
public void callEnd(Call call) {
super.callEnd(call);
recordEventLog("callEnd");
}
@Override
public void callFailed(Call call, IOException ioe) {
super.callFailed(call, ioe);
recordEventLog("callFailed");
}
}
自定义EventListener实现和EventListener工厂实现,其中每个网络请求都对应一个EventListener监听。对于相同地址的请求,为了区分其对应的EventListener,需要通过EventListener工厂创建带有唯一标识的EventListener。这里是为每个EventListener分配唯一的自增编号。
在创建OkHttpClient时将EventListener工厂作为构建参数添加进去。
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.eventListenerFactory(HttpEventListener.FACTORY)
.build();
下面详细说明每个回调的触发时机:
callStart(Call call) 请求开始
当一个Call(代表一个请求)被同步执行或被添加异步队列中时。
final class RealCall implements Call {
@Override
public Response execute() throws IOException {
eventListener.callStart(this);
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
}
@Override
public void enqueue(Callback responseCallback) {
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
}
由于线程或事件流的限制,这里的请求开始并不是真正的去执行的这个请求。
如果发生重定向和多域名重试时,这个方法也仅被调用一次。
callFailed/callEnd 请求异常和请求结束
每一个callStart都对应着一个callFailed或callEnd。
callFailed在两种情况下被调用
第一种是在请求执行的过程中发生异常时。
第二种是在请求结束后,关闭输入流时产生异常时。
网友评论