美文网首页
OkHttp 之 网络请求耗时统计

OkHttp 之 网络请求耗时统计

作者: Ray_8080 | 来源:发表于2019-07-09 16:02 被阅读0次

    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在两种情况下被调用
    第一种是在请求执行的过程中发生异常时。
    第二种是在请求结束后,关闭输入流时产生异常时。

    相关文章

      网友评论

          本文标题:OkHttp 之 网络请求耗时统计

          本文链接:https://www.haomeiwen.com/subject/pqaxkctx.html