目录介绍
- 01.先提问一个问题
- 02.EventListener回调原理
- 03.请求开始结束监听
- 04.dns解析开始结束监听
- 05.连接开始结束监听
- 06.TLS连接开始结束监听
- 07.连接绑定和释放监听
- 08.request请求监听
- 09.response响应监听
- 10.如何监听统计耗时
- 11.应用实践之案例
01.先提问一个问题
- OkHttp如何进行各个请求环节的耗时统计呢?
- OkHttp 版本提供了EventListener接口,可以让调用者接收一系列网络请求过程中的事件,例如DNS解析、TSL/SSL连接、Response接收等。
- 通过继承此接口,调用者可以监视整个应用中网络请求次数、流量大小、耗时(比如dns解析时间,请求时间,响应时间等等)情况。
02.EventListener回调原理
-
先来看一下
1public abstract class EventListener { 2 // 按照请求顺序回调 3 public void callStart(Call call) {} 4 // 域名解析 5 public void dnsStart(Call call, String domainName) {} 6 public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {} 7 // 释放当前Transmitter的RealConnection 8 public void connectionReleased(Call call, Connection connection) {} 9 public void connectionAcquired(call, result){}; 10 // 开始连接 11 public void connectStart(call, route.socketAddress(), proxy){} 12 // 请求 13 public void requestHeadersStart(@NotNull Call call){} 14 public void requestHeadersEnd(@NotNull Call call, @NotNull Request request) {} 15 // 响应 16 public void requestBodyStart(@NotNull Call call) {} 17 public void requestBodyEnd(@NotNull Call call, long byteCount) {} 18 // 结束 19 public void callEnd(Call call) {} 20 // 失败 21 public void callFailed(Call call, IOException ioe) {} 22}
03.请求开始结束监听
-
callStart(Call call) 请求开始
-
当一个Call(代表一个请求)被同步执行或被添加异步队列中时,即会调用这个回调方法。
-
需要说明这个方法是在dispatcher.executed/enqueue前执行的。
-
由于线程或事件流的限制,这里的请求开始并不是真正的去执行的这个请求。如果发生重定向和多域名重试时,这个方法也仅被调用一次。
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;
}1@Override 2public void enqueue(Callback responseCallback) { 3 eventListener.callStart(this); 4 client.dispatcher().enqueue(new AsyncCall(responseCallback)); 5}}
-
-
callFailed/callEnd 请求异常和请求结束
-
每一个callStart都对应着一个callFailed或callEnd。
-
callFailed在两种情况下被调用,第一种是在请求执行的过程中发生异常时。第二种是在请求结束后,关闭输入流时产生异常时。
final class RealCall implements Call { @Override public Response execute() throws IOException { try { client.dispatcher().executed(this); Response result = getResponseWithInterceptorChain(); if (result == null) throw new IOException("Canceled"); return result; } catch (IOException e) { eventListener.callFailed(this, e); throw e; } } final class AsyncCall extends NamedRunnable { @Override protected void execute() { try { Response response = getResponseWithInterceptorChain(); } catch (IOException e) { eventListener.callFailed(RealCall.this, e);
1 } 2 } 3}}
//第二种 public final class StreamAllocation { public void streamFinished(boolean noNewStreams, HttpCodec codec, long bytesRead, IOException e) { ... if (e != null) { eventListener.callFailed(call, e); } else if (callEnd) { eventListener.callEnd(call); } ... } }
-
callEnd也有两种调用场景。第一种也是在关闭流时。第二种是在释放连接时。
public final class StreamAllocation {
1public void streamFinished(boolean noNewStreams, HttpCodec codec, long bytesRead, IOException e) { 2 ... 3 if (e != null) { 4 eventListener.callFailed(call, e); 5 } else if (callEnd) { 6 eventListener.callEnd(call); 7 } 8 ... 9} 10 11public void release() { 12 ... 13 if (releasedConnection != null) { 14 eventListener.connectionReleased(call, releasedConnection); 15 eventListener.callEnd(call); 16 } 17}}
-
为什么会将关闭流和关闭连接区分开?
- 在http2版本中,一个连接上允许打开多个流,OkHttp使用StreamAllocation来作为流和连接的桥梁。当一个流被关闭时,要检查这条连接上还有没有其他流,如果没有其他流了,则可以将连接关闭了。
- streamFinished和release作用是一样的,都是关闭当前流,并检查是否需要关闭连接。不同的是,当调用者手动取消请求时,调用的是release方法,并由调用者负责关闭请求输出流和响应输入流。
-
04.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. */ private void resetNextInetSocketAddress(Proxy proxy) throws IOException { if (proxy.type() == Proxy.Type.SOCKS) { inetSocketAddresses.add(InetSocketAddress.createUnresolved(socketHost, socketPort)); } else { eventListener.dnsStart(call, socketHost);
1 // Try each address for best behavior in mixed IPv4/IPv6 environments. 2 List<InetAddress> addresses = address.dns().lookup(socketHost); 3 if (addresses.isEmpty()) { 4 throw new UnknownHostException(address.dns() + " returned no addresses for " + socketHost); 5 } 6 7 eventListener.dnsEnd(call, socketHost, addresses); 8}}
-
-
那么RouteSelector这个类是在哪里调用
1public final class StreamAllocation { 2 3 public StreamAllocation(ConnectionPool connectionPool, Address address, Call call, 4 EventListener eventListener, Object callStackTrace) { 5 this.routeSelector = new RouteSelector(address, routeDatabase(), call, eventListener); 6 } 7}
05.连接开始结束监听
-
connectStart连接开始
-
OkHttp是使用Socket接口建立Tcp连接的,所以这里的连接就是指Socket建立一个连接的过程。
-
当连接被重用时,connectStart/connectEnd不会被调用。当请求被重定向到新的域名后,connectStart/connectEnd会被调用多次。
private void connectSocket(int connectTimeout, int readTimeout, Call call, EventListener eventListener) throws IOException { Proxy proxy = route.proxy(); Address address = route.address();
1rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP 2 ? address.socketFactory().createSocket() 3 : new Socket(proxy); 4 5eventListener.connectStart(call, route.socketAddress(), proxy);}
-
-
connectEnd连接结束
-
因为创建的连接有两种类型(服务端直连和隧道代理),所以callEnd有两处调用位置。为了在基于代理的连接上使用SSL,需要单独发送CONECT请求。
-
在连接过程中,无论是Socket连接失败,还是TSL/SSL握手失败,都会回调connectEnd。
public void connect(int connectTimeout, int readTimeout, int writeTimeout, 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); } }
private void connectTunnel(int connectTimeout, int readTimeout, int writeTimeout, Call call, EventListener eventListener) throws IOException { Request tunnelRequest = createTunnelRequest(); HttpUrl url = tunnelRequest.url(); for (int i = 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会被调用多次。
private void establishProtocol(ConnectionSpecSelector connectionSpecSelector, int pingIntervalMillis, Call call, EventListener eventListener) throws IOException { if (route.address().sslSocketFactory() == null) { protocol = Protocol.HTTP_1_1; socket = rawSocket; return; }
1eventListener.secureConnectStart(call); 2connectTls(connectionSpecSelector); 3eventListener.secureConnectEnd(call, handshake);}
-
-
结合连接监听可知
- 如果我们使用了HTTPS安全连接,在TCP连接成功后需要进行TLS安全协议通信,等TLS通讯结束后才能算是整个连接过程的结束,也就是说connectEnd在secureConnectEnd之后调用。
-
所以顺序是这样的
- connectStart ---> secureConnectStart ---> secureConnectEnd ---> ConnectEnd
07.连接绑定和释放监听
-
因为OkHttp是基于连接复用的,当一次请求结束后并不会马上关闭当前连接,而是放到连接池中。
-
当有相同域名的请求时,会从连接池中取出对应的连接使用,减少了连接的频繁创建和销毁。
-
当根据一个请求从连接池取连接时,并打开输入输出流就是acquired,用完释放流就是released。
-
如果直接复用StreamAllocation中的连接,则不会调用connectionAcquired/connectReleased。
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException { 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); } }
1if (releasedConnection != null) { 2 eventListener.connectionReleased(call, releasedConnection); 3} 4if (foundPooledConnection) { 5 eventListener.connectionAcquired(call, result); 6} 7 8synchronized (connectionPool) { 9 if (canceled) throw new IOException("Canceled"); 10 11 if (newRouteSelection) { 12 //第二次查缓存 13 List<Route> routes = routeSelection.getAll(); 14 for (int i = 0, size = routes.size(); i < size; i++) { 15 Route route = routes.get(i); 16 Internal.instance.get(connectionPool, address, this, route); 17 if (connection != null) { 18 foundPooledConnection = true; 19 result = connection; 20 this.route = route; 21 break; 22 } 23 } 24 } 25 26 if (!foundPooledConnection) { 27 //如果缓存没有,则新建连接 28 route = selectedRoute; 29 refusedStreamCount = 0; 30 result = new RealConnection(connectionPool, selectedRoute); 31 acquire(result, false); 32 } 33} 34 35// If we found a pooled connection on the 2nd time around, we're done. 36if (foundPooledConnection) { 37 eventListener.connectionAcquired(call, result); 38 return result; 39} 40 41// Do TCP + TLS handshakes. This is a blocking operation. 42result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis, 43 connectionRetryEnabled, call, eventListener); 44routeDatabase().connected(result.route()); 45 46eventListener.connectionAcquired(call, result); 47return result;}
-
-
connectionAcquired是在连接成功后被调用的。
- 但是在连接复用的情况下没有连接步骤,connectAcquired会在获取缓存连接后被调用。由于StreamAllocation是连接“Stream”和“Connection”的桥梁,所以在StreamAllocation中会持有一个RealConnection引用。StreamAllocation在查找可用连接的顺序为:StreamAllocation.RealConnection -> ConnectionPool -> ConnectionPool -> new RealConnection
08.request请求监听
-
在OkHttp中,HttpCodec负责对请求和响应按照Http协议进行编解码,包含发送请求头、发送请求体、读取响应头、读取响应体。
-
requestHeaders开始和结束,这个直接看CallServerInterceptor拦截器代码即可。
1public final class CallServerInterceptor implements Interceptor { 2 3 @Override public Response intercept(Chain chain) throws IOException { 4 RealInterceptorChain realChain = (RealInterceptorChain) chain; 5 HttpCodec httpCodec = realChain.httpStream(); 6 StreamAllocation streamAllocation = realChain.streamAllocation(); 7 RealConnection connection = (RealConnection) realChain.connection(); 8 Request request = realChain.request(); 9 10 long sentRequestMillis = System.currentTimeMillis(); 11 12 realChain.eventListener().requestHeadersStart(realChain.call()); 13 httpCodec.writeRequestHeaders(request); 14 realChain.eventListener().requestHeadersEnd(realChain.call(), request); 15 16 if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) { 17 if (responseBuilder == null) { 18 // Write the request body if the "Expect: 100-continue" expectation was met. 19 realChain.eventListener().requestBodyStart(realChain.call()); 20 long contentLength = request.body().contentLength(); 21 CountingSink requestBodyOut = 22 new CountingSink(httpCodec.createRequestBody(request, contentLength)); 23 BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut); 24 25 request.body().writeTo(bufferedRequestBody); 26 bufferedRequestBody.close(); 27 realChain.eventListener().requestBodyEnd(realChain.call(), requestBodyOut.successfulCount); 28 } 29 } 30 return response; 31 } 32}
09.response响应监听
-
responseHeadersStart和responseHeadersEnd代码如下所示
1public final class CallServerInterceptor implements Interceptor { 2 3 @Override public Response intercept(Chain chain) throws IOException { 4 5 Response.Builder responseBuilder = null; 6 if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) { 7 if ("100-continue".equalsIgnoreCase(request.header("Expect"))) { 8 httpCodec.flushRequest(); 9 realChain.eventListener().responseHeadersStart(realChain.call()); 10 responseBuilder = httpCodec.readResponseHeaders(true); 11 } 12 } 13 14 httpCodec.finishRequest(); 15 16 if (responseBuilder == null) { 17 realChain.eventListener().responseHeadersStart(realChain.call()); 18 responseBuilder = httpCodec.readResponseHeaders(false); 19 } 20 21 int code = response.code(); 22 if (code == 100) { 23 // server sent a 100-continue even though we did not request one. 24 // try again to read the actual response 25 responseBuilder = httpCodec.readResponseHeaders(false); 26 27 response = responseBuilder 28 .request(request) 29 .handshake(streamAllocation.connection().handshake()) 30 .sentRequestAtMillis(sentRequestMillis) 31 .receivedResponseAtMillis(System.currentTimeMillis()) 32 .build(); 33 34 code = response.code(); 35 } 36 37 realChain.eventListener() .responseHeadersEnd(realChain.call(), response); 38 return response; 39 } 40} -
responseBodyStart监听
-
响应体的读取有些复杂,要根据不同类型的Content-Type决定如何读取响应体,例如固定长度的、基于块(chunk)数据的、未知长度的。具体看openResponseBody方法里面的代码。
-
同时Http1与Http2也有不同的解析方式。下面以Http1为例。
public final class Http1Codec implements HttpCodec {
@Override public ResponseBody openResponseBody(Response response) throws IOException { streamAllocation.eventListener.responseBodyStart(streamAllocation.call); String contentType = response.header("Content-Type");
1if (!HttpHeaders.hasBody(response)) { 2 Source source = newFixedLengthSource(0); 3 return new RealResponseBody(contentType, 0, Okio.buffer(source)); 4} 5 6if ("chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) { 7 Source source = newChunkedSource(response.request().url()); 8 return new RealResponseBody(contentType, -1L, Okio.buffer(source)); 9} 10 11long contentLength = HttpHeaders.contentLength(response); 12if (contentLength != -1) { 13 Source source = newFixedLengthSource(contentLength); 14 return new RealResponseBody(contentType, contentLength, Okio.buffer(source)); 15} 16 17return new RealResponseBody(contentType, -1L, Okio.buffer(newUnknownLengthSource()));} }
-
-
responseBodyEnd监听
-
由下面代码可知,当响应结束后,会调用连接callEnd回调(如果异常则会调用callFailed回调)
public final class StreamAllocation { public void streamFinished(boolean noNewStreams, HttpCodec codec, long bytesRead, IOException e) { eventListener.responseBodyEnd(call, bytesRead); if (releasedConnection != null) { eventListener.connectionReleased(call, releasedConnection); } if (e != null) { eventListener.callFailed(call, e); } else if (callEnd) { eventListener.callEnd(call); } } }
-
10.如何监听统计耗时
-
如何消耗记录时间
- 在OkHttp库中有一个EventListener类。该类是网络事件的侦听器。扩展这个类以监视应用程序的HTTP调用的数量、大小和持续时间。
- 所有启动/连接/获取事件最终将接收到匹配的结束/释放事件,要么成功(非空参数),要么失败(非空可抛出)。
- 比如,可以在开始链接记录时间;dns开始,结束等方法解析记录时间,可以计算dns的解析时间。
- 比如,可以在开始请求记录时间,记录connectStart,connectEnd等方法时间,则可以计算出connect连接时间。
-
代码如下所示
-
Eventlistener只适用于没有并发的情况,如果有多个请求并发执行我们需要使用Eventlistener. Factory来给每个请求创建一个Eventlistener。
-
这个mRequestId是唯一值,可以选择使用AtomicInteger自增+1的方式设置id,这个使用了cas保证多线程条件下的原子性特性。
/**
- <pre>
-
@author yangchong -
email : yangchong211@163.com -
time : 2019/07/22 -
desc : EventListener子类 -
revise: - </pre>
*/ public class NetworkListener extends EventListener {
1private static final String TAG = "NetworkEventListener"; 2private static AtomicInteger mNextRequestId = new AtomicInteger(0); 3private String mRequestId ; 4 5public static Factory get(){ 6 Factory factory = new Factory() { 7 @NotNull 8 @Override 9 public EventListener create(@NotNull Call call) { 10 return new NetworkListener(); 11 } 12 }; 13 return factory; 14} 15 16@Override 17public void callStart(@NotNull Call call) { 18 super.callStart(call); 19 //mRequestId = mNextRequestId.getAndIncrement() + ""; 20 //getAndAdd,在多线程下使用cas保证原子性 21 mRequestId = String.valueOf(mNextRequestId.getAndIncrement()); 22 ToolLogUtils.i(TAG+"-------callStart---requestId-----"+mRequestId); 23 saveEvent(NetworkTraceBean.CALL_START); 24 saveUrl(call.request().url().toString()); 25} 26 27@Override 28public void dnsStart(@NotNull Call call, @NotNull String domainName) { 29 super.dnsStart(call, domainName); 30 ToolLogUtils.d(TAG, "dnsStart"); 31 saveEvent(NetworkTraceBean.DNS_START); 32} 33 34@Override 35public void dnsEnd(@NotNull Call call, @NotNull String domainName, @NotNull List<InetAddress> inetAddressList) { 36 super.dnsEnd(call, domainName, inetAddressList); 37 ToolLogUtils.d(TAG, "dnsEnd"); 38 saveEvent(NetworkTraceBean.DNS_END); 39} 40 41@Override 42public void connectStart(@NotNull Call call, @NotNull InetSocketAddress inetSocketAddress, @NotNull Proxy proxy) { 43 super.connectStart(call, inetSocketAddress, proxy); 44 ToolLogUtils.d(TAG, "connectStart"); 45 saveEvent(NetworkTraceBean.CONNECT_START); 46} 47 48@Override 49public void secureConnectStart(@NotNull Call call) { 50 super.secureConnectStart(call); 51 ToolLogUtils.d(TAG, "secureConnectStart"); 52 saveEvent(NetworkTraceBean.SECURE_CONNECT_START); 53} 54 55@Override 56public void secureConnectEnd(@NotNull Call call, @Nullable Handshake handshake) { 57 super.secureConnectEnd(call, handshake); 58 ToolLogUtils.d(TAG, "secureConnectEnd"); 59 saveEvent(NetworkTraceBean.SECURE_CONNECT_END); 60} 61 62@Override 63public void connectEnd(@NotNull Call call, @NotNull InetSocketAddress inetSocketAddress, 64 @NotNull Proxy proxy, @Nullable Protocol protocol) { 65 super.connectEnd(call, inetSocketAddress, proxy, protocol); 66 ToolLogUtils.d(TAG, "connectEnd"); 67 saveEvent(NetworkTraceBean.CONNECT_END); 68} 69 70@Override 71public void connectFailed(@NotNull Call call, @NotNull InetSocketAddress inetSocketAddress, @NotNull Proxy proxy, @Nullable Protocol protocol, @NotNull IOException ioe) { 72 super.connectFailed(call, inetSocketAddress, proxy, protocol, ioe); 73 ToolLogUtils.d(TAG, "connectFailed"); 74} 75 76@Override 77public void requestHeadersStart(@NotNull Call call) { 78 super.requestHeadersStart(call); 79 ToolLogUtils.d(TAG, "requestHeadersStart"); 80 saveEvent(NetworkTraceBean.REQUEST_HEADERS_START); 81} 82 83@Override 84public void requestHeadersEnd(@NotNull Call call, @NotNull Request request) { 85 super.requestHeadersEnd(call, request); 86 ToolLogUtils.d(TAG, "requestHeadersEnd"); 87 saveEvent(NetworkTraceBean.REQUEST_HEADERS_END); 88} 89 90@Override 91public void requestBodyStart(@NotNull Call call) { 92 super.requestBodyStart(call); 93 ToolLogUtils.d(TAG, "requestBodyStart"); 94 saveEvent(NetworkTraceBean.REQUEST_BODY_START); 95} 96 97@Override 98public void requestBodyEnd(@NotNull Call call, long byteCount) { 99 super.requestBodyEnd(call, byteCount); 100 ToolLogUtils.d(TAG, "requestBodyEnd"); 101 saveEvent(NetworkTraceBean.REQUEST_BODY_END); 102} 103 104@Override 105public void responseHeadersStart(@NotNull Call call) { 106 super.responseHeadersStart(call); 107 ToolLogUtils.d(TAG, "responseHeadersStart"); 108 saveEvent(NetworkTraceBean.RESPONSE_HEADERS_START); 109} 110 111@Override 112public void responseHeadersEnd(@NotNull Call call, @NotNull Response response) { 113 super.responseHeadersEnd(call, response); 114 ToolLogUtils.d(TAG, "responseHeadersEnd"); 115 saveEvent(NetworkTraceBean.RESPONSE_HEADERS_END); 116} 117 118@Override 119public void responseBodyStart(@NotNull Call call) { 120 super.responseBodyStart(call); 121 ToolLogUtils.d(TAG, "responseBodyStart"); 122 saveEvent(NetworkTraceBean.RESPONSE_BODY_START); 123} 124 125@Override 126public void responseBodyEnd(@NotNull Call call, long byteCount) { 127 super.responseBodyEnd(call, byteCount); 128 ToolLogUtils.d(TAG, "responseBodyEnd"); 129 saveEvent(NetworkTraceBean.RESPONSE_BODY_END); 130} 131 132@Override 133public void callEnd(@NotNull Call call) { 134 super.callEnd(call); 135 ToolLogUtils.d(TAG, "callEnd"); 136 saveEvent(NetworkTraceBean.CALL_END); 137 generateTraceData(); 138 NetWorkUtils.timeoutChecker(mRequestId); 139} 140 141@Override 142public void callFailed(@NotNull Call call, @NotNull IOException ioe) { 143 super.callFailed(call, ioe); 144 ToolLogUtils.d(TAG, "callFailed"); 145} 146 147private void generateTraceData(){ 148 NetworkTraceBean traceModel = IDataPoolHandleImpl.getInstance().getNetworkTraceModel(mRequestId); 149 Map<String, Long> eventsTimeMap = traceModel.getNetworkEventsMap(); 150 Map<String, Long> traceList = traceModel.getTraceItemList(); 151 traceList.put(NetworkTraceBean.TRACE_NAME_TOTAL,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.CALL_START, NetworkTraceBean.CALL_END)); 152 traceList.put(NetworkTraceBean.TRACE_NAME_DNS,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.DNS_START, NetworkTraceBean.DNS_END)); 153 traceList.put(NetworkTraceBean.TRACE_NAME_SECURE_CONNECT,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.SECURE_CONNECT_START, NetworkTraceBean.SECURE_CONNECT_END)); 154 traceList.put(NetworkTraceBean.TRACE_NAME_CONNECT,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.CONNECT_START, NetworkTraceBean.CONNECT_END)); 155 traceList.put(NetworkTraceBean.TRACE_NAME_REQUEST_HEADERS,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.REQUEST_HEADERS_START, NetworkTraceBean.REQUEST_HEADERS_END)); 156 traceList.put(NetworkTraceBean.TRACE_NAME_REQUEST_BODY,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.REQUEST_BODY_START, NetworkTraceBean.REQUEST_BODY_END)); 157 traceList.put(NetworkTraceBean.TRACE_NAME_RESPONSE_HEADERS,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.RESPONSE_HEADERS_START, NetworkTraceBean.RESPONSE_HEADERS_END)); 158 traceList.put(NetworkTraceBean.TRACE_NAME_RESPONSE_BODY,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.RESPONSE_BODY_START, NetworkTraceBean.RESPONSE_BODY_END)); 159} 160 161private void saveEvent(String eventName){ 162 NetworkTraceBean networkTraceModel = IDataPoolHandleImpl.getInstance().getNetworkTraceModel(mRequestId); 163 Map<String, Long> networkEventsMap = networkTraceModel.getNetworkEventsMap(); 164 networkEventsMap.put(eventName, SystemClock.elapsedRealtime()); 165} 166 167private void saveUrl(String url){ 168 NetworkTraceBean networkTraceModel = IDataPoolHandleImpl.getInstance().getNetworkTraceModel(mRequestId); 169 networkTraceModel.setUrl(url); 170}}
-
-
关于执行顺序,打印结果如下所示
12020-09-22 20:50:15.351 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: dnsStart 22020-09-22 20:50:15.373 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: dnsEnd 32020-09-22 20:50:15.374 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: connectStart 42020-09-22 20:50:15.404 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: secureConnectStart 52020-09-22 20:50:15.490 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: secureConnectEnd 62020-09-22 20:50:15.490 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: connectEnd 72020-09-22 20:50:15.492 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: requestHeadersStart 82020-09-22 20:50:15.492 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: requestHeadersEnd 92020-09-22 20:50:15.528 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: responseHeadersStart 102020-09-22 20:50:15.528 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: responseHeadersEnd 112020-09-22 20:50:15.532 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: responseBodyStart 122020-09-22 20:50:15.534 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: responseBodyEnd 132020-09-22 20:50:15.547 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: callEnd
11.应用实践之案例

- 网络拦截分析,主要是分析网络流量损耗,以及request,respond过程时间。打造网络分析工具……
- 项目代码地址:https://github.com/yangchong211/YCAndroidTool
- 如果你觉得这个拦截网络助手方便了测试,以及开发中查看网络数据,可以star一下……