OkHttp使用及流程分析

  1. get & post
    1.1 組裝Get or Post請求
    Request requestGet = new Request.Builder().url(url).build();
    // 其他request可選項
    //.tag(tag)
    //.header(key, value)
    //.cacheControl(CacheControl.FORCE_NETWORK)
    //.method(METHOD_GET, null)

    Request requestPost = new Request.Builder().url(url).post(requestBody).build();
    RequestBody:
        FormBody formBody = new FormBody.Builder().add(key, value).build(); // 提交表單,使用默認MediaType application/x-www-form-urlencoded
        RequestBody fileBody = RequestBody.create(MediaType.parse(""), file); // 提交文件(上傳文件)
        RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json.toString); // 提交Json
        MultipartBody.Builder.setType(MultipartBody.FORM).addPart(key, value).addPart(fileBody).build(); // 提交表單和文件
    

    1.2 發送請求
    Call call = new OkHttpClient.Builder().build().newCall(request);
    非Async方式:
    Response response = call.execute();
    Async方式:
    call.enqueue(new Callback() {
    onFailure(Call call, IOException e){非UI線程}
    onResponse(Call call, Response response){非UI線程}
    });

  2. 下載文件 & 圖片顯示
    同1中的使用方式,組裝請求後發送,拿到Response或者在Callback中對Response做進一步處理。
    ResponseBody body = Response.body();
    InputStream is = body.byteStream(); // 獲取response的InputStream流,這個用於直接read然後保存在文件(下載)或者Bitmap中。
    byte[] bytes = body.bytes(); // 獲取response的byte數據
    Reader reader = body.charStream();
    String string = body.string();
  3. 取消
    Call call; call.cancel();
    對於同一個OkHttpClient中的Call,可以在dispatcher中進行遍歷查詢。
    for (Call call: client.dispatcher().queuedCalls()) {
    if (call.request().tag() == TAG) {} // 根據tag判斷
    if (call.request().url() == URL){} // 根據url判斷
    if (call.request().header(key) == VALUE){} // 根據header判斷
    }
    for (Call call: client.dispatcher().runningCalls())

    client.dispatcher().cancelAll(); // 取消全部

  4. OkHttpClient的設置(OkHttpClient.Builder)
    public Builder() {
    dispatcher = new Dispatcher();
    protocols = DEFAULT_PROTOCOLS;
    connectionSpecs = DEFAULT_CONNECTION_SPECS;
    eventListenerFactory = EventListener.factory(EventListener.NONE);
    proxySelector = ProxySelector.getDefault();
    cookieJar = CookieJar.NO_COOKIES;
    socketFactory = SocketFactory.getDefault();
    hostnameVerifier = OkHostnameVerifier.INSTANCE;
    certificatePinner = CertificatePinner.DEFAULT;
    proxyAuthenticator = Authenticator.NONE;
    authenticator = Authenticator.NONE;
    connectionPool = new ConnectionPool();
    dns = Dns.SYSTEM;
    followSslRedirects = true;
    followRedirects = true;
    retryOnConnectionFailure = true;
    connectTimeout = 10_000;
    readTimeout = 10_000;
    writeTimeout = 10_000;
    pingInterval = 0;
    }

    @Nullable Proxy proxy;
    final List interceptors = new ArrayList<>();
    final List networkInterceptors = new ArrayList<>();
    @Nullable Cache cache;
    @Nullable InternalCache internalCache;
    @Nullable SSLSocketFactory sslSocketFactory;
    @Nullable CertificateChainCleaner certificateChainCleaner;

  5. Call的執行流程
    5.1 RealCall
    創建RealCall並執行RealCall中的execute或enqueue方法
    class RealCall {
    final OkHttpClient client;
    final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;

        Response execute() {
            try {
                client.dispatcher().executed(this);  // dispatcher.runningSyncCalls.add(call); ArrayDeque來保存runningCalls
                Response result = getResponseWithInterceptorChain();
                if (result == null) throw new IOException("Canceled");
                    return result;
            } finally {
                client.dispatcher().finished(this);
            }
        }
    
        void enqueue(Callback callback) {
            client.dispatcher().enqueue(new AsyncCall(responseCallback)); // dispatcher.runningAsyncCalls.add(call); ArrayDeque來保存runningCalls。 
                                                                            //dispatcher中使用ExecutorService維護了一個線程池,來執行Runnable
        }
    
        class AsyncCall extends NamedRunnable {
            void execute() {
                Response response = getResponseWithInterceptorChain();
                if (retryAndFollowUpInterceptor.isCanceled()) {
                    responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
                } else {
                    responseCallback.onResponse(RealCall.this, response);
                }
            }
        }
    
        Response getResponseWithInterceptorChain() {
            List<Interceptor> interceptors = new ArrayList<>();
            interceptors.addAll(client.interceptors());
            interceptors.add(retryAndFollowUpInterceptor);
            interceptors.add(new BridgeInterceptor(client.cookieJar()));
            interceptors.add(new CacheInterceptor(client.internalCache()));
            interceptors.add(new ConnectInterceptor(client));
            if (!forWebSocket) {
                interceptors.addAll(client.networkInterceptors());
            }
            interceptors.add(new CallServerInterceptor(forWebSocket));
    
            Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);
            return chain.proceed(originalRequest);
        }
    }
    

    5.2 RealInterceptorChain & Interceptor
    ////index = 0
    Response proceed(Request) {
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
    }

    (client.interceptors)
    ////index = 1
    // This interceptor recovers from failures and follows redirects as necessary.負責失敗重試及重定向
    RetryAndFollowUpInterceptor.intercept(Chain chain) {  
        Response priorResponse = null;
        while (true) {
            // 調用Chain的proceed,調用下一個Interceptor
            response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null); // RouteException, IOException 等不再重試,throw exceptions
    
            if (priorResponse != null) { // Attach the prior response if it exists. Such responses never have a body.
                response = response.newBuilder()
                    .priorResponse(priorResponse.newBuilder()
                        .body(null)
                        .build())
                    .build();
            }
    
            Request followUp = followUpRequest(response); // 拿到新的Request(重定向或重試)
    
            if (followUp == null) { // 沒有新的request了,返回response
                return response;
            }
    
            if (!sameConnection(response, followUp.url())) { // 如果重定向了,使用新的StreamAllocation
                streamAllocation.release();
                streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(followUp.url()), callStackTrace);
            }
    
            request = followUp;
            priorResponse = response;  // 保存此次的Response爲prior, 保存followUp作爲新的request,進入循環
        }
    
    }
    
    ////index = 2
    // Bridges from application code to network code. First it builds a network request from a user
    // request. Then it proceeds to call the network. Finally it builds a user response from the network
    // response. 
    // 負責把用戶構造的請求轉換爲發送到服務器的請求,把服務器端返回的響應轉換爲用戶友好的響應。
    BridgeInterceptor.intercept(Chain chain) {
        // request轉換
        Request userRequest = chain.request();
        Request.Builder requestBuilder = userRequest.newBuilder();
    
        RequestBody body = userRequest.body();
    
        // add or remove Header(根據原request參數判斷是否增加)
        requestBuilder.header("Content-Type", contentType.toString());
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
        requestBuilder.header("Host", hostHeader(userRequest.url(), false));
        requestBuilder.header("Connection", "Keep-Alive");
        requestBuilder.header("Accept-Encoding", "gzip"); // 
        requestBuilder.header("Cookie", cookieHeader(cookies));
        requestBuilder.header("User-Agent", Version.userAgent());
    
        // 轉換request完畢,使用新的request繼續執行
        Response networkResponse = chain.proceed(requestBuilder.build());
    
        // response轉換
        HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers()); // 更新cookie
    
        Response.Builder responseBuilder = networkResponse.newBuilder().request(userRequest);
    
        if (transparentGzip
            && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
            && HttpHeaders.hasBody(networkResponse)) {
            GzipSource responseBody = new GzipSource(networkResponse.body().source());
            Headers strippedHeaders = networkResponse.headers().newBuilder()
                .removeAll("Content-Encoding")
                .removeAll("Content-Length")
                .build();
            responseBuilder.headers(strippedHeaders);
            responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
        }
    
        // 返回轉換後的新response
        return responseBuilder.build();
    
    ////index = 3
    // Serves requests from the cache and writes responses to the cache.
    // 從cache取request,將Response保存到cache
    CacheInterceptor.intercept(Chain chain) {
        // 先調用Cache,拿到Response
        Response cacheCandidate = cache != null ? cache.get(chain.request()) : null;
    
        // 根據Cache策略判斷能否使用cache,能否使用network
        // 如果不能使用network,則返回cacheResponse或者錯誤response
    
        // 調用網絡
        Response networkResponse = chain.proceed(networkRequest);
    
        // 如果有CacheResponse,根據條件決定返回
        if (cacheResponse != null) {
            if (networkResponse.code() == HTTP_NOT_MODIFIED) {
                // 返回CacheResponse
            }
        }
    
        // 使用networkResponse
        Response response = networkResponse.newBuilder()
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
    
        // 如果支持Cache,保存Response到Cache
        cacheWritingResponse(cacheRequest, response);
    }
    
    ////index = 4
    // Opens a connection to the target server. 創建一個到Server端的Connection,HttpCodec,然後傳到下一個攔截器
    ConnectInterceptor.intercept(Chain chain) {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Request request = realChain.request();
        StreamAllocation streamAllocation = realChain.streamAllocation();
    
        // We need the network to satisfy this request. Possibly for validating a conditional GET.
        boolean doExtensiveHealthChecks = !request.method().equals("GET");
        HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
        RealConnection connection = streamAllocation.connection();
    
        return realChain.proceed(request, streamAllocation, httpCodec, connection);
    }
    
    (client.networkInterceptors)
    ////index = 5
    // This is the last interceptor in the chain. It makes a network call to the server.這個最後一個攔截器,負責真正和server端交互。
    CallServerInterceptor.intercept(Chain chain) {
        ...
        Response response = responseBuilder
            .request(request)
            .handshake(streamAllocation.connection().handshake())
            .sentRequestAtMillis(sentRequestMillis)
            .receivedResponseAtMillis(System.currentTimeMillis())
            .build();
        ...
    }
    
  6. 項目地址
    https://github.com/guolina02/HttpDemo
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章