catalogue

  • 01. Ask a question first
  • 02.EventListener Callback principle
  • 03. Request to end listening
  • 04. DNS parsing starts End Listening
  • 05. Connection starts end Listen
  • 06.TLS connection starts and ends
  • 07. Connect bind and release listener
  • 08. Request Request listening
  • Response Response monitoring
  • 10. How do I monitor time statistics
  • 11. Cases of applied practice

01. Ask a question first

  • How does OkHttp count the time spent on each request?
    • The OkHttp version provides an EventListener interface that lets the caller receive a series of events during a network request, such as DNS resolution, TSL/SSL connection, Response reception, and so on.
    • By inheriting this interface, callers can monitor the number of network requests, the volume of traffic, and time (such as DNS resolution time, request time, response time, and so on) throughout the application.

02.EventListener Callback principle

  • Let’s take a look at that
    public abstract class EventListener {
       // Callback in the order requested
        public void callStart(Call call) {}
        // Domain name resolution
        public void dnsStart(Call call, String domainName) {}
        public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {}
        // Release the current Transmitter RealConnection
        public void connectionReleased(Call call, Connection connection) {}
        public void connectionAcquired(call, result){};
        // Start the connection
        public void connectStart(call, route.socketAddress(), proxy){}
        / / request
        public void requestHeadersStart(@NotNull Call call){}
        public void requestHeadersEnd(@NotNull Call call, @NotNull Request request) {}
        / / response
        public void requestBodyStart(@NotNull Call call) {}
        public void requestBodyEnd(@NotNull Call call, long byteCount) {}
        / / end
        public void callEnd(Call call) {}
        / / fail
        public void callFailed(Call call, IOException ioe) {}}Copy the code

03. Request to end listening

  • CallStart (Call Call) The request starts
    • This callback method is called when a Call (representing a request) is executed synchronously or added to an asynchronous queue.
    • To explain this method is in the dispatcher executed/the enqueue before execution.
    • Because of thread or event flow constraints, the request here is not really the request to execute. This method is also called only once if redirects and multiple domain retries occur.
    final class RealCall implements Call {
        @Override 
        public Response execute(a) 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(newAsyncCall(responseCallback)); }}Copy the code
  • CallFailed /callEnd Request exception and request end
    • Each callStart corresponds to a callFailed or callEnd.
    • CallFailed is invoked in two cases, the first when an exception occurs during the execution of the request. The second is when an exception occurs when the input stream is closed after the request ends.
    final class RealCall implements Call {
        @Override 
        public Response execute(a) 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);
              throwe; }}final class AsyncCall extends NamedRunnable {
            @Override 
            protected void execute(a) {
                try {
                    Response response = getResponseWithInterceptorChain();
                } catch (IOException e) {
                    eventListener.callFailed(RealCall.this, e); }}}}/ / the second
    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); }... }}Copy the code
    • CallEnd also has two invocation scenarios. The first is also when closing the stream. The second is when the connection is released.
    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); }... }public void release(a) {...if(releasedConnection ! =null) { eventListener.connectionReleased(call, releasedConnection); eventListener.callEnd(call); }}}Copy the code
    • Why is a closed flow separated from a closed connection?
      • In the HTTP2 version, multiple streams are allowed to open on a connection, and OkHttp uses StreamAllocation as a bridge between streams and connections. When a stream is closed, check to see if there are any other streams on the connection. If there are none, close the connection.
      • StreamFinished and Release work the same way, closing the current stream and checking to see if the connection needs to be closed. The difference is that when the caller manually cancels the request, the release method is called and it is the caller’s responsibility to close the request output stream and response input stream.

04. DNS parsing starts End Listening

  • DnsStart began
    • The lookup(String Hostname) method represents the domain name resolution process, dnsStart/dnsEnd is called before and after the lookup
    • DNS resolution is the process of asking the Domain Name System (DNS) server to resolve a Domain Name into an IP address. Domain name resolution is done by the InetAddress class in the JDK.
      /** 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);
    
          // Try each address for best behavior in mixed IPv4/IPv6 environments.
          List<InetAddress> addresses = address.dns().lookup(socketHost);
          if (addresses.isEmpty()) {
            throw new UnknownHostException(address.dns() + " returned no addresses for "+ socketHost); } eventListener.dnsEnd(call, socketHost, addresses); }}Copy the code
  • So where is the RouteSelector class called
    public final class StreamAllocation {
    
      public StreamAllocation(ConnectionPool connectionPool, Address address, Call call, EventListener eventListener, Object callStackTrace) {
        this.routeSelector = newRouteSelector(address, routeDatabase(), call, eventListener); }}Copy the code

05. Connection starts end Listen

  • ConnectStart The connection starts
    • OkHttp uses the Socket interface to establish a Tcp connection, so the connection refers to the process by which a Socket establishes a connection.
    • When a connection is reused, connectStart/connectEnd is not called. ConnectStart /connectEnd will be called multiple times after the request is redirected to the new domain name.
      private void connectSocket(int connectTimeout, int readTimeout, Call call,
          EventListener eventListener) throws IOException {
        Proxy proxy = route.proxy();
        Address address = route.address();
    
        rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
            ? address.socketFactory().createSocket()
            : new Socket(proxy);
    
        eventListener.connectStart(call, route.socketAddress(), proxy);
      }
    Copy the code
  • ConnectEnd The connection ends
    • Because there are two types of connections created (server direct connection and tunnel proxy), callEnd has two places to call. To use SSL on a proxy-based connection, you need to send a separate CONECT request.
    • During a connection, the connectEnd is called back whether the Socket connection fails or the TSL/SSL handshake fails.
      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); }}Copy the code

06.TLS connection starts and ends

  • Start the connection, as shown below
    • As you saw above, an establishProtocol method is performed after the Socket establishes a connection, which serves as the TSL/SSL handshake.
    • When the presence of redirection or connection retry, secureConnectStart/secureConnectEnd is called many times.
      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;
        }
    
        eventListener.secureConnectStart(call);
        connectTls(connectionSpecSelector);
        eventListener.secureConnectEnd(call, handshake);
      }
    Copy the code
  • You can see this in combination with connection listening
    • If the HTTPS connection is used, the TLS communication is required after the TCP connection is successful. The connection process is complete only after the TLS communication is complete. That is, connectEnd is invoked after secureConnectEnd.
  • So this is the order
    • connectStart —> secureConnectStart —> secureConnectEnd —> ConnectEnd

07. Connect bind and release listener

  • Because OkHttp is based on connection reuse, the current connection is not closed immediately after a request is completed, but is placed into the connection pool.
    • When there is a request for the same domain name, the system removes the corresponding connection from the connection pool, reducing the frequent creation and destruction of connections.
    • When a connection is fetched from the connection pool based on a request and the input/output stream is opened, the input/output stream is acquired and the release stream is released.
    • If the direct reuse of StreamAllocation connection, do not call 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); }}if(releasedConnection ! =null) {
          eventListener.connectionReleased(call, releasedConnection);
        }
        if (foundPooledConnection) {
          eventListener.connectionAcquired(call, result);
        }
    
        synchronized (connectionPool) {
          if (canceled) throw new IOException("Canceled");
    
          if (newRouteSelection) {
            // Check the cache a second time
            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 the cache does not exist, create a new connection
            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.
        result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
            connectionRetryEnabled, call, eventListener);
        routeDatabase().connected(result.route());
    
        eventListener.connectionAcquired(call, result);
        return result;
      }
    Copy the code
  • ConnectionAcquired is called after a successful connection.
    • However, there is no connection step in the case of connection reuse and connectAcquired will be called after obtaining a cached connection. Since StreamAllocation is a bridge between “Stream” and “Connection”, a RealConnection reference is held in StreamAllocation. StreamAllocation in finding available connection order: StreamAllocation. RealConnection – > ConnectionPool – > ConnectionPool – > new RealConnection

08. Request Request listening

  • In OkHttp, HttpCodec is responsible for encoding and decoding requests and responses according to the Http protocol, including sending the request header, sending the request body, reading the response header, and reading the response body.
  • RequestHeaders start and end with the CallServerInterceptor interceptor code.
    public final class CallServerInterceptor implements Interceptor {
    
      @Override public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        HttpCodec httpCodec = realChain.httpStream();
        StreamAllocation streamAllocation = realChain.streamAllocation();
        RealConnection connection = (RealConnection) realChain.connection();
        Request request = realChain.request();
    
        long sentRequestMillis = 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());
            long contentLength = 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; }}Copy the code

Response Response monitoring

  • The responseHeadersStart and responseHeadersEnd codes are shown below
    public final class CallServerInterceptor implements Interceptor {
    
      @Override public Response intercept(Chain chain) throws IOException {
    
        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);
        }
    
        int code = 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; }}Copy the code
  • ResponseBodyStart listening
    • The reading of the response body is somewhat complicated. It depends on different content-Types, such as those with fixed length, those based on chunk data, and those of unknown length. Look at the code inside the openResponseBody method.
    • Also, Http1 and Http2 have different parsing methods. The following uses Http1 as an example.
    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");
    
        if(! HttpHeaders.hasBody(response)) { Source source = newFixedLengthSource(0);
          return new RealResponseBody(contentType, 0, Okio.buffer(source));
        }
    
        if ("chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) {
          Source source = newChunkedSource(response.request().url());
          return new RealResponseBody(contentType, -1L, Okio.buffer(source));
        }
    
        long contentLength = HttpHeaders.contentLength(response);
        if(contentLength ! = -1) {
          Source source = newFixedLengthSource(contentLength);
          return new RealResponseBody(contentType, contentLength, Okio.buffer(source));
        }
    
        return new RealResponseBody(contentType, -1L, Okio.buffer(newUnknownLengthSource())); }}Copy the code
  • ResponseBodyEnd listening
    • As you can see from the following code, when the response ends, the connection callEnd callback is called (or the callFailed callback is called if there is an exception)
    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); }}}Copy the code

10. How do I monitor time statistics

  • How to consume recording time
    • There is an EventListener class in the OkHttp library. This class is a listener for network events. Extend this class to monitor the number, size, and duration of HTTP calls to your application.
    • All start/connect/get events will eventually receive a matching end/release event that either succeeds (non-empty arguments) or fails (non-empty throwable).
    • For example, you can log the time at the start of the link; DNS start, end and other methods to resolve the record time, you can calculate the DNS resolution time.
    • For example, you can calculate the connect connection time by recording the time of the start request, connectStart, connectEnd, etc.
  • The code is shown below
    • Eventlistener only works if there is no concurrency. If multiple requests are executed concurrently, we need to create an Eventlistener for each request using eventListener.factory.
    • The mRequestId is unique and the ID can optionally be set with AtomicInteger incrementing by +1, which uses CAS to ensure atomicity in multithreading conditions.
    /**
     * <pre>
     *     @authorYangchong * Email: [email protected] * time: 2019/07/22 * desc: EventListener subclass * revise: * </pre> */
    public class NetworkListener extends EventListener {
    
        private static final String TAG = "NetworkEventListener";
        private static AtomicInteger mNextRequestId = new AtomicInteger(0);
        private String mRequestId ;
    
        public static Factory get(a){
            Factory factory = new Factory() {
                @NotNull
                @Override
                public EventListener create(@NotNull Call call) {
                    return newNetworkListener(); }};return factory;
        }
    
        @Override
        public void callStart(@NotNull Call call) {
            super.callStart(call);
            //mRequestId = mNextRequestId.getAndIncrement() + "";
            //getAndAdd, use CAS to ensure atomicity in multithreading
            mRequestId = String.valueOf(mNextRequestId.getAndIncrement());
            ToolLogUtils.i(TAG+"-------callStart---requestId-----"+mRequestId);
            saveEvent(NetworkTraceBean.CALL_START);
            saveUrl(call.request().url().toString());
        }
    
        @Override
        public void dnsStart(@NotNull Call call, @NotNull String domainName) {
            super.dnsStart(call, domainName);
            ToolLogUtils.d(TAG, "dnsStart");
            saveEvent(NetworkTraceBean.DNS_START);
        }
    
        @Override
        public void dnsEnd(@NotNull Call call, @NotNull String domainName, @NotNull List<InetAddress> inetAddressList) {
            super.dnsEnd(call, domainName, inetAddressList);
            ToolLogUtils.d(TAG, "dnsEnd");
            saveEvent(NetworkTraceBean.DNS_END);
        }
    
        @Override
        public void connectStart(@NotNull Call call, @NotNull InetSocketAddress inetSocketAddress, @NotNull Proxy proxy) {
            super.connectStart(call, inetSocketAddress, proxy);
            ToolLogUtils.d(TAG, "connectStart");
            saveEvent(NetworkTraceBean.CONNECT_START);
        }
    
        @Override
        public void secureConnectStart(@NotNull Call call) {
            super.secureConnectStart(call);
            ToolLogUtils.d(TAG, "secureConnectStart");
            saveEvent(NetworkTraceBean.SECURE_CONNECT_START);
        }
    
        @Override
        public void secureConnectEnd(@NotNull Call call, @Nullable Handshake handshake) {
            super.secureConnectEnd(call, handshake);
            ToolLogUtils.d(TAG, "secureConnectEnd");
            saveEvent(NetworkTraceBean.SECURE_CONNECT_END);
        }
    
        @Override
        public void connectEnd(@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
        public void connectFailed(@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
        public void requestHeadersStart(@NotNull Call call) {
            super.requestHeadersStart(call);
            ToolLogUtils.d(TAG, "requestHeadersStart");
            saveEvent(NetworkTraceBean.REQUEST_HEADERS_START);
        }
    
        @Override
        public void requestHeadersEnd(@NotNull Call call, @NotNull Request request) {
            super.requestHeadersEnd(call, request);
            ToolLogUtils.d(TAG, "requestHeadersEnd");
            saveEvent(NetworkTraceBean.REQUEST_HEADERS_END);
        }
    
        @Override
        public void requestBodyStart(@NotNull Call call) {
            super.requestBodyStart(call);
            ToolLogUtils.d(TAG, "requestBodyStart");
            saveEvent(NetworkTraceBean.REQUEST_BODY_START);
        }
    
        @Override
        public void requestBodyEnd(@NotNull Call call, long byteCount) {
            super.requestBodyEnd(call, byteCount);
            ToolLogUtils.d(TAG, "requestBodyEnd");
            saveEvent(NetworkTraceBean.REQUEST_BODY_END);
        }
    
        @Override
        public void responseHeadersStart(@NotNull Call call) {
            super.responseHeadersStart(call);
            ToolLogUtils.d(TAG, "responseHeadersStart");
            saveEvent(NetworkTraceBean.RESPONSE_HEADERS_START);
        }
    
        @Override
        public void responseHeadersEnd(@NotNull Call call, @NotNull Response response) {
            super.responseHeadersEnd(call, response);
            ToolLogUtils.d(TAG, "responseHeadersEnd");
            saveEvent(NetworkTraceBean.RESPONSE_HEADERS_END);
        }
    
        @Override
        public void responseBodyStart(@NotNull Call call) {
            super.responseBodyStart(call);
            ToolLogUtils.d(TAG, "responseBodyStart");
            saveEvent(NetworkTraceBean.RESPONSE_BODY_START);
        }
    
        @Override
        public void responseBodyEnd(@NotNull Call call, long byteCount) {
            super.responseBodyEnd(call, byteCount);
            ToolLogUtils.d(TAG, "responseBodyEnd");
            saveEvent(NetworkTraceBean.RESPONSE_BODY_END);
        }
    
        @Override
        public void callEnd(@NotNull Call call) {
            super.callEnd(call);
            ToolLogUtils.d(TAG, "callEnd");
            saveEvent(NetworkTraceBean.CALL_END);
            generateTraceData();
            NetWorkUtils.timeoutChecker(mRequestId);
        }
    
        @Override
        public void callFailed(@NotNull Call call, @NotNull IOException ioe) {
            super.callFailed(call, ioe);
            ToolLogUtils.d(TAG, "callFailed");
        }
    
        private void generateTraceData(a){ 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.SE CURE_CONNECT_START, NetworkTraceBean.SECURE_CONNECT_END)); traceList.put(NetworkTraceBean.TRACE_NAME_CONNECT,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.CONNECT_S TART, NetworkTraceBean.CONNECT_END)); traceList.put(NetworkTraceBean.TRACE_NAME_REQUEST_HEADERS,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.R EQUEST_HEADERS_START, NetworkTraceBean.REQUEST_HEADERS_END)); traceList.put(NetworkTraceBean.TRACE_NAME_REQUEST_BODY,NetWorkUtils.getEventCostTime(eventsTimeMap,NetworkTraceBean.REQU EST_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.RES PONSE_BODY_START, NetworkTraceBean.RESPONSE_BODY_END)); }private void saveEvent(String eventName){
            NetworkTraceBean networkTraceModel = IDataPoolHandleImpl.getInstance().getNetworkTraceModel(mRequestId);
            Map<String, Long> networkEventsMap = networkTraceModel.getNetworkEventsMap();
            networkEventsMap.put(eventName, SystemClock.elapsedRealtime());
        }
    
        private void saveUrl(String url){ NetworkTraceBean networkTraceModel = IDataPoolHandleImpl.getInstance().getNetworkTraceModel(mRequestId); networkTraceModel.setUrl(url); }}Copy the code
  • For the order of execution, the print is as follows
    2020- 09 -22 20:50:15.351 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: dnsStart
    2020- 09 -22 20:50:15.373 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: dnsEnd
    2020- 09 -22 20:50:15.374 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: connectStart
    2020- 09 -22 20:50:15.404 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: secureConnectStart
    2020- 09 -22 20:50:15.490 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: secureConnectEnd
    2020- 09 -22 20:50:15.490 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: connectEnd
    2020- 09 -22 20:50:15.492 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: requestHeadersStart
    2020- 09 -22 20:50:15.492 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: requestHeadersEnd
    2020- 09 -22 20:50:15.528 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: responseHeadersStart
    2020- 09 -22 20:50:15.528 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: responseHeadersEnd
    2020- 09 -22 20:50:15.532 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: responseBodyStart
    2020- 09 -22 20:50:15.534 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: responseBodyEnd
    2020- 09 -22 20:50:15.547 28144-28277/cn.com.zwwl.bayuwen D/NetworkEventListener: callEnd
    Copy the code

11. Cases of applied practice

  • Network intercept analysis analyzes network traffic loss and request and respond process time. Build web analytics tools…
  • Project code address: github.com/yangchong21…
  • If you find the Blocking Network assistant convenient for testing and viewing network data during development, you can star…

Network Interception library:Github.com/yangchong21…