OKhttp學習筆記--使用和源碼分析

使用
同步

    String url = "https://wanandroid.com/wxarticle/chapters/json";
    OkHttpClient okHttpClient = new OkHttpClient();
    Request request= new Request.Builder().url(url).build();
    Response response = okHttpClient.newCall(request).execute();

異步

 String url  ="https://wanandroid.com/wxarticle/chapters/json";
    OkHttpClient okHttpClient = new OkHttpClient();
    Request request = new Request.Builder().url(url).build();
    okHttpClient.newCall(request).enqueue(new Callback() {
      @Override
      public void onFailure(okhttp3.Call call, IOException e) {
        
      }

      @Override
      public void onResponse(okhttp3.Call call, Response response) throws IOException {

      }
    });

還可以添加自定義攔截器、設置超時時間、請求過程事件監聽、dns、緩存等等。。。
添加攔截器 和 事件監聽的例子 更詳細的功能使用看官方網站

OkHttpClient client = new OkHttpClient.Builder().eventListener(new EventListener() {
      @Override
      public void callStart(okhttp3.Call call) {
        super.callStart(call);
        System.out.println("callStart");
      }

      @Override
      public void dnsStart(okhttp3.Call call, String domainName) {
        super.dnsStart(call, domainName);
        System.out.println("dnsStart");
      }

      @Override
      public void dnsEnd(okhttp3.Call call, String domainName, List<InetAddress> inetAddressList) {
        super.dnsEnd(call, domainName, inetAddressList);
        System.out.println("dnsEnd");
      }
    }).addInterceptor(new Interceptor() {
      @Override
      public Response intercept(Chain chain) throws IOException {

        System.out.println("我先做點事情");


        return chain.proceed(chain.request());
      }
    }).build();
    Request request = new Request.Builder()
            .url(url)
            .build();
    Response response = client.newCall(request).execute();

    client.newCall(request).enqueue(new Callback() {
      @Override
      public void onFailure(okhttp3.Call call, IOException e) {

      }

      @Override
      public void onResponse(okhttp3.Call call, Response response) throws IOException {
        System.out.println(response.body().string());
      }
    });

源碼分析 (主流程)
OkHttpClient 和 Request 通過建造者模式構建,主要是基礎屬性 攔截器、url、dns等等

同步請求源碼流程
發起真正執行請求

Response response = client.newCall(request).execute();

最終是通過RealCall 去執行execute()

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

ReclCall裏則是通過dispatcher 調度器去執行,同步請求時將call存入
runningSyncCalls 然後發起請求 通過責任鏈模式 返回結果後從隊列中移除call,最終返回結果

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    transmitter.timeoutEnter();
    transmitter.callStart();
    try {
      //加到同步隊列中
      client.dispatcher().executed(this);
      //獲取結果返回
      return getResponseWithInterceptorChain();
    } finally {
      //從同步隊列中移除
      client.dispatcher().finished(this);
    }
  }

// client.dispatcher().executed(this)
 synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

異步請求源碼流程

  client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(okhttp3.Call call, IOException e) {

            }

            @Override
            public void onResponse(okhttp3.Call call, Response response) throws IOException {
                System.out.println(response.body().string());
            }
        });

異步請求有兩個隊列 readyAsyncCalls和runningAsyncCalls
1、先是加入到readyAsyncCalls 隊列 預備隊列
2、從readyAsyncCalls和runningAsyncCalls 中找出和此次host相同的請求,如果存在記錄個數
3、繼續執行

 void enqueue(AsyncCall call) {
    synchronized (this) {
     //加入readyAsyncCalls隊列
      readyAsyncCalls.add(call);

      // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
      // the same host.
      //RealCall的forWebSocket=false 
      if (!call.get().forWebSocket) {
        //從readyAsyncCalls和runningAsyncCalls 找出host相同的請求
        AsyncCall existingCall = findExistingCallWithHost(call.host());
        //如果找到相同的 則將host計數賦值
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
      }
    }
    //推動執行
    promoteAndExecute();
  }

//找出相同的host
@Nullable private AsyncCall findExistingCallWithHost(String host) {
    for (AsyncCall existingCall : runningAsyncCalls) {
      if (existingCall.host().equals(host)) return existingCall;
    }
    for (AsyncCall existingCall : readyAsyncCalls) {
      if (existingCall.host().equals(host)) return existingCall;
    }
    return null;
  }

接下來執行promoteAndExecute(),這裏涉及一個邏輯 ,如何將readyAsyncCalls裏的call轉移到runningAsyncCalls中
規則是:如果runningAsyncCalls大於等於64 默認 或者正在執行的host 相同的已經超過5個默認 則不轉移 每次取出要執行的call都是這樣去判斷的

private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();
        //如果runningAsyncCalls大於等於maxRequests 默認64 則直接中斷
        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        //或者 如果相同host大於等於maxRequestsPerHost 默認5 也不添加
        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
        //從readyAsyncCalls中移除
        i.remove();
        //將host的次數++
        asyncCall.callsPerHost().incrementAndGet();
        //加入到可執行的executableCalls隊列中 臨時隊列
        executableCalls.add(asyncCall);
        //加到runningAsyncCalls中
        runningAsyncCalls.add(asyncCall);
      }
      //是否正常執行  runningAsyncCalls.size() + runningSyncCalls.size()  同步隊列或異步隊列有數據則證明有在運行  因爲運行結束會移除
      isRunning = runningCallsCount() > 0;
    }
    //將滿足條件的可執行的call 執行 
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      //調用executeOn 其實是一個Runnable
      asyncCall.executeOn(executorService());
    }

    return isRunning;
  }

//AsyncCall 繼承NamedRunnable 包裝了一下

public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

在AsyncCall中執行executeOn方法

void executeOn(ExecutorService executorService) {
      assert (!Thread.holdsLock(client.dispatcher()));
      boolean success = false;
      try {
        //線程池 執行runnable 即 execute()
        executorService.execute(this);
        success = true;
      } catch (RejectedExecutionException e) {
        InterruptedIOException ioException = new InterruptedIOException("executor rejected");
        ioException.initCause(e);
        transmitter.noMoreExchanges(ioException);
        responseCallback.onFailure(RealCall.this, ioException);
      } finally {
        if (!success) {
          client.dispatcher().finished(this); // This call is no longer running!
        }
      }
    }

@Override protected void execute() {
      boolean signalledCallback = false;
      transmitter.timeoutEnter();
      try {
      //和同步請求一樣  通過攔截器 獲取結果 
        Response response = getResponseWithInterceptorChain();
        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 {
          responseCallback.onFailure(RealCall.this, e);
        }
      } catch (Throwable t) {
        cancel();
        if (!signalledCallback) {
          IOException canceledException = new IOException("canceled due to " + t);
          canceledException.addSuppressed(t);
          responseCallback.onFailure(RealCall.this, canceledException);
        }
        throw t;
      } finally {
        //從runningAsyncCalls移除請求call 並且在相同的host上減1
        client.dispatcher().finished(this);
      }
    }
  }

除了五大攔截器 主線流程大致如此
五大攔截器大致邏輯(圖片來自享學)
圖片來自享學
備註下連接池 (圖片來自享學)
在這裏插入圖片描述

核心線程數:0,不會緩存線程,所有線程60秒內沒工作就會被回收
最大線程數爲:Integer.MAX_VALUE
等待隊列:SynchronousQueue,兩者結合達最大吞吐量,沒有空閒線程無需等待,直接創建新的執行
//連接池的線程池

public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

清理任務的線程池

  private static final Executor executor = new ThreadPoolExecutor(0 /* corePoolSize */,
      Integer.MAX_VALUE /* maximumPoolSize */, 60L /* keepAliveTime */, TimeUnit.SECONDS,
      new SynchronousQueue<>(), Util.threadFactory("OkHttp ConnectionPool", true));
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章