如何优雅地知道 OkHttp 的请求时间

 天顺注册招商   2021-01-11 07:52   26 人阅读  0 条评论

天顺娱乐总代【总代QQ223345】

原标题:如何优雅地知道 OkHttp 的请求时间

转自:杨充

转自:杨充

先提问一个问题

OkHttp如何进行各个请求环节的耗时统计呢?

OkHttp 版本提供了EventListener接口,可以让调用者接收一系列网络请求过程中的事件,例如DNS解析、TSL/SSL连接、Response接收等。通过继承此接口,调用者可以监视整个应用中网络请求次数、流量大小、耗时(比如dns解析时间,请求时间,响应时间等等)情况。

EventListener回调原理

天顺娱乐总代【总代QQ223345】先来看一下。

publicabstractclassEventListener{

// 按照请求顺序回调

publicvoidcallStart(Call call){}

// 域名解析

publicvoiddnsStart(Call call, String domainName){}

publicvoiddnsEnd(Call call, String domainName, List<InetAddress> inetAddressList){}

// 释放当前Transmitter的RealConnection

publicvoidconnectionReleased(Call call, Connection connection){}

publicvoidconnectionAcquired(call, result){};

// 开始连接

publicvoidconnectStart(call, route.socketAddress, proxy) {}

// 请求

publicvoidrequestHeadersStart(@NotNull Call call){}

publicvoidrequestHeadersEnd(@NotNull Call call, @NotNull Request request){}

展开全文

// 响应

publicvoidrequestBodyStart(@NotNull Call call){}

publicvoidrequestBodyEnd(@NotNull Call call, longbyteCount) {}

// 结束

publicvoidcallEnd(Call call){}

// 失败

publicvoidcallFailed(Call call, IOException ioe){}

}

请求开始结束监听

callStart(Call call) 请求开始。

当一个Call(代表一个请求)被同步执行或被添加异步队列中时,即会调用这个回调方法。需要说明这个方法是在dispatcher.executed/enqueue前执行的。由于线程或事件流的限制,这里的请求开始并不是真正的去执行的这个请求。如果发生重定向和多域名重试时,这个方法也仅被调用一次。

finalclassRealCallimplementsCall{

@Override

publicResponse executethrowsIOException {

eventListener.callStart( this);

client.dispatcher.executed( this);

Response result = getResponseWithInterceptorChain;

if(result == null) thrownewIOException( "Canceled");

returnresult;

}

@Override

publicvoidenqueue(Callback responseCallback){

eventListener.callStart( this);

client.dispatcher.enqueue( newAsyncCall(responseCallback));

}

}

callFailed/callEnd 请求异常和请求结束。

每一个callStart都对应着一个callFailed或callEnd。callFailed在两种情况下被调用,第一种是在请求执行的过程中发生异常时。第二种是在请求结束后,关闭输入流时产生异常时。

finalclassRealCallimplementsCall{

@Override

publicResponse executethrowsIOException {

try{

client.dispatcher.executed( this);

Response result = getResponseWithInterceptorChain;

if(result == null) thrownewIOException( "Canceled");

returnresult;

} catch(IOException e) {

eventListener.callFailed( this, e);

throwe;

}

}

finalclassAsyncCallextendsNamedRunnable{

@Override

protectedvoidexecute{

try{

Response response = getResponseWithInterceptorChain;

} catch(IOException e) {

eventListener.callFailed(RealCall. this, e);

}

}

}

}

//第二种

publicfinalclassStreamAllocation{

publicvoidstreamFinished( booleannoNewStreams, HttpCodec codec, longbytesRead, IOException e) {

...

if(e != null) {

eventListener.callFailed(call, e);

} elseif(callEnd) {

eventListener.callEnd(call);

}

...

}

}

callEnd也有两种调用场景。第一种也是在关闭流时。第二种是在释放连接时。

publicfinalclassStreamAllocation{

publicvoidstreamFinished( booleannoNewStreams, HttpCodec codec, longbytesRead, IOException e) {

...

if(e != null) {

eventListener.callFailed(call, e);

} elseif(callEnd) {

eventListener.callEnd(call);

}

...

}

publicvoidrelease{

...

if(releasedConnection != null) {

eventListener.connectionReleased(call, releasedConnection);

eventListener.callEnd(call);

}

}

}

为什么会将关闭流和关闭连接区分开?

  • 在http2版本中,一个连接上允许打开多个流,OkHttp使用StreamAllocation来作为流和连接的桥梁。当一个流被关闭时,要检查这条连接上还有没有其他流,如果没有其他流了,则可以将连接关闭了。

  • streamFinished和release作用是一样的,都是关闭当前流,并检查是否需要关闭连接。不同的是,当调用者手动取消请求时,调用的是release方法,并由调用者负责关闭请求输出流和响应输入流。

在http2版本中,一个连接上允许打开多个流,OkHttp使用StreamAllocation来作为流和连接的桥梁。当一个流被关闭时,要检查这条连接上还有没有其他流,如果没有其他流了,则可以将连接关闭了。

streamFinished和release作用是一样的,都是关闭当前流,并检查是否需要关闭连接。不同的是,当调用者手动取消请求时,调用的是release方法,并由调用者负责关闭请求输出流和响应输入流。

dns解析开始结束监听

dnsStart开始

其中的lookup(String hostname)方法代表了域名解析的过程,dnsStart/dnsEnd就是在lookup前后被调用的。DNS解析是请求DNS(Domain Name System)服务器,将域名解析成ip的过程。域名解析工作是由JDK中的InetAddress类完成的。

/** Prepares the socket addresses to attempt for the current proxy or host. */

privatevoidresetNextInetSocketAddress(Proxy proxy)throwsIOException {

if(proxy.type == Proxy.Type.SOCKS) {

inetSocketAddresses.add(InetSocketAddress.createUnresolved(socketHost, socketPort));

} else{

eventListener.dnsStart(call, socketHost);

// Try each address for best behavior in mixed IPv4/IPv6 environments.

List<InetAddress> addresses = address.dns.lookup(socketHost);

if(addresses.isEmpty) {

thrownewUnknownHostException(address.dns + " returned no addresses for "+ socketHost);

}

eventListener.dnsEnd(call, socketHost, addresses);

}

}

那么RouteSelector这个类是在哪里调用。

publicfinalclassStreamAllocation{

publicStreamAllocation(ConnectionPool connectionPool, Address address, Call call,

EventListener eventListener, Object callStackTrace) {

this.routeSelector = newRouteSelector(address, routeDatabase, call, eventListener);

}

}

连接开始结束监听

connectStart连接开

OkHttp是使用Socket接口建立Tcp连接的,所以这里的连接就是指Socket建立一个连接的过程。当连接被重用时,connectStart/connectEnd不会被调用。当请求被重定向到新的域名后,connectStart/connectEnd会被调用多次。

privatevoidconnectSocket( intconnectTimeout, intreadTimeout, Call call,

EventListener eventListener) throwsIOException {

Proxy proxy = route.proxy;

Address address = route.address;

rawSocket = proxy.type == Proxy.Type.DIRECT || proxy.type == Proxy.Type.HTTP

? address.socketFactory.createSocket

: newSocket(proxy);

eventListener.connectStart(call, route.socketAddress, proxy);

}

connectEnd连接结束

因为创建的连接有两种类型(服务端直连和隧道代理),所以callEnd有两处调用位置。为了在基于代理的连接上使用SSL,需要单独发送CONECT请求。在连接过程中,无论是Socket连接失败,还是TSL/SSL握手失败,都会回调connectEnd。

publicvoidconnect( intconnectTimeout, intreadTimeout, intwriteTimeout,

while( true) {

try{

establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener);

eventListener.connectEnd(call, route.socketAddress, route.proxy, protocol);

break;

} catch(IOException e) {

eventListener.connectFailed(call, route.socketAddress, route.proxy, null, e);

}

}

privatevoidconnectTunnel( intconnectTimeout, intreadTimeout, intwriteTimeout, Call call,

EventListener eventListener) throwsIOException {

Request tunnelRequest = createTunnelRequest;

HttpUrl url = tunnelRequest.url;

for( inti = 0; i < MAX_TUNNEL_ATTEMPTS; i++) {

connectSocket(connectTimeout, readTimeout, call, eventListener);

eventListener.connectEnd(call, route.socketAddress, route.proxy, null);

}

}

06.TLS连接开始结束监听

开始连接,代码如下所示:

在上面看到,在Socket建立连接后,会执行一个establishProtocol方法,这个方法的作用就是TSL/SSL握手。当存在重定向或连接重试的情况下,secureConnectStart/secureConnectEnd会被调用多次。

privatevoidestablishProtocol(ConnectionSpecSelector connectionSpecSelector,

intpingIntervalMillis, Call call, EventListener eventListener) throwsIOException {

if(route.address.sslSocketFactory == null) {

protocol = Protocol.HTTP_1_1;

socket = rawSocket;

return;

}

eventListener.secureConnectStart(call);

connectTls(connectionSpecSelector);

eventListener.secureConnectEnd(call, handshake);

}

结合连接监听可知。如果我们使用了HTTPS安全连接,在TCP连接成功后需要进行TLS安全协议通信,等TLS通讯结束后才能算是整个连接过程的结束,也就是说connectEnd在secureConnectEnd之后调用。

所以顺序是这样的:

connectStart ---> secureConnectStart ---> secureConnectEnd ---> ConnectEnd。

连接绑定和释放监听

因为OkHttp是基于连接复用的,当一次请求结束后并不会马上关闭当前连接,而是放到连接池中。

当有相同域名的请求时,会从连接池中取出对应的连接使用,减少了连接的频繁创建和销毁。当根据一个请求从连接池取连接时,并打开输入输出流就是acquired,用完释放流就是released。如果直接复用StreamAllocation中的连接,则不会调用connectionAcquired/connectReleased。

privateRealConnection findConnection( intconnectTimeout, intreadTimeout, intwriteTimeout,

intpingIntervalMillis, booleanconnectionRetryEnabled) throwsIOException {

synchronized(connectionPool) {

if(result == null) {

// 第一次查缓存 Attempt to get a connection from the pool.

// Attempt to get a connection from the pool.

Internal.instance.get(connectionPool, address, this, null);

}

}

if(releasedConnection != null) {

eventListener.connectionReleased(call, releasedConnection);

}

if(foundPooledConnection) {

eventListener.connectionAcquired(call, result);

}

synchronized(connectionPool) {

if(canceled) thrownewIOException( "Canceled");

if(newRouteSelection) {

//第二次查缓存

List<Route> routes = routeSelection.getAll;

for( inti = 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) {

//如果缓存没有,则新建连接

route = selectedRoute;

refusedStreamCount = 0;

result = newRealConnection(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);

returnresult;

}

// Do TCP + TLS handshakes. This is a blocking operation.

result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,

connectionRetryEnabled, call, eventListener);

routeDatabase.connected(result.route);

eventListener.connectionAcquired(call, result);

returnresult;

}

connectionAcquired是在连接成功后被调用的。

但是在连接复用的情况下没有连接步骤,connectAcquired会在获取缓存连接后被调用。由于StreamAllocation是连接“Stream”和“Connection”的桥梁,所以在StreamAllocation中会持有一个RealConnection引用。StreamAllocation在查找可用连接的顺序为:StreamAllocation.RealConnection -> ConnectionPool -> ConnectionPool -> new RealConnection

request请求监听

在OkHttp中,HttpCodec负责对请求和响应按照Http协议进行编解码,包含发送请求头、发送请求体、读取响应头、读取响应体。requestHeaders开始和结束,这个直接看CallServerInterceptor拦截器代码即可。

publicfinalclassCallServerInterceptorimplementsInterceptor{

@OverridepublicResponse intercept(Chain chain)throwsIOException {

RealInterceptorChain realChain = (RealInterceptorChain) chain;

HttpCodec httpCodec = realChain.httpStream;

StreamAllocation streamAllocation = realChain.streamAllocation;

RealConnection connection = (RealConnection) realChain.connection;

Request request = realChain.request;

longsentRequestMillis = System.currentTimeMillis;

realChain.eventListener.requestHeadersStart(realChain.call);

httpCodec.writeRequestHeaders(request);

realChain.eventListener.requestHeadersEnd(realChain.call, request);

if(HttpMethod.permitsRequestBody(request.method) && request.body != null) {

if(responseBuilder == null) {

// Write the request body if the "Expect: 100-continue" expectation was met.

realChain.eventListener.requestBodyStart(realChain.call);

longcontentLength = request.body.contentLength;

CountingSink requestBodyOut =

newCountingSink(httpCodec.createRequestBody(request, contentLength));

BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);

request.body.writeTo(bufferedRequestBody);

bufferedRequestBody.close;

realChain.eventListener.requestBodyEnd(realChain.call, requestBodyOut.successfulCount);

}

}

returnresponse;

}

}

09.response响应监听

responseHeadersStart和responseHeadersEnd代码如下所示:

publicfinalclassCallServerInterceptorimplementsInterceptor{

@OverridepublicResponse intercept(Chain chain)throwsIOException {

Response.Builder responseBuilder = null;

if(HttpMethod.permitsRequestBody(request.method) && request.body != null) {

if( "100-continue".equalsIgnoreCase(request.header( "Expect"))) {

httpCodec.flushRequest;

realChain.eventListener.responseHeadersStart(realChain.call);

responseBuilder = httpCodec.readResponseHeaders( true);

}

}

httpCodec.finishRequest;

if(responseBuilder == null) {

realChain.eventListener.responseHeadersStart(realChain.call);

responseBuilder = httpCodec.readResponseHeaders( false);

}

intcode = response.code;

if(code == 100) {

// server sent a 100-continue even though we did not request one.

// try again to read the actual response

responseBuilder = httpCodec.readResponseHeaders( false);

response = responseBuilder

.request(request)

.handshake(streamAllocation.connection.handshake)

.sentRequestAtMillis(sentRequestMillis)

.receivedResponseAtMillis(System.currentTimeMillis)

.build;

code = response.code;

}

realChain.eventListener .responseHeadersEnd(realChain.call, response);

returnresponse;

}

}

responseBodyStart监听

响应体的读取有些复杂,要根据不同类型的Content-Type决定如何读取响应体,例如固定长度的、基于块(chunk)数据的、未知长度的。具体看openResponseBody方法里面的代码。同时Http1与Http2也有不同的解析方式。下面以Http1为例。

publicfinalclassHttp1CodecimplementsHttpCodec{

@OverridepublicResponseBody openResponseBody(Response response)throwsIOException {

streamAllocation.eventListener.responseBodyStart(streamAllocation.call);

String contentType = response.header( "Content-Type");

if(!HttpHeaders.hasBody(response)) {

Source source = newFixedLengthSource( 0);

returnnewRealResponseBody(contentType, 0, Okio.buffer(source));

}

if( "chunked".equalsIgnoreCase(response.header( "Transfer-Encoding"))) {

Source source = newChunkedSource(response.request.url);

returnnewRealResponseBody(contentType, - 1L, Okio.buffer(source));

}

longcontentLength = HttpHeaders.contentLength(response);

if(contentLength != - 1) {

Source source = newFixedLengthSource(contentLength);

returnnewRealResponseBody(contentType, contentLength, Okio.buffer(source));

}

returnnewRealResponseBody(contentType, - 1L, Okio.buffer(newUnknownLengthSource));

}

}

responseBodyEnd监听

由下面代码可知,当响应结束后,会调用连接callEnd回调(如果异常则会调用callFailed回调)。

publicfinalclassStreamAllocation{

publicvoidstreamFinished( booleannoNewStreams, HttpCodec codec, longbytesRead, IOException e) {

eventListener.responseBodyEnd(call, bytesRead);

if(releasedConnection != null) {

eventListener.connectionReleased(call, releasedConnection);

}

if(e != null) {

eventListener.callFailed(call, e);

} elseif(callEnd) {

eventListener.callEnd(call);

}

}

}

如何监听统计耗时

如何消耗记录时间

在OkHttp库中有一个EventListener类。该类是网络事件的侦听器。扩展这个类以监视应用程序的HTTP调用的数量、大小和持续时间。所有启动/连接/获取事件最终将接收到匹配的结束/释放事件,要么成功(非空参数),要么失败(非空可抛出)。

比如,可以在开始链接记录时间;dns开始,结束等方法解析记录时间,可以计算dns的解析时间。

比如,可以在开始请求记录时间,记录connectStart,connectEnd等方法时间,则可以计算出connect连接时间。

代码如下所示:

Eventlistener只适用于没有并发的情况,如果有多个请求并发执行我们需要使用Eventlistener. Factory来给每个请求创建一个Eventlistener。这个mRequestId是唯一值,可以选择使用AtomicInteger自增+1的方式设置id,这个使用了cas保证多线程条件下的原子性特性。

/**

* <pre>

* @authoryangchong

* email : yangchong211@163.com

* time : 2019/07/22

* desc : EventListener子类

* revise:

* </pre>

*/

publicclassNetworkListenerextendsEventListener{

privatestaticfinalString TAG = "NetworkEventListener";

privatestaticAtomicInteger mNextRequestId = newAtomicInteger( 0);

privateString mRequestId ;

publicstaticFactory get{

Factory factory = newFactory {

@NotNull

@Override

publicEventListener create(@NotNull Call call){

returnnewNetworkListener;

}

};

returnfactory;

}

@Override

publicvoidcallStart(@NotNull Call call){

super.callStart(call);

//mRequestId = mNextRequestId.getAndIncrement + "";

//getAndAdd,在多线程下使用cas保证原子性

mRequestId = String.valueOf(mNextRequestId.getAndIncrement);

ToolLogUtils.i(TAG+ "-------callStart---requestId-----"+mRequestId);

saveEvent(NetworkTraceBean.CALL_START);

saveUrl(call.request.url.toString);

}

@Override

publicvoiddnsStart(@NotNull Call call, @NotNull String domainName){

super.dnsStart(call, domainName);

ToolLogUtils.d(TAG, "dnsStart");

saveEvent(NetworkTraceBean.DNS_START);

}

@Override

publicvoiddnsEnd(@NotNull Call call, @NotNull String domainName, @NotNull List<InetAddress> inetAddressList){

super.dnsEnd(call, domainName, inetAddressList);

ToolLogUtils.d(TAG, "dnsEnd");

saveEvent(NetworkTraceBean.DNS_END);

}

@Override

publicvoidconnectStart(@NotNull Call call, @NotNull InetSocketAddress inetSocketAddress, @NotNull Proxy proxy){

super.connectStart(call, inetSocketAddress, proxy);

ToolLogUtils.d(TAG, "connectStart");

saveEvent(NetworkTraceBean.CONNECT_START);

}

@Override

publicvoidsecureConnectStart(@NotNull Call call){

super.secureConnectStart(call);

ToolLogUtils.d(TAG, "secureConnectStart");

saveEvent(NetworkTraceBean.SECURE_CONNECT_START);

}

@Override

publicvoidsecureConnectEnd(@NotNull Call call, @Nullable Handshake handshake){

super.secureConnectEnd(call, handshake);

ToolLogUtils.d(TAG, "secureConnectEnd");

saveEvent(NetworkTraceBean.SECURE_CONNECT_END);

}

@Override

publicvoidconnectEnd(@NotNull Call call, @NotNull InetSocketAddress inetSocketAddress,

@NotNull Proxy proxy, @Nullable Protocol protocol) {

super.connectEnd(call, inetSocketAddress, proxy, protocol);

ToolLogUtils.d(TAG, "connectEnd");

saveEvent(NetworkTraceBean.CONNECT_END);

}

@Override

publicvoidconnectFailed(@NotNull Call call, @NotNull InetSocketAddress inetSocketAddress, @NotNull Proxy proxy, @Nullable Protocol protocol, @NotNull IOException ioe){

super.connectFailed(call, inetSocketAddress, proxy, protocol, ioe);

ToolLogUtils.d(TAG, "connectFailed");

}

@Override

publicvoidrequestHeadersStart(@NotNull Call call){

super.requestHeadersStart(call);

ToolLogUtils.d(TAG, "requestHeadersStart");

saveEvent(NetworkTraceBean.REQUEST_HEADERS_START);

}

@Override

publicvoidrequestHeadersEnd(@NotNull Call call, @NotNull Request request){

super.requestHeadersEnd(call, request);

ToolLogUtils.d(TAG, "requestHeadersEnd");

saveEvent(NetworkTraceBean.REQUEST_HEADERS_END);

}

@Override

publicvoidrequestBodyStart(@NotNull Call call){

super.requestBodyStart(call);

ToolLogUtils.d(TAG, "requestBodyStart");

saveEvent(NetworkTraceBean.REQUEST_BODY_START);

}

@Override

publicvoidrequestBodyEnd(@NotNull Call call, longbyteCount) {

super.requestBodyEnd(call, byteCount);

ToolLogUtils.d(TAG, "requestBodyEnd");

saveEvent(NetworkTraceBean.REQUEST_BODY_END);

}

@Override

publicvoidresponseHeadersStart(@NotNull Call call){

super.responseHeadersStart(call);

ToolLogUtils.d(TAG, "responseHeadersStart");

saveEvent(NetworkTraceBean.RESPONSE_HEADERS_START);

}

@Override

publicvoidresponseHeadersEnd(@NotNull Call call, @NotNull Response response){

super.responseHeadersEnd(call, response);

ToolLogUtils.d(TAG, "responseHeadersEnd");

saveEvent(NetworkTraceBean.RESPONSE_HEADERS_END);

}

@Override

publicvoidresponseBodyStart(@NotNull Call call){

super.responseBodyStart(call);

ToolLogUtils.d(TAG, "responseBodyStart");

saveEvent(NetworkTraceBean.RESPONSE_BODY_START);

}

@Override

publicvoidresponseBodyEnd(@NotNull Call call, longbyteCount) {

super.responseBodyEnd(call, byteCount);

ToolLogUtils.d(TAG, "responseBodyEnd");

saveEvent(NetworkTraceBean.RESPONSE_BODY_END);

}

@Override

publicvoidcallEnd(@NotNull Call call){

super.callEnd(call);

ToolLogUtils.d(TAG, "callEnd");

saveEvent(NetworkTraceBean.CALL_END);

generateTraceData;

NetWorkUtils.timeoutChecker(mRequestId);

}

@Override

publicvoidcallFailed(@NotNull Call call, @NotNull IOException ioe){

super.callFailed(call, ioe);

ToolLogUtils.d(TAG, "callFailed");

}

privatevoidgenerateTraceData{

NetworkTraceBean traceModel = IDataPoolHandleImpl.getInstance.getNetworkTraceModel(mRequestId);

Map<String, Long> eventsTimeMap = traceModel.getNetworkEventsMap;

Map<String, Long> traceList = traceModel.getTraceItemList;

traceList.put(NetworkTraceBean.TRACE_NAME_TOTAL,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.CALL_START, NetworkTraceBean.CALL_END));

traceList.put(NetworkTraceBean.TRACE_NAME_DNS,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.DNS_START, NetworkTraceBean.DNS_END));

traceList.put(NetworkTraceBean.TRACE_NAME_SECURE_CONNECT,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.SECURE_CONNECT_START, NetworkTraceBean.SECURE_CONNECT_END));

traceList.put(NetworkTraceBean.TRACE_NAME_CONNECT,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.CONNECT_START, NetworkTraceBean.CONNECT_END));

traceList.put(NetworkTraceBean.TRACE_NAME_REQUEST_HEADERS,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.REQUEST_HEADERS_START, NetworkTraceBean.REQUEST_HEADERS_END));

traceList.put(NetworkTraceBean.TRACE_NAME_REQUEST_BODY,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.REQUEST_BODY_START, NetworkTraceBean.REQUEST_BODY_END));

traceList.put(NetworkTraceBean.TRACE_NAME_RESPONSE_HEADERS,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.RESPONSE_HEADERS_START, NetworkTraceBean.RESPONSE_HEADERS_END));

traceList.put(NetworkTraceBean.TRACE_NAME_RESPONSE_BODY,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.RESPONSE_BODY_START, NetworkTraceBean.RESPONSE_BODY_END));

}

privatevoidsaveEvent(String eventName){

NetworkTraceBean networkTraceModel = IDataPoolHandleImpl.getInstance.getNetworkTraceModel(mRequestId);

Map<String, Long> networkEventsMap = networkTraceModel.getNetworkEventsMap;

networkEventsMap.put(eventName, SystemClock.elapsedRealtime);

}

privatevoidsaveUrl(String url){

NetworkTraceBean networkTraceModel = IDataPoolHandleImpl.getInstance.getNetworkTraceModel(mRequestId);

networkTraceModel.setUrl(url);

}

}

关于执行顺序,打印结果如下所示:

2020- 09- 2220: 50: 15.35128144- 28277/cn.com.zwwl.bayuwen D/NetworkEventListener: dnsStart

2020- 09- 2220: 50: 15.37328144- 28277/cn.com.zwwl.bayuwen D/NetworkEventListener: dnsEnd

2020- 09- 2220: 50: 15.37428144- 28277/cn.com.zwwl.bayuwen D/NetworkEventListener: connectStart

2020- 09- 2220: 50: 15.40428144- 28277/cn.com.zwwl.bayuwen D/NetworkEventListener: secureConnectStart

2020- 09- 2220: 50: 15.49028144- 28277/cn.com.zwwl.bayuwen D/NetworkEventListener: secureConnectEnd

2020- 09- 2220: 50: 15.49028144- 28277/cn.com.zwwl.bayuwen D/NetworkEventListener: connectEnd

2020- 09- 2220: 50: 15.49228144- 28277/cn.com.zwwl.bayuwen D/NetworkEventListener: requestHeadersStart

2020- 09- 2220: 50: 15.49228144- 28277/cn.com.zwwl.bayuwen D/NetworkEventListener: requestHeadersEnd

2020- 09- 2220: 50: 15.52828144- 28277/cn.com.zwwl.bayuwen D/NetworkEventListener: responseHeadersStart

2020- 09- 2220: 50: 15.52828144- 28277/cn.com.zwwl.bayuwen D/NetworkEventListener: responseHeadersEnd

2020- 09- 2220: 50: 15.53228144- 28277/cn.com.zwwl.bayuwen D/NetworkEventListener: responseBodyStart

2020- 09- 2220: 50: 15.53428144- 28277/cn.com.zwwl.bayuwen D/NetworkEventListener: responseBodyEnd

2020- 09- 2220: 50: 15.54728144- 28277/cn.com.zwwl.bayuwen D/NetworkEventListener: callEnd

应用实践之案例

如何优雅地知道 OkHttp 的请求时间  第1张

如何优雅地知道 OkHttp 的请求时间  第2张

如何优雅地知道 OkHttp 的请求时间  第3张

网络拦截分析,主要是分析网络流量损耗,以及request,respond过程时间。打造网络分析工具……如果你觉得这个拦截网络助手方便了测试,以及开发中查看网络数据,可以star一下……

网络拦截库地址如下所示:

https://github.com/yangchong211/YCAndroidTool

https://github.com/yangchong211/YCAndroidTool

本文地址:https://lkwed.com/post/5645.html
版权声明:本文为原创文章,版权归 天顺注册招商 所有,欢迎分享本文,转载请保留出处!

 发表评论


表情

还没有留言,还不快点抢沙发?