1. 程式人生 > >Okhttp3使用 + 原始碼完全解析

Okhttp3使用 + 原始碼完全解析

在使用過okhttp3之後,必然的一步當是對原始碼的研究 這樣可以對其優劣和功能封裝有一個全面詳盡的瞭解
ok 下面貼上okhttp3的核心程式碼(url暫時隨意定義)

 OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder()
                .url("www.baidu.com")
                .build();
        Call call = okHttpClient.newCall(request);
        call
.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { } });

下面進入正題,來對原始碼進行分析

1.okhttp例項 和Request例項和配置

相當於初始化例項 比較簡單
首先來看okhttp初始化例項

1.1 //OkHttpClient okHttpClient = new OkHttpClient();

public OkHttpClient() {
    this(new 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; }

其實就是初始化物件 和屬性的相關配置過程

1.2Requst的初始化

 Request request = new Request.Builder()
                .url("www.baidu.com")
                .build();

下面來看其原始碼:

 public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }

  public Builder url(String url) {
      if (url == null) throw new NullPointerException("url == null");

      // Silently replace web socket URLs with HTTP URLs.
      if (url.regionMatches(true, 0, "ws:", 0, 3)) {
        url = "http:" + url.substring(3);
      } else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
        url = "https:" + url.substring(4);
      }

      HttpUrl parsed = HttpUrl.parse(url);
      if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);
      return url(parsed);
    }


public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }

到這裡準備工作就完成了

下面來從請求開始詳細看原始碼(以上比較簡單 沒有什麼可以講的)

2.請求處理

//Call call = okHttpClient.newCall(request);

@Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }




static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }

okhttp3建立請求的主體call物件 其實返回的是一個realCall的例項化物件,並完成一系列的相關初始化配置;
下面來看請求

//call.enqueue

 @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

由以上程式碼 可以看出來 okhttp的請求是由dispatcher來完成的;而dispatcher又是什麼呢 ?
其實是一個網路的任務排程器;
那麼下面來看下排程器的實現.

3.dispatcher任務排程

  //最大請求數
  private int maxRequests = 64;
  //最大主機請求數
  private int maxRequestsPerHost = 5;
  private @Nullable Runnable idleCallback;
  //消費者執行緒池
  private @Nullable ExecutorService executorService;
  //將要執行的非同步請求佇列
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
  //正在執行的非同步請求佇列
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
  //正在執行的同步請求佇列
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

  public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }

  public Dispatcher() {
  }

Dispatcher主要用於控制併發 並且維護了一部分變數
在請求之前dispatcher會自己建立執行緒池
當然也可以有程式設計師自己來建立執行緒池

ok~初步瞭解排程器之後來看非同步請求的原始碼


  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

在排程器的enqueue中會先判斷當前最大請求數和當前最大主機請求數 如果不超過預設的最大值 則吧請求加入到正在執行的請求佇列—->runningAsyncCalls;
否則會加入到將要執行的請求佇列中進行等待—->readyAsyncCalls

然後 executorService().execute(call);開始執行傳入的執行緒 即傳入的AsyncCall;而AsyncCall作為RealCall的內部實現類 會走execute;

ok下面來看AsyncCall–>execute ,在這裡 開始進行網路請求:

  @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

在上面程式碼中不難看出走了一系列方法 來進行網路請求 為了方便讀者理解 先來看finally下的方法 (這個方法必然會走到的):

  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

//finished
  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }




//####promoteCalls()
private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

直接提取關鍵程式碼:
finished—->if (promoteCalls) promoteCalls(); 當請求完之後 promoteCalls為true 則會走promoteCalls() ;從上面程式碼中可圖看出來promoteCalls()中用迭代器遍歷readyAsyncCalls 然後加入到runningAsyncCalls

其實就是在請求完成後 來請求快取池中的執行緒

那麼繼續回頭看AsyncCall–>execute中的邏輯,在這之前先簡單介紹一下攔截器 okhttp中請求有用到攔截器

4.攔截器Interceptors

攔截器主要是用來監聽網路的請求和響應 攔截器的新增可以新增,移除或者轉換請求頭;其實簡單來說就是對網路請求和響應的一個包裝吧,有興趣可以自己研究一下 這裡簡單介紹一下方便接下來的講解;

繼續回到程式碼:AsyncCall–>execute:


    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

上面程式碼中關鍵的請求程式碼是:

Response response = getResponseWithInterceptorChain();

下面我們來看見其原始碼:

 Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    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, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

在上面程式碼中:

 Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

其實是初始化了攔截器 傳參為讀取/請求超時等….
實際請求的程式碼是:chain.proceed(originalRequest);

@Override public Response proceed(Request request) throws IOException {
      // If there's another interceptor in the chain, call that.
      if (index < client.interceptors().size()) {
        Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
        //從攔截器列表取出攔截器
        Interceptor interceptor = client.interceptors().get(index);
        Response interceptedResponse = interceptor.intercept(chain);

        if (interceptedResponse == null) {
          throw new NullPointerException("application interceptor " + interceptor
              + " returned null");
        }

        return interceptedResponse;
      }

      // No more interceptors. Do HTTP.
      return getResponse(request, forWebSocket);
    }

在攔截器攔截過程中,當存在多個攔截器,需要攔截等待,即第一個請求完下一個請求;其中攔截的程式碼會在上面程式碼中:

    Interceptor interceptor = client.interceptors().get(index);
        Response interceptedResponse = interceptor.intercept(chain);

而真正請求的程式碼是//getResponse(request, forWebSocket);
那麼接著來看它的原始碼:

Response getResponse(Request request, boolean forWebSocket) throws IOException {
 //請求成功的核心原始碼

    engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);

    int followUpCount = 0;
    while (true) {
      if (canceled) {
        engine.releaseStreamAllocation();
        throw new IOException("Canceled");
      }

      boolean releaseConnection = true;
      try {
        engine.sendRequest();
        engine.readResponse();
        releaseConnection = false;
      } catch (RequestException e) {
        // The attempt to interpret the request failed. Give up.
        throw e.getCause();
      } catch (RouteException e) {
        ...    
    }


     //請求失敗的核心原始碼
     boolean releaseConnection = true;
      try {
        engine.sendRequest();
        engine.readResponse();
        releaseConnection = false;
      } catch (RequestException e) {
        // The attempt to interpret the request failed. Give up.
        throw e.getCause();
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null);
        if (retryEngine != null) {
          releaseConnection = false;
          engine = retryEngine;
          continue;
        }
        // Give up; recovery is not possible.
        throw e.getLastConnectException();
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        HttpEngine retryEngine = engine.recover(e, null);
        if (retryEngine != null) {
          releaseConnection = false;
          engine = retryEngine;
          continue;
        }

        // Give up; recovery is not possible.
        throw e;
      } finally {
        // We're throwing an unchecked exception. Release any resources.
        if (releaseConnection) {
          StreamAllocation streamAllocation = engine.close();
          streamAllocation.release();
        }
      }
     .....
      engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,
          response);
    }

  }

到這裡okhttp3的原始碼就解析完畢了 感謝支援 希望可以幫到你們~

相關推薦

Okhttp3使用 + 原始碼完全解析

在使用過okhttp3之後,必然的一步當是對原始碼的研究 這樣可以對其優劣和功能封裝有一個全面詳盡的瞭解 ok 下面貼上okhttp3的核心程式碼(url暫時隨意定義) OkHttpClient okHttpClient = new OkHttpCl

DialogFragment使用到原始碼完全解析

前言 最近專案中用到了DialogFragment,用起來很方便,但是坑比較多,於是自己研究了下原始碼,理清楚DialogFragment中Dialog和Fragment的關係,以及DialogFragment的原理。 DialogFragment的使用方法 1、重寫onCr

HandlerThread 使用及其原始碼完全解析

二.HandlerThread的使用案例 package com.zejian.handlerlooper; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory

Android 圖片載入框架Picasso基本使用和原始碼完全解析

寫在之前 原本打算是每週更新一篇博文,同時記錄一週的生活狀態,但是稍微工作忙一點就顧不上寫部落格了。悲催 還是說下最近的狀況,最近兩週一直在接公司申請的計費點, 沃商店,銀貝殼,微信等等,然後就是不停的被人催促催促,真是一個頭兩個大。在這期間專案組還搞了個A

Spring框架之beans原始碼完全解析

導讀:Spring可以說是Java企業開發裡最重要的技術。而Spring兩大核心IOC(Inversion of Control控制反轉)和AOP(Aspect Oriented Programming面向切面程式設計)其中又以IOC最為核心。IOC容器是Spring的核心模組,Spring提供了兩種型別的容

Spring框架之AOP原始碼完全解析

Spring框架之AOP原始碼完全解析         Spring可以說是Java企業開發裡最重要的技術。Spring兩大核心IOC(Inversion of Control控制反轉)和AOP(Aspect Oriented Programming面向切面程式設計)。

Spring框架之jms原始碼完全解析

Spring框架之jms原始碼完全解析         我們在前兩篇文章中介紹了Spring兩大核心IOC(Inversion of Control控制反轉)和AOP(Aspect Oriented Programming面向切面程式設計)技術:Spring框架之bea

Spring框架之spring-web http原始碼完全解析

Spring框架之spring-web http原始碼完全解析         Spring-web是Spring webMVC的基礎,由http、remoting、web三部分組成。         http:封裝了http協議

Spring框架之websocket原始碼完全解析

Spring框架之websocket原始碼完全解析        Spring框架從4.0版開始支援WebSocket,先簡單介紹WebSocket協議(詳細介紹參見“WebSocket協議中文版”https://www.cnblogs.com/xx

Spring框架之事務原始碼完全解析

Spring框架之事務原始碼完全解析   事務的定義及特性:        事務是併發控制的單元,是使用者定義的一個操作序列。這些操作要麼都做,要麼都不做,是一個不可分割的工作單位。通過事務將邏輯相關的一組操作繫結在一起,以便伺服器保持資料

OkHttp3原始碼解析

在OkHttp3中,其靈活性很大程度上體現在可以 intercept 其任意一個環節,而這個優勢便是okhttp3整個請求響應架構體系的精髓所在,先放出一張主框架請求流程圖,接著再分析原始碼。 Okhttp請求流程 String url = "http://www

Android事件分發機制完全解析,帶你從原始碼的角度徹底理解(上)-郭霖

其實我一直準備寫一篇關於Android事件分發機制的文章,從我的第一篇部落格開始,就零零散散在好多地方使用到了Android事件分發的知識。也有好多朋友問過我各種問題,比如:onTouch和onTouchEvent有什麼區別,又該如何使用?為什麼給ListView引入了一

Android事件分發機制完全解析,帶你從原始碼的角度徹底理解(下)-郭霖

記得在前面的文章中,我帶大家一起從原始碼的角度分析了Android中View的事件分發機制,相信閱讀過的朋友對View的事件分發已經有比較深刻的理解了。 還未閱讀過的朋友,請先參考 Android事件分發機制完全解析,帶你從原始碼的角度徹底理解(上) 。 那麼今天我們將繼

Android Volley完全解析 四 ,帶你從原始碼的角度理解Volley

                經過前三篇文章的學習,Volley的用法我們已經掌握的差不多了,但是對於Volley的工作原理,恐怕有很多朋友還不是很清楚。因此,本篇文章中我們就來一起閱讀一下Volley的原始碼,將它的工作流程整體地梳理一遍。同時,這也是Volley系列的最後一篇文章了。其實,Volley的

Android ListView工作原理完全解析,帶你從原始碼的角度徹底理解

在Android所有常用的原生控制元件當中,用法最複雜的應該就是ListView了,它專門用於處理那種內容元素很多,手機螢幕無法展示出所有內容的情況。ListView可以使用列表的形式來展示內容,超出螢幕部分的內容只需要通過手指滑動就可以移動到螢幕內了。另外ListView還

Android事件分發機制完全解析,帶你從原始碼的角度徹底理解(上)

其實我一直準備寫一篇關於Android事件分發機制的文章,從我的第一篇部落格開始,就零零散散在好多地方使用到了Android事件分發的知識。也有好多朋友問過我各種問題,比如:onTouch和onTouchEvent有什麼區別,又該如何使用?為什麼給ListView引入

Android事件分發機制完全解析,帶你從原始碼的角度徹底理解(下)

記得在前面的文章中,我帶大家一起從原始碼的角度分析了Android中View的事件分發機制,相信閱讀過的朋友對View的事件分發已經有比較深刻的理解了。那麼今天我們將繼續上次未完成的話題,從原始碼的角度分析ViewGroup的事件分發。首先我們來探討一下,什麼是ViewGro

Activity原始碼之Android 6.0許可權相關完全解析

我們都知道Android6.0以前許可權的申請非常簡單,只需要在mainfest宣告所需的許可權即可。而6.0以後,Android將許可權的管理進一步嚴格化,它要求使用者在使用某些敏感許可權時,必須在mainfest中先宣告之後再動態申請。在一定程度上約束了

Android事件分發機制完全解析,帶你從原始碼的角度徹底理解(下) (出自郭林老師)

記得在前面的文章中,我帶大家一起從原始碼的角度分析了Android中View的事件分發機制,相信閱讀過的朋友對View的事件分發已經有比較深刻的理解了。 那麼今天我們將繼續上次未完成的話題,從原始碼的角度分析ViewGroup的事件分發。 首先我們來探討一下,什

Android事件分發機制完全解析,帶你從原始碼的角度徹底理解(上) (出自郭霖老師)

其實我一直準備寫一篇關於Android事件分發機制的文章,從我的第一篇部落格開始,就零零散散在好多地方使用到了Android事件分發的知識。也有好多朋友問過我各種問題,比如:onTouch和onTouchEvent有什麼區別,又該如何使用?為什麼給ListView引入了