日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

OkHttp源码解析(上)

發布時間:2024/1/18 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OkHttp源码解析(上) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

導語

學過Android開發的同學都知道,我們使用網絡請求獲取服務器的數據時,通常使用的是封裝好的Retrofit框架,這個框架很好的幫助我們對網絡的發起,以及返回的數據進行操作,我們使用起來十分方便,對于Retrofit來說,我們僅僅看到了它的表面,如何正確使用等,其內部還是要借助OkHtttp來請求網絡的,Retrofit只是對OkHttp進行了再次的封裝,而且Retrofit不具備網絡請求功能,只是在OkHtttp的外表又套了一層,對返回的數據支持RxJava轉換和Gson解析。真正起到網絡請求的還是OkHttp,所以要了解里面的實質,我們還是著手從OkHttp出發,來探索它對網絡的認知和對數據的傳遞。

OkHttp使用方式

要想了解其原理,首先得學會使用它,OkHttp的使用也非常簡單。

從請求方式來講,分為 getpost

get請求的同步和異步

// 構建一個OkHttpClient對象 OkHttpClient client = new OkHttpClient.Builder().build();// 創建一個Request請求對象 Request request = new Request.Builder().get().url("https://www.baidu.com/").build();// 把request對象 通過 newCall 轉換成call Call call = client.newCall(request);try {// 通過call來發起網絡請求// 同步請求Response response = call.execute();//返回響應體ResponseBody body = response.body();System.out.println(body.string()); } catch (IOException e) {e.printStackTrace(); }// 通過call來發起網絡請求 // 異步請求 call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {}@Overridepublic void onResponse(Call call, Response response) throws IOException {// 返回響應體ResponseBody body = response.body();System.out.println(body.string());} });

上面的步驟也非常清楚:

  • 構建一個OkHttpClient對象,通過Builder來構建我們的client,我們自由配置client。(添加攔截器,超時時間等)
  • 創建一個Request請求對象,Request對象通過構建者模式創建,我們可以自由地配置Request對象。
  • 把request對象 通過 newCall 轉換成call。
  • 通過call來發起網絡請求,同步請求使用execute,異步請求使用enqueue。
  • 通過response.body()來獲取響應體。
  • post請求的同步和異步

    OkHttpClient client = new OkHttpClient.Builder().build();// 表單格式構建 RequestBody RequestBody requestBody = new FormBody.Builder().add("username", "admin").add("password", "123456").build();Request request = new Request.Builder().post(requestBody).url("https://www.baidu.com/").build();Call call = client.newCall(request); try {Response response = call.execute();ResponseBody responseBody = response.body();System.out.println(responseBody.string()); } catch (IOException e) {e.printStackTrace(); }call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {}@Overridepublic void onResponse(Call call, Response response) throws IOException {ResponseBody responseBody = response.body();System.out.println(responseBody.string());} });

    post請求與get請求的唯一區別就是:post請求通過RequestBody來構建一個請求體,Body里面帶上我們要請求的參數;而get請求的參數是拼接在url后面。

    了解了OkHttp的使用,我們梳理下整個OkHttp的調用過程。

    不管我們通過execute還是enqueue,都會經過Dispatcher(分發器)和 Interceptors(攔截器)來獲得Response。

    分發器到底做了什么事情,接下來我們深入地了解分發器內部的原理。

    分發器—Dispatcher

    我們從上面的代碼中知道,真正請求網絡的是一個call對象,call是一個接口,通過RealCall實現,我們通過newCall(request)構建的這個call,通過execute或enqueue就能獲得response,為何,進入execute看下:

    @Override public Response execute() throws IOException {synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}captureCallStackTrace();eventListener.callStart(this);try {client.dispatcher().executed(this);Response result = getResponseWithInterceptorChain();if (result == null) throw new IOException("Canceled");return result;} catch (IOException e) {eventListener.callFailed(this, e);throw e;} finally {client.dispatcher().finished(this);} }

    可以看到,調用了分發器的executed,然后通過getResponseWithInterceptorChain()獲得響應。

    synchronized void executed(RealCall call) {runningSyncCalls.add(call); }

    將call加入running隊列。

    對于同步請求,分發器并沒有做什么事情。我們分析下異步請求的分發器干了什么事情。

    @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)); }

    調用了分發器的enqueue,注意這里:分發器將Callback包裝成AsyncCall來傳給了enqueue。AsyncCall繼承自NamedRunnable,而NamedRunnable又實現了Runnable,所以AsyncCall本質上就是一個Runnable。AsyncCall交給了分發器:

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

    這里通過兩個條件判斷:

  • 正在執行的running隊列里面的任務數小于最大請求數(64)以及同一Host的請求數小于最大同一Host請求數(5)時,將call加入running隊列,然后交給線程池處理。否則,將AsyncCall加入ready隊列。

    分別解釋下這兩個條件:

  • 第一個條件:充分考慮到客戶端的壓力,如果沒有這個64的限制,客戶端不停的進行網絡請求,這樣會讓客戶端的壓力特別大。
  • 第二個條件:充分考慮到服務器的壓力,如果同一個Host的服務器,沒有這個限制,對于一個客戶端就建立64次連接,如果有多個客戶端同時建立連接的話會撐爆服務器。
  • 如果把任務放在ready隊列后,這個隊列里的任務怎么執行,什么時候執行?

  • 前面說到,正在執行的任務會交給線程池處理,當線程池處理完之后,會finish掉這個任務。由于AsyncCall本質上就是一個Runnable,所以會調用run方法,而run方法里面又調用了execute方法,execute方法是一個抽象方法,所以在分發器里實現如下:

    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(); } @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里的代碼,(看這個try…catch代碼塊),在finally里會調用分發器的finished方法:

    void finished(AsyncCall call) {finished(runningAsyncCalls, call, true); } 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();} }

    到這里,我們分析下這個方法:

    記錄這兩個參數:calls就是runningAsyncCalls,promoteCalls是true

    首先執行calls.remove(call),說明這個call完成了,從隊列里面移除,然后調用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.} }

    來看promoteCalls,這個方法首先做兩個判斷,如果running隊列任務數大于maxRequests,直接返回;如果ready隊列為空,直接返回。

    接下來,開始從ready執行隊列里遍歷任務,通過next()取到下一個任務,再對這個任務進行判斷,如果同一Host請求數已經有5個了,那就不會從ready隊列取出任務到running隊列,否則,從ready隊列取出任務放入running隊列,交給線程池,同時移除掉ready隊列的任務。

    用流程圖看下分發器的流程:

    講到這里,我們說一下OkHttp里用到的線程池。

    線程池

    線程池的工作原理

    當一個任務通過execute(Runnable)方法添加到線程池時:

  • 當線程數小于corepoolsize時,創建新的線程來處理被添加的任務
  • 當線程數大于等于corepoolsize時,如果有空閑線程,則使用空閑線程開處理被添加的任務
  • 當線程數大于等于corepoolsize時,如果沒有空閑線程,則任務放入等待隊列,添加成功則等待空閑線程,添加失敗:
  • 當線程數小于maxpoolsize時,創建新的線程來執行任務,當線程數大于maxpoolsize時,拒絕任務.
  • OkHttp如何創建線程池

    前面我們說到,running隊列的任務直接交給線程池處理,那我們看下線程池是如何處理這么多任務的。

    OkHttp通過executorService()構建一個線程池

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

    帶著兩個問題分析:

  • 注意到這個線程池和其他線程池有什么區別?
  • OkHttp為什么會構建這樣一個線程池?
  • 首先看第一個問題:

    我們知道,構建線程池的幾大參數是:核心線程數、最大線程數、線程存活時間、存活時間單位、阻塞隊列、線程工廠。

    對應到這個線程池中:核心線程數為0,最大線程數為0x7fffffff(2147483647),存活時間是60s,阻塞隊列使用了SynchronousQueue。

    如果了解線程池的工作原理的話,這個線程池沒有核心線程數,來一個任務加入隊列,那么看看這個隊列:SynchronousQueue

    這是一個沒有容量的雙端隊列,說明一個問題,這個隊列里存放不了任務,而最大線程數是如此的龐大,那么,來一個任務就會立馬新建線程來執行,但是并不是每一個任務都會新建線程,線程有60s的存活時間,如果這個線程執行完任務后,下一個任務來時,就會復用線程,所以這樣設計,就是為了提高執行效率,這是一個高并發,最大吞吐量的線程池

    第二個問題:

    OkHttp這么設計,就是為了能夠最大限度地執行任務請求,任務無需等待,立馬執行。

    攔截器—Interceptors

    上面分析到,分發器會將任務交給線程池處理,然后調用getResponseWithInterceptorChain()獲取響應結果,這個方法字面意思就是:通過攔截器責任鏈獲取響應,看一下這個方法:

    Response result = 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) { // 如果不是webSocket,將自定義的網絡攔截器添加進去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的List集合,首先通過addAll方式將攔截器添加進list集合中,這里的攔截器是我們自定義的攔截器。以及下面的client.networkInterceptors()。

    /*** 自定義攔截器*/ public class MyInterceptor implements Interceptor {@Overridepublic Response intercept(Chain chain) throws IOException {Request request = chain.request();RealInterceptorChain realChain = (RealInterceptorChain) chain;return realChain.proceed(request);} } OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new MyInterceptor()).addInterceptor(new MyInterceptor()).addInterceptor(new MyInterceptor()).addNetworkInterceptor(new MyInterceptor()).build();

    如果沒有自定義攔截器,那么這個集合就是null。

    接下來添加retryAndFollowUpInterceptor(重試重定向攔截器)、 BridgeInterceptor(橋接攔截器)、CacheInterceptor(緩存攔截器)、ConnectInterceptor(連接攔截器)、CallServerInterceptor(服務器通訊攔截器),一共有五大攔截器,這些攔截器是怎么一個一個執行起來,幫助我們完成整個請求過程,這里就涉及到一個設計模式——責任鏈模式

    責任鏈模式

    責任鏈模式是一種對象行為型模式,為請求創建了一個接收者對象的鏈,在處理請求的時候執行過濾(各司其職)。

    責任鏈上的處理者負責處理請求,客戶只需要將請求發送到責任鏈即可,無須關心請求的處理細節和請求的傳遞,所以責任鏈將請求的發送者和請求的處理者解耦了。

    文字描述理解起來確定有點困難,我們通過代碼來看一下什么是責任鏈。

    首先定義一個攔截器接口,五大攔截器分別實現這個接口,定義一個Chain鏈條實體。

    第一個攔截器:RetryAndFollowUpInterceptor:

    public class RetryAndFollowUpInterceptor implements Interceptor {@Overridepublic String intercept(Chain chain) {System.out.println("開始執行重試重定向攔截器");String result = chain.proceed(chain.request + "===>經過重試重定向攔截器");System.out.println("結束執行重試重定向攔截器");return result + "===>經過重試重定向攔截器";} }

    最后一個攔截器:CallServerInterceptor:

    public class CallServerInterceptor implements Interceptor {@Overridepublic String intercept(Chain chain) {System.out.println("開始執行服務器通訊攔截器");System.out.println("===發起請求===");System.out.println("結束執行服務器通訊攔截器");return chain.request + "===>經過請求服務器攔截器\nOkHttp響應===>經過請求服務器攔截器";} }

    Chain:

    public class Chain {private List<Interceptor> interceptors;private int index;public String request;public Chain(List<Interceptor> interceptors, int index, String request) {this.interceptors = interceptors;this.index = index;this.request = request;}public Chain(List<Interceptor> interceptors, int index) {this.interceptors = interceptors;this.index = index;}public String proceed(String request) {if (index >= interceptors.size()) {throw new AssertionError();}Chain chain = new Chain(interceptors, index + 1, request);Interceptor interceptor = interceptors.get(index);return interceptor.intercept(chain);} } public static void main(String[] args) {List<Interceptor> interceptors = new ArrayList<>();interceptors.add(new RetryAndFollowUpInterceptor());interceptors.add(new BridgeInterceptor());interceptors.add(new CacheInterceptor());interceptors.add(new ConnectInterceptor());interceptors.add(new CallServerInterceptor());Chain chain = new Chain(interceptors, 0);System.out.println(chain.proceed("OkHttp請求"));}

    首先將五大攔截器加入List集合中,創建一個Chain對象,將List集合加入鏈條,并指向第一個攔截器,開始執行proceed,調用Chain的proceed,new一個新的Chain,這時index + 1, 說明新的鏈條指向第二個攔截器,拿到index對應的攔截器,執行intercept方法,在intercept方法中,通過新鏈條的index執行第二個攔截器的intercept,以此類推,這樣就像工廠流水線一樣,一個工序一個工序流下去,而且每個工序在傳遞給下一個工序之前,還能做自己的事情,互相不影響,這就是所謂的責任鏈。看下這個過程的打印結果:

    OkHttp請求===>經過重試重定向攔截器 ===>經過橋接攔截器 ===>經過緩存攔截器 ===>經過連接攔截器===>經過請求服務器攔截器 OkHttp響應===>經過請求服務器攔截器===>經過連接攔截器===>經過緩存攔截器===>經過橋接攔截器===>經過重試重定向攔截器

    拿到響應后,再根據對應的攔截器反向傳輸回來,類似一個U型流向。

    了解了責任鏈模式后,我們分析下這五大默認攔截器。

    重試重定向攔截器

    第一個攔截器:RetryAndFollowUpInterceptor,主要就是完成兩件事情:重試和重定向。

    重試

    @Override public Response intercept(Chain chain) throws IOException {Request request = chain.request();RealInterceptorChain realChain = (RealInterceptorChain) chain;Call call = realChain.call();EventListener eventListener = realChain.eventListener();streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()),call, eventListener, callStackTrace);int followUpCount = 0;Response priorResponse = null;while (true) {if (canceled) {streamAllocation.release();throw new IOException("Canceled");}Response response;boolean releaseConnection = true;try {response = realChain.proceed(request, streamAllocation, null, null);releaseConnection = false;} catch (RouteException e) {// The attempt to connect via a route failed. The request will not have been sent.if (!recover(e.getLastConnectException(), false, request)) {throw e.getLastConnectException();}releaseConnection = false;continue;} catch (IOException e) {// An attempt to communicate with a server failed. The request may have been sent.boolean requestSendStarted = !(e instanceof ConnectionShutdownException);if (!recover(e, requestSendStarted, request)) throw e;releaseConnection = false;continue;} finally {// We're throwing an unchecked exception. Release any resources.if (releaseConnection) {streamAllocation.streamFailed(null);streamAllocation.release();}}// Attach the prior response if it exists. Such responses never have a body.if (priorResponse != null) {response = response.newBuilder().priorResponse(priorResponse.newBuilder().body(null).build()).build();}Request followUp;try {followUp = followUpRequest(response, streamAllocation.route());} catch (IOException e) {streamAllocation.release();throw e;}if (followUp == null) {streamAllocation.release();return response;}closeQuietly(response.body());// 限制重定向最大次數為20次if (++followUpCount > MAX_FOLLOW_UPS) {streamAllocation.release();throw new ProtocolException("Too many follow-up requests: " + followUpCount);}if (followUp.body() instanceof UnrepeatableRequestBody) {streamAllocation.release();throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());}// 判斷是不是可以復用同一份連接if (!sameConnection(response, followUp.url())) {streamAllocation.release();streamAllocation = new StreamAllocation(client.connectionPool(),createAddress(followUp.url()), call, eventListener, callStackTrace);this.streamAllocation = streamAllocation;} else if (streamAllocation.codec() != null) {throw new IllegalStateException("Closing the body of " + response+ " didn't close its backing stream. Bad interceptor?");}request = followUp;priorResponse = response;} }

    首先在一個while(true)的死循環里,通過各種try…catch來分別處理不同類型的異常。

    首先定義了一個布爾型releaseConnection變量,默認為true,在try語句塊里:

    response = realChain.proceed(request, streamAllocation, null, null); releaseConnection = false;

    如果請求沒有異常,責任鏈將事件分發到下一個攔截器,releaseConnection置為false。

    如果請求階段發生了 RouteException 或者 IOException會進行判斷是否重新發起請求。

    RouteException
    catch (RouteException e) {// 路由異常,連接未成功,請求還沒發出去。if (!recover(e.getLastConnectException(), false, request)) {throw e.getLastConnectException();}releaseConnection = false;continue; }
    IOException
    catch (IOException e) {// 請求發出去了,但是和服務器通信失敗了。(socket流正在讀寫數據的時候斷開連接)// HTTP2才會拋出ConnectionShutdownException。所以對于HTTP1 requestSendStarted一定是trueboolean requestSendStarted = !(e instanceof ConnectionShutdownException);if (!recover(e, requestSendStarted, request)) throw e;releaseConnection = false;continue; }

    兩個方法都是通過recover方法判斷是否能夠進行重試,如果返回true,則表示允許重試。進入recover方法看下:

    private boolean recover(IOException e, boolean requestSendStarted, Request userRequest) {streamAllocation.streamFailed(e);// 1if (!client.retryOnConnectionFailure()) return false;// 2if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody) return false;// 3if (!isRecoverable(e, requestSendStarted)) return false;// 4if (!streamAllocation.hasMoreRoutes()) return false;// For failure recovery, use the same route selector with a new connection.return true; }

    分別看下這幾個if語句:

  • 在配置OkhttpClient是設置了不允許重試(默認允許),則一旦發生請求失敗就不再重試。這個就是在我們最開始配置OkhttpClient時,如果手動配置了fasle,則不允許重試。

  • 這個判斷在RouteException下是永遠不成立的,所以我們看IOException的情況,在IOException里面:

    boolean requestSendStarted = !(e instanceof ConnectionShutdownException);

    前面我們分析到,HTTP2才會拋出ConnectionShutdownException。所以對于HTTP1,requestSendStarted一定是true,所以主要看第二個條件,userRequest.body() instanceof UnrepeatableRequestBody,UnrepeatableRequestBody是一個接口,這個接口的作用就是打個標簽,如果我們的請求體標記成這種類型,那就表示這個請求攔截器不會幫你重試。

    public class MyRequestBody extends RequestBody implements UnrepeatableRequestBody {@Nullable@Overridepublic MediaType contentType() {return null;}@Overridepublic void writeTo(BufferedSink sink) throws IOException {} }

    就像上面這樣,自定義MyRequestBody,并且實現了UnrepeatableRequestBody這個接口,如果請求的是我們自定義的請求體,那么OkHttp對這次請求失敗后,判斷到你的請求體實現了這個接口,那他就不會幫你重試。

  • 判斷是不是屬于重試的異常,這個里面調用了isRecoverable方法。如果這個方法返回了false,那么也不會重試,什么情況下返回false呢,我們看一下:

    private boolean isRecoverable(IOException e, boolean requestSendStarted) {// 如果是協議異常,返回falseif (e instanceof ProtocolException) {return false;}if (e instanceof InterruptedIOException) {return e instanceof SocketTimeoutException && !requestSendStarted;}if (e instanceof SSLHandshakeException) {if (e.getCause() instanceof CertificateException) {return false;}}if (e instanceof SSLPeerUnverifiedException) {// e.g. a certificate pinning error.return false;}return true; }
  • 如果是協議的異常,則返回false,什么情況下是協議的異常,我們看下源碼:在CallServerInterceptor中,有一段這樣的代碼:

    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {throw new ProtocolException("HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength()); }

    如果code是204或者205(204表示服務器沒有返回內容,205表示服務器讓你重置內容,也就是刷新網頁。),這種情況下,是沒有響應體的,但是第二個條件是響應體長度大于0,這就沖突了,這種沖突是服務器的問題,就會拋出ProtocolException。

  • 如果異常屬于IO異常,同時又屬于SocketTimeoutException,那OkHttp就會幫你重試。例如:網絡波動造成了Socket連接的超時,可以使用不同路線重試。

  • SSL證書異常/SSL驗證失敗異常,則不會重試。前者是證書驗證失敗,后者可能就是壓根就沒證書,或者證書數據不正確。

  • 如果SSL握手未授權異常,也不能重試。

  • 經過了異常的判定之后,如果仍然允許進行重試,就會再檢查當前有沒有可用路由路線來進行連接。簡單來說,比如 DNS 對域名解 析后可能會返回多個 IP,在一個IP失敗后,嘗試另一個IP進行重試。

  • 重定向

    如果請求結束后沒有發生異常并不代表當前獲得的響應就是最終需要給用戶的,還需要進一步來判斷是否需要重定向,重定向的判斷在followUpRequest方法中。

    private Request followUpRequest(Response userResponse, Route route) throws IOException {if (userResponse == null) throw new IllegalStateException();int responseCode = userResponse.code();final String method = userResponse.request().method();switch (responseCode) {// 407 客戶端使用了HTTP代理服務器,在請求頭中添加 “Proxy-Authorization”,讓代理服務器授權case HTTP_PROXY_AUTH:Proxy selectedProxy = route != null? route.proxy(): client.proxy();if (selectedProxy.type() != Proxy.Type.HTTP) {throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");}return client.proxyAuthenticator().authenticate(route, userResponse);// 401 需要身份驗證 有些服務器接口需要驗證使用者身份 在請求頭中添加 “Authorization”case HTTP_UNAUTHORIZED:return client.authenticator().authenticate(route, userResponse);// 308 永久重定向// 307 臨時重定向case HTTP_PERM_REDIRECT:case HTTP_TEMP_REDIRECT:// 如果請求方式不是GET或者HEAD,框架不會自動重定向請求if (!method.equals("GET") && !method.equals("HEAD")) {return null;}// 300 301 302 303case HTTP_MULT_CHOICE:case HTTP_MOVED_PERM:case HTTP_MOVED_TEMP:case HTTP_SEE_OTHER:// 如果用戶不允許重定向,那就返回nullif (!client.followRedirects()) return null;// 從響應頭取出locationString location = userResponse.header("Location");if (location == null) return null;// 根據location 配置新的請求 urlHttpUrl url = userResponse.request().url().resolve(location);// 如果為null,說明協議有問題,取不出來HttpUrl,那就返回null,不進行重定向if (url == null) return null;// 如果重定向在http到https之間切換,需要檢查用戶是不是允許(默認允許)boolean sameScheme = url.scheme().equals(userResponse.request().url().scheme());if (!sameScheme && !client.followSslRedirects()) return null;// Most redirects don't include a request body.Request.Builder requestBuilder = userResponse.request().newBuilder();/*** 重定向請求中 只要不是 PROPFIND 請求,無論是POST還是其他的方法都要改為GET請求方式,* 即只有 PROPFIND 請求才能有請求體*///請求不是get與headif (HttpMethod.permitsRequestBody(method)) {final boolean maintainBody = HttpMethod.redirectsWithBody(method);// 除了 PROPFIND 請求之外都改成GET請求if (HttpMethod.redirectsToGet(method)) {requestBuilder.method("GET", null);} else {RequestBody requestBody = maintainBody ? userResponse.request().body() : null;requestBuilder.method(method, requestBody);}// 不是 PROPFIND 的請求,把請求頭中關于請求體的數據刪掉if (!maintainBody) {requestBuilder.removeHeader("Transfer-Encoding");requestBuilder.removeHeader("Content-Length");requestBuilder.removeHeader("Content-Type");}}// 在跨主機重定向時,刪除身份驗證請求頭if (!sameConnection(userResponse, url)) {requestBuilder.removeHeader("Authorization");}return requestBuilder.url(url).build();// 408 客戶端請求超時case HTTP_CLIENT_TIMEOUT:// 408 算是連接失敗了,所以判斷用戶是不是允許重試if (!client.retryOnConnectionFailure()) {// The application layer has directed us not to retry the request.return null;}// UnrepeatableRequestBody實際并沒發現有其他地方用到if (userResponse.request().body() instanceof UnrepeatableRequestBody) {return null;} // 如果是本身這次的響應就是重新請求的產物同時上一次之所以重請求還是因為408,那我們這次不再重請求if (userResponse.priorResponse() != null&& userResponse.priorResponse().code() == HTTP_CLIENT_TIMEOUT) {// We attempted to retry and got another timeout. Give up.return null;}return userResponse.request();// 503 服務不可用 和408差不多,但是只在服務器告訴你 Retry-After:0(意思就是立即重試) 才重請求case HTTP_UNAVAILABLE:if (userResponse.priorResponse() != null&& userResponse.priorResponse().code() == HTTP_UNAVAILABLE) {// We attempted to retry and got another timeout. Give up.return null;}if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {// specifically received an instruction to retry without delayreturn userResponse.request();}return null;default:return null;} }

    重定向的主要過程分析都在代理中注釋了,主要注意的一點是:followup在攔截器中定義的最大次數是20次。

    總結

    本攔截器是整個責任鏈上的第一環,這意味著它會是首次接觸到Request與最后接收到Response的角色,在這個攔截器中主要的功能就是判斷是否需要重試與重定向。

    重試的前提是出現了 RouteException 或者 IOException,一旦在后續的攔截器執行過程中出現了這兩個異常,就會通過recover方法進行判斷是否進行連接重試。

    重定向發生在重試的判定之后,如果不滿足重試的條件,還需要進一步調用followUpRequest根據Response的響應碼(當然,如果直接請求失敗, Response 都不存在就會拋出異常),來進行不同的重定向操作。注意:followup 最大發生20次

    橋接攔截器

    BridgeInterceptor ,連接應用程序和服務器的橋梁,我們發出的請求將會經過它的處理才能發給服務器,比如設置請求內容長度,編碼,gzip壓縮,cookie等,獲取響應后保存Cookie等操作。這個攔截器相對比較簡單。

    // 為我們補全請求頭,并默認使用gzip壓縮,同時將響應體同時設置為gzip讀取。 @Override public Response intercept(Chain chain) throws IOException {Request userRequest = chain.request();// 創建新的requestBuilderRequest.Builder requestBuilder = userRequest.newBuilder();RequestBody body = userRequest.body();if (body != null) {MediaType contentType = body.contentType();if (contentType != null) {requestBuilder.header("Content-Type", contentType.toString());}long contentLength = body.contentLength();if (contentLength != -1) {requestBuilder.header("Content-Length", Long.toString(contentLength));requestBuilder.removeHeader("Transfer-Encoding");} else {requestBuilder.header("Transfer-Encoding", "chunked");requestBuilder.removeHeader("Content-Length");}}if (userRequest.header("Host") == null) {requestBuilder.header("Host", hostHeader(userRequest.url(), false));}if (userRequest.header("Connection") == null) {// 默認建立長連接,如果我們不想與服務器建立長連接,value改為Close。requestBuilder.header("Connection", "Keep-Alive");}// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing// the transfer stream.boolean transparentGzip = false;if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {transparentGzip = true;requestBuilder.header("Accept-Encoding", "gzip");}List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());if (!cookies.isEmpty()) {requestBuilder.header("Cookie", cookieHeader(cookies));}if (userRequest.header("User-Agent") == null) {requestBuilder.header("User-Agent", Version.userAgent());}Response networkResponse = chain.proceed(requestBuilder.build());HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());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);String contentType = networkResponse.header("Content-Type");responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));}return responseBuilder.build(); }

    補全請求頭:

    請求頭說明
    Content-Type請求體類型,如: application/x-www-form-urlencoded
    Content-Length / Transfer-Encoding請求體解析方式
    Host請求的主機站點
    Connection: Keep-Alive保持長連接
    Accept-Encoding: gzip接受響應支持gzip壓縮
    Cookiecookie身份辨別
    User-Agent請求的用戶信息,如:操作系統、瀏覽器等

    在補全了請求頭后交給下一個攔截器處理,得到響應后,主要干兩件事情:

  • 保存cookie,在下次請求則會讀取對應的數據設置進入請求頭,默認的 CookieJar 不提供實現
  • 如果使用gzip返回的數據,則使用 GzipSource 包裝便于解析。
  • 總結

    橋接攔截器的執行邏輯主要就是以下幾點:

  • 對用戶構建的Request進行添加或者刪除相關頭部信息,以轉化成能夠正在進行網絡請求的Request。
  • 將符合網絡請求規范的Request交給下一個攔截器處理,并獲取Response,如果響應體經過了gzip壓縮,那就解壓縮,再構建成用戶可用的Response返回。
  • 接下來的緩存攔截器以及后續的分析會在《OkHttp源碼解析(下)》中體現。

    總結

    以上是生活随笔為你收集整理的OkHttp源码解析(上)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    三级av小说 | 亚洲色图激情文学 | 午夜久久影视 | 国产玖玖在线 | 久久综合中文字幕 | 国产午夜小视频 | 香蕉影院在线播放 | 五月天久久激情 | 国产高清在线观看 | 婷婷日 | av免费网页 | 成人在线黄色电影 | 国产精品久久久久久久久蜜臀 | 久久久国产精品视频 | 精品国产乱码久久久久久三级人 | 午夜狠狠干| 五月婷在线观看 | 在线观看国产一区 | 在线观看成人网 | 99在线热播精品免费 | 精品影院| 国产精品一区二区av麻豆 | 亚洲免费黄色 | 亚洲国产高清在线观看视频 | 99精品国产福利在线观看免费 | 91精彩视频在线观看 | 视频精品一区二区三区 | 黄色成人影院 | 欧美a在线看 | 国产九九九视频 | 69亚洲乱| 中文字幕最新精品 | 亚洲乱亚洲乱妇 | av播放在线 | 在线视频国产区 | 久久精品国产一区 | 成人免费网站视频 | 久久久精品综合 | 国产在线观看高清视频 | 成人精品一区二区三区电影免费 | 精品国产一区二区三区久久影院 | 美女视频又黄又免费 | 超薄丝袜一二三区 | 一级免费看视频 | 黄色三级网站 | 久久国产视频网站 | 国产精品美女在线 | 一区二区三区精品在线视频 | 中文视频在线播放 | 狠狠地操 | 国产精品久久久久久久久久东京 | 亚洲男男gaygay无套同网址 | 四虎永久网站 | 黄色av电影一级片 | 成人国产精品av | av免费电影网站 | 国产一级大片在线观看 | 国产aaa毛片| 中文字幕日韩免费视频 | 在线三级av | 性色av香蕉一区二区 | 五月色婷 | 成人久久综合 | 日韩精品一区二区三区免费观看视频 | 亚洲综合在线发布 | 亚洲欧洲成人 | 中文字幕乱码在线播放 | 久久有精品 | 日本黄色一级电影 | 久久久香蕉视频 | 国产成人一区二区三区 | 91免费的视频在线播放 | av免费在线观看1 | 日韩成人在线一区二区 | 国产精品日韩高清 | 中文字幕第一页在线 | 午夜视频导航 | 欧美久久久久久久久久 | 国产国语在线 | 开心婷婷色 | 亚洲国产精品成人精品 | 日韩a级黄色 | 国产在线一区二区三区播放 | www久久| 综合婷婷丁香 | av大片免费| 99超碰在线播放 | 欧美少妇18p | 国产在线中文字幕 | 日韩久久精品一区二区 | 国产四虎影院 | 欧美在线久久 | 黄色片网站 | 亚洲黄色网络 | 在线播放视频一区 | av中文天堂在线 | 在线91播放 | 国产日产在线观看 | 玖玖玖国产精品 | 久草视频免费播放 | 日批网站免费观看 | 日韩国产高清在线 | 五月激情电影 | 最近日韩中文字幕中文 | 韩国一区在线 | 天天做日日爱夜夜爽 | 国产精品第三页 | 激情综合一区 | a级片久久久 | 免费亚洲婷婷 | 国产 一区二区三区 在线 | 91桃色视频 | 成人久久网 | 9ⅰ精品久久久久久久久中文字幕 | 免费在线观看的av网站 | 偷拍视频一区 | 久精品在线观看 | 免费看污的网站 | 精品久久久久久亚洲综合网站 | 99视频在线免费看 | 国产手机视频 | 91人人干| 五月天中文字幕mv在线 | 亚洲精品日韩一区二区电影 | 亚洲欧美国内爽妇网 | 日韩乱码在线 | 国产在线观看91 | 美女视频黄免费的 | 一区二区精品在线观看 | 狠狠操在线 | 久久男人免费视频 | 涩涩网站在线观看 | 三级黄色免费 | 国内精品毛片 | 国产一区福利在线 | 国产伦精品一区二区三区无广告 | 国产无遮挡猛进猛出免费软件 | 超碰人人国产 | 97电影网站| 成年人免费av | 黄色资源网站 | 999电影免费在线观看2020 | 五月婷婷色播 | 国产一区免费在线 | 91色吧| 亚洲成av人影院 | 中文字幕丰满人伦在线 | av亚洲产国偷v产偷v自拍小说 | 欧美 日韩 性 | 狠狠做深爱婷婷综合一区 | 黄色成人在线观看 | 午夜国产福利在线观看 | 91字幕 | 欧美-第1页-屁屁影院 | 一区二区三区在线观看免费 | 1024手机在线看 | 美女久久久久 | 深夜免费福利在线 | 欧美综合在线观看 | 国产精品一区在线观看你懂的 | 97成人在线视频 | 九九久久成人 | 99国产在线观看 | 国产精品18久久久久久vr | 一区二区三区中文字幕在线观看 | 免费在线播放视频 | 国产精品二区在线观看 | 狠狠狠色丁香综合久久天下网 | 日本在线视频一区二区三区 | 亚洲尺码电影av久久 | 国产欧美综合在线观看 | 日韩视频一区二区三区在线播放免费观看 | 一区二区中文字幕在线观看 | 99视频在线免费 | 久久精品8 | 天天操夜夜爱 | 色吊丝在线永久观看最新版本 | 日韩电影一区二区三区 | 日日夜夜天天人人 | 99国产精品久久久久老师 | 91看片淫黄大片一级在线观看 | 丝袜护士aⅴ在线白丝护士 天天综合精品 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 天天草av| 91社区国产高清 | 国产精品久久久久久久毛片 | 国产日产av| 五月香婷| 18网站在线观看 | 黄色.com | 国产精品免费在线播放 | 成人一级影视 | 91精品国产九九九久久久亚洲 | 国产成人资源 | 免费三及片 | 成人精品久久 | 天天射天天操天天干 | 久久精品系列 | 亚洲高清激情 | 狠狠操狠狠 | 日韩特黄一级欧美毛片特黄 | 正在播放五月婷婷狠狠干 | 亚洲最新av | 91精品久久久久久综合乱菊 | 精品在线观 | 欧美日韩视频在线 | 亚洲精品视频大全 | 91中文字幕| 天天干,天天射,天天操,天天摸 | .国产精品成人自产拍在线观看6 | 日本久久精 | 欧美激情视频在线免费观看 | 免费午夜视频在线观看 | 996久久国产精品线观看 | 国产一区网 | 九九色综合 | 日韩欧美在线一区 | 日韩三区在线观看 | 日本黄色免费在线 | 香蕉视频在线看 | 中文字幕亚洲字幕 | 免费久久99精品国产婷婷六月 | 四虎精品成人免费网站 | 国产午夜精品理论片在线 | 亚洲视频在线视频 | 国产一区二区三区免费在线 | 肉色欧美久久久久久久免费看 | 丁香久久综合 | 国产又粗又猛又黄视频 | 国产精品99精品 | 国产一区二区手机在线观看 | 456免费视频| 中文字幕av影院 | 欧美淫视频 | 久久综合九色综合久99 | 久草在线91 | 婷婷丁香九月 | 日韩免费高清在线 | 亚洲综合色av | 亚欧日韩成人h片 | 欧美日韩精品在线免费观看 | 乱男乱女www7788 | 国产精品免费一区二区三区在线观看 | av亚洲产国偷v产偷v自拍小说 | 国产精品一区二区三区电影 | 在线黄色免费av | 国产精品中文久久久久久久 | 99精品免费视频 | 亚洲欧美日本国产 | 手机av电影在线 | 亚洲精品国偷拍自产在线观看 | 最新在线你懂的 | 久久久国产精品免费 | 精品视频一区在线观看 | 天天看天天干 | 一区二区伦理电影 | 欧美 激情在线 | 色视频在线免费观看 | 亚洲精品在线网站 | av资源免费观看 | 色综合久久中文字幕综合网 | 精品一区在线看 | 999视频网| 毛片在线网 | 91精品视频免费看 | 久久专区| 国内精品久久久久影院男同志 | 欧美精品三级在线观看 | 日韩免费一区二区在线观看 | 999色视频 | 又黄又爽又湿又无遮挡的在线视频 | 黄色特级片 | 精品视频免费在线 | 欧美激情视频久久 | 久久视频这里有久久精品视频11 | 日韩国产精品久久久久久亚洲 | 国产精品扒开做爽爽的视频 | 亚洲精品在线视频网站 | 五月婷婷综合在线观看 | 91久久精品一区二区三区 | 特黄一级毛片 | 国产精品毛片久久久久久久 | 99久久久国产精品 | 久久www免费视频 | 久久久久久美女 | 午夜久久久久久久 | 亚洲精品综合一区二区 | 在线观看亚洲电影 | 日本中文字幕观看 | 国产精品久久久久久久电影 | 亚洲播播| va视频在线观看 | 又粗又长又大又爽又黄少妇毛片 | 亚洲一级片免费观看 | 午夜久久久影院 | 亚洲电影黄色 | 综合网伊人| 国产美女视频 | 成人午夜剧场在线观看 | 91人人揉日日捏人人看 | 色福利网站 | 91麻豆精品国产91久久久无限制版 | 国产精品欧美日韩 | 毛片无卡免费无播放器 | 中日韩在线视频 | 亚洲日本国产精品 | 国产高清在线免费 | 国产一级一片免费播放放a 一区二区三区国产欧美 | 人人精品久久 | 日韩免费中文 | 国产一区二区视频在线 | 午夜影院三级 | www天天干com| 国产高清av | 不卡视频在线看 | 中文字幕日韩在线播放 | 91成人在线视频 | 91精品国产一区 | 国产人成看黄久久久久久久久 | 亚洲欧美日韩国产一区二区三区 | 日日夜夜狠狠操 | 九月婷婷人人澡人人添人人爽 | 青青河边草免费直播 | 亚洲国产欧美在线看片xxoo | 精品电影一区二区 | 国产精品片 | 中文字幕一区二区三区四区视频 | 久久桃花网 | 国产91探花 | 天堂av最新网址 | 国产成人在线免费观看 | 国产在线观看你懂得 | 992tv在线观看 | 国产精品乱码在线 | 久久久久五月天 | av电影在线不卡 | 天堂av观看| 精品一区二区免费在线观看 | 久久精品福利视频 | 日韩一区二区免费在线观看 | 五月天久久久久久 | 欧美精品久久久久久久久久白贞 | 久久影院中文字幕 | 久久国产影院 | 欧美日韩国产精品久久 | 婷婷丁香六月天 | 日本护士三级少妇三级999 | 亚洲影视九九影院在线观看 | 欧美另类激情 | 99一区二区三区 | 91在线区| 日韩网站一区 | 美女久久 | 黄色三级免费网址 | 国产不卡在线看 | 视频福利在线 | 亚洲永久精品一区 | 国产精品永久久久久久久久久 | 九九九九九九精品任你躁 | 欧洲亚洲国产视频 | 99久久综合国产精品二区 | 精品国产诱惑 | 福利一区视频 | 91传媒免费观看 | 亚洲一级在线观看 | 日韩中文字幕网站 | 伊人资源视频在线 | 久久av在线播放 | 四虎在线观看视频 | 久久露脸国产精品 | 久久久久福利视频 | 日韩黄在线观看 | 免费人成网| 狠狠狠综合 | 国产成人久久av免费高清密臂 | 99精品福利视频 | 中文字幕色在线 | 国产精品一二 | 99热在线观看免费 | 久久全国免费视频 | 国产精品美女久久久久久免费 | 日本三级久久久 | 日日摸日日碰 | 又黄又爽又无遮挡的视频 | 久久综合色一综合色88 | 91丨九色丨国产丨porny精品 | 国产精品麻豆欧美日韩ww | 国产精品久久久久三级 | 在线观看中文字幕网站 | 最新av网址大全 | 婷婷丁香色 | 亚洲资源在线网 | 97久久精品午夜一区二区 | 一本—道久久a久久精品蜜桃 | 日韩欧美xxxx | 国产涩图 | 在线观看久久久久久 | 久热色超碰| 国产一区免费在线观看 | 国产精品成人国产乱 | 免费在线观看视频一区 | 欧美乱淫视频 | 中文字幕在线影视资源 | 日本久久久精品视频 | 久久久久久久网 | 国产精品com | 免费看片成年人 | 亚洲精品在线观看中文字幕 | 久久久久久久免费看 | 91在线精品播放 | 亚洲精品国精品久久99热一 | 亚洲欧洲久久久 | 日韩色中色 | 欧美日韩中文字幕在线视频 | 97av超碰| 色婷婷综合久久久 | 黄色av观看 | www.国产在线观看 | 99在线观看免费视频精品观看 | 精品一区二区三区四区在线 | 国内久久久久 | 国产精品久久久毛片 | 一区二区三区免费在线观看 | www.夜夜干.com | 免费高清在线视频一区· | 夜夜婷婷 | 狠狠狠狠狠狠天天爱 | 免费观看国产视频 | 国产在线精品区 | 中文字幕一区二区三区久久 | 国产精品久久久网站 | 亚洲欧美一区二区三区孕妇写真 | 我要色综合天天 | 精品国产午夜 | 91av视频在线免费观看 | 亚洲乱亚洲乱妇 | 在线免费观看国产精品 | 国产美腿白丝袜足在线av | 久草9视频| 日韩精品高清不卡 | 国产一级免费在线 | 999久久久免费精品国产 | 成年人黄色免费网站 | 91精品视频在线免费观看 | 亚洲无吗视频在线 | av再线观看 | 中文字幕麻豆 | 在线欧美小视频 | 国产亚洲一级高清 | 欧美激情另类文学 | 国产无吗一区二区三区在线欢 | 四虎国产精品免费 | 在线观看av网| 国产色黄网站 | 91av蜜桃 | 在线免费观看视频一区二区三区 | 国产一区二区在线免费观看 | 日韩女同av | 欧美视频国产视频 | 91黄视频在线观看 | 99精品偷拍视频一区二区三区 | 久久久久国产精品免费网站 | 欧美激情视频一区二区三区 | 天海冀一区二区三区 | 天天色影院| 97伊人网| 日韩av手机在线观看 | 午夜视频一区二区三区 | 在线电影 一区 | 日日爽夜夜操 | 国产日女人 | 久久国产免费 | 超碰97中文 | 91最新视频 | 中日韩免费视频 | 一本一本久久a久久精品综合妖精 | 在线观看视频精品 | 18+视频网站链接 | 国产亚洲精品福利 | 亚洲在线网址 | 日韩成人不卡 | 精品毛片一区二区免费看 | 24小时日本在线www免费的 | 久久久久免费网 | 精品毛片一区二区免费看 | 国产精品永久在线 | 午夜色场| 日韩肉感妇bbwbbwbbw | 四虎免费av| 国产精品久久久久av免费 | 国产在线不卡视频 | 91.精品高清在线观看 | 国产高清视频免费 | a黄在线观看 | 91av在线视频播放 | 91久久在线观看 | 探花视频在线观看免费 | 久久久久久高潮国产精品视 | 91精品国产入口 | 五月婷婷丁香色 | 日韩一区在线播放 | 正在播放亚洲精品 | 欧美另类性 | 亚洲激精日韩激精欧美精品 | 久久这里只有精品首页 | 精品国内自产拍在线观看视频 | 一本之道乱码区 | 国产一级精品视频 | 99这里有精品 | 最新av网址在线观看 | 男女激情麻豆 | 国产欧美中文字幕 | 免费看的视频 | av综合站 | 国产精品18久久久久久首页狼 | 国产精品久久久久久久久久 | 成年人在线免费视频观看 | 美女网站在线 | 最近免费中文字幕大全高清10 | 日韩婷婷 | 园产精品久久久久久久7电影 | 国产免费观看视频 | 人人澡人人干 | 亚洲国产精品成人综合 | 亚洲乱亚洲乱妇 | 国产麻豆精品久久 | 国产精品一区二区三区免费视频 | 蜜桃视频在线观看一区 | 精品国产观看 | 婷婷丁香花五月天 | 五月婷婷欧美 | 亚洲毛片久久 | 国产一区二区三区久久久 | 亚洲h在线播放在线观看h | 99久高清在线观看视频99精品热在线观看视频 | 亚洲国产精品一区二区尤物区 | 精品国产电影一区二区 | 国产亚洲在线视频 | 中日韩男男gay无套 日韩精品一区二区三区高清免费 | 国产精品欧美一区二区 | 久久9999久久免费精品国产 | 黄色av电影免费观看 | 国产 在线 高清 精品 | 91精品欧美一区二区三区 | 国产日韩欧美在线播放 | 久久视频中文字幕 | 日韩网站免费观看 | 97色视频在线 | 992tv人人草 黄色国产区 | 国产精品99久久久久久久久久久久 | 香蕉网在线播放 | 人人爱人人添 | 久久久久免费电影 | 午夜久久久久久久久久久 | 激情五月六月婷婷 | 国产精品一区二区麻豆 | 免费一级片观看 | 国产亚洲欧美精品久久久久久 | 免费在线观看成人av | 精品久久久久久久久亚洲 | 日韩在线免费 | 亚洲草视频| 国产精品一区二区电影 | 天天干天天天天 | 国产色啪 | 成人av动漫在线观看 | 国产性xxxx| 国产视频久久久久 | 日本三级久久 | 国产福利一区二区三区在线观看 | 深爱激情站 | 欧美91精品| 亚洲精品av在线 | 国产精品久久久久久欧美 | 国产一级黄色av | a久久免费视频 | av天天澡天天爽天天av | 精品国产成人在线影院 | 欧美韩日在线 | 欧美精品生活片 | 欧美日韩视频观看 | 亚洲成人黄 | 麻豆va一区二区三区久久浪 | 成年人视频在线免费观看 | 九九免费在线观看 | 色五丁香 | 欧美少妇xxx | 免费一级特黄录像 | 51久久夜色精品国产麻豆 | 亚洲aⅴ久久精品 | 久久久久亚洲精品国产 | 亚洲天堂首页 | 免费看三级 | 国产一区二区在线免费播放 | 国产日韩欧美在线影视 | 免费精品人在线二线三线 | 国产精品久久久久久久久久三级 | 99久久精品国产系列 | 成人黄色影片在线 | 亚洲国产成人精品在线观看 | 又长又大又黑又粗欧美 | 国产无限资源在线观看 | 亚洲 成人 欧美 | 国模一区二区三区四区 | 91福利视频免费观看 | 久久久久亚洲精品成人网小说 | 精品字幕在线 | 国产一区黄色 | 久久久久久美女 | 国产福利一区二区三区视频 | 9草在线| 一本到在线 | 欧美日韩国产免费视频 | 亚洲精品国产麻豆 | 激情丁香久久 | 97视频在线观看网址 | 手机看片中文字幕 | 日韩h在线观看 | 国产高清中文字幕 | 四虎免费在线观看视频 | 日本激情视频中文字幕 | 色网站视频| 精品国产乱码久久久久久久 | 欧美日韩在线观看一区二区 | 亚洲视频在线看 | 有码中文字幕在线观看 | 999成人国产 | 超碰伊人网| 欧美最猛性xxxxx免费 | 91麻豆精品国产午夜天堂 | 国产精品亚洲精品 | 久久久久久久久黄色 | 中文字幕免费在线看 | 日韩高清激情 | 久久久午夜视频 | 国产一级精品绿帽视频 | 国产区在线视频 | 亚洲精品欧美精品 | 亚洲日本精品 | 99久久久久国产精品免费 | 久久不卡日韩美女 | 久久福利小视频 | 日日天天狠狠 | 蜜臀久久99精品久久久酒店新书 | 99一区二区三区 | 在线看av网址 | 亚洲精品日韩av | 三上悠亚在线免费 | 99在线精品视频观看 | 操综合 | 日本中文字幕在线 | 国产视频一区在线免费观看 | 一区二区精品在线观看 | 91免费的视频在线播放 | 中文字幕丝袜 | 午夜久久久久久久久久久 | 一区二区欧美在线观看 | 青青色影院 | 国产第页 | 一区二区三区电影大全 | 波多野结衣视频网址 | 亚洲综合在线播放 | 亚洲精品自在在线观看 | 99视频这里只有 | 五月婷婷久久丁香 | 91成人在线观看喷潮 | 久久久久久久久久久电影 | 91刺激视频| 免费在线黄色av | 久久久久久激情 | 国产一区二区高清不卡 | 色视频在线免费 | 中文字幕人成人 | 99久久精品免费看国产一区二区三区 | 91视频 - x99av | 最近日本中文字幕a | 欧美aaa级片| 激情网在线观看 | www.日韩免费 | 婷婷在线视频观看 | 欧洲激情综合 | 免费观看v片在线观看 | 婷婷在线五月 | 中国美女一级看片 | 免费黄av| 亚洲作爱| 亚洲一区精品二人人爽久久 | 天堂av在线网 | 国产精品美女久久久久久久网站 | 综合久久久 | 亚洲理论片 | 亚洲撸撸 | 正在播放 国产精品 | 天天玩夜夜操 | 亚洲天天在线日亚洲洲精 | 久久日本视频 | 摸阴视频 | 国产1级视频| avhd高清在线谜片 | 久久久蜜桃一区二区 | 国产婷婷久久 | 国产精品av久久久久久无 | 成片视频免费观看 | 人人爽人人爽人人爽学生一级 | 99色婷婷 | 日韩免费精品 | 激情av综合 | 成人av网站在线 | 国产无套精品久久久久久 | 中文字幕在线影院 | 日本公妇在线观看 | 婷婷日日 | 国产精品久久久久久一区二区三区 | 精品视频www | 91九色蝌蚪视频 | 欧美国产三区 | 91精品视频免费在线观看 | 国产精品免费麻豆入口 | 婷色在线 | 久久久精品国产免费观看一区二区 | 999视频在线观看 | 69视频在线播放 | 亚洲激情网站免费观看 | 亚洲天堂网在线视频 | 精品国产伦一区二区三区观看说明 | 正在播放久久 | 色综合久久精品 | 免费成人av在线 | 视频直播国产精品 | 91成人破解版 | 免费国产视频 | 国产精品久久久久久久久久三级 | 久久激情视频网 | 91麻豆精品国产午夜天堂 | 在线电影播放 | 麻豆成人在线观看 | 天天插日日射 | 午夜久久影视 | 中文字幕精品一区二区三区电影 | 国产精品嫩草影院9 | 国产91av视频在线观看 | 麻豆视频免费在线 | 久久中文字幕导航 | 免费在线观看一区二区三区 | 亚洲资源在线 | 精品视频123区在线观看 | 国产视频网站在线观看 | 久草资源免费 | 国产精品国产三级国产aⅴ9色 | 国产美女在线免费观看 | 国产精品一区二区在线 | 国产精品久久久久久久午夜片 | 日韩精品中文字幕av | 免费视频91蜜桃 | 国产精品国产三级国产 | 国产成人久久精品 | 国产精品久久久久一区二区三区 | 成人午夜在线电影 | 日韩av高清在线观看 | 国产伦理一区二区 | 国产精品美女免费看 | 伊人影院得得 | 国内偷拍精品视频 | 国产在线高清 | 国产精品久久久久一区 | 久久国产热 | 欧美人操人 | 一级国产视频 | 天堂久久电影网 | 亚洲日本中文字幕在线观看 | 亚洲国产免费网站 | 婷婷婷国产在线视频 | 蜜臀av麻豆 | 久久不卡av | 一区二区三区国产欧美 | 91精品免费在线 | 五月天中文在线 | 亚洲精品乱码久久久久久蜜桃91 | 男女激情网址 | 91精品推荐| 精品国产综合区久久久久久 | 免费日韩精品 | 黄色大片国产 | 日韩av快播电影网 | 久久国产精品99国产精 | 另类五月激情 | 午夜精品导航 | 免费网站污 | 久久国产视屏 | 97精品欧美91久久久久久 | 亚洲最大成人网4388xx | 狠狠操狠狠干2017 | 国产精品国产亚洲精品看不卡 | 亚洲天堂社区 | 久久大片网站 | 免费日韩 精品中文字幕视频在线 | 成人禁用看黄a在线 | 天天激情综合网 | 香蕉影院在线播放 | 91精彩视频在线观看 | 91免费在线看片 | 色婷婷综合视频在线观看 | 成人av网页 | 蜜臀av夜夜澡人人爽人人桃色 | 99精品国产99久久久久久福利 | 五月天狠狠操 | 天天天天天天天天操 | 日韩成人精品一区二区三区 | 成人久久精品 | 狠狠色丁香婷婷综合 | 91成品人影院 | 国产成人免费在线 | 日韩精品一区二区三区在线播放 | av在线影视 | 日韩欧美第二页 | 在线中文视频 | 韩国av免费观看 | 久久精品99国产精品酒店日本 | 在线免费观看麻豆 | 婷五月激情 | 又黄又爽免费视频 | 久久久久亚洲精品男人的天堂 | 国产中文字幕网 | 日韩久久久 | 亚洲日本欧美 | 精品视频久久久久久 | 久久精品一区二区三 | 国产成人精品电影久久久 | 国内一级片在线观看 | 91av免费看 | 亚洲精品免费观看视频 | 亚洲成人黄色av | 97av免费视频 | 欧美精品v国产精品v日韩精品 | 日韩特级毛片 | 久久久精品高清 | 欧美午夜久久久 | 久久国产综合视频 | 在线视频麻豆 | 欧美精品久久久久久久亚洲调教 | 97精品国产97久久久久久久久久久久 | 日韩二区三区在线观看 | 在线免费看黄色 | 免费在线一区二区三区 | 91久草视频 | 久久欧美在线电影 | 日韩精品黄 | 国产一卡久久电影永久 | 亚洲最新av在线网站 | 成人av中文字幕在线观看 | 黄色三级免费片 | 天天干夜夜爱 | 就要干b | 久久成人欧美 | 久久婷婷国产色一区二区三区 | 精品av网站 | 国产一区二区三区免费在线观看 | 一区二区精品久久 | 日韩高清av | 国内精品久久久久国产 | 久久久久久亚洲精品 | 午夜精品一区二区三区在线视频 | 你操综合 | 伊香蕉大综综综合久久啪 | 久久久国产毛片 | 在线免费看黄色 | 一区三区视频 | 91香蕉视频好色先生 | 91看片麻豆| 欧美日韩亚洲第一 | 亚洲国产片| 在线观看视频在线观看 | 日韩成人精品在线观看 | 国产亚洲精品久久19p | 91禁在线观看 | 色综合夜色一区 | 久久久久久久久久久久久久免费看 | 日韩久久精品一区二区三区下载 | 在线观看视频黄 | av先锋影音少妇 | 亚洲精品一区二区三区在线观看 | 日韩电影中文 | 午夜av免费看 | 久久国产午夜精品理论片最新版本 | 狠狠做六月爱婷婷综合aⅴ 日本高清免费中文字幕 | 免费看三级 | 在线之家免费在线观看电影 | 亚洲影院色 | 在线 视频 一区二区 | 丰满少妇在线观看 | 在线黄网站 | 久久久久www | 91麻豆精品一区二区三区 | 九九热在线播放 | 色偷偷88888欧美精品久久久 | 久久久久国产精品免费免费搜索 | 国产美女久久久 | 国产精品女人久久久久久 | 欧洲精品在线视频 | 亚洲精品日韩一区二区电影 | 国产老熟 | 麻豆va一区二区三区久久浪 | 国产精品一区二区久久久久 | a在线播放 | 国产在线综合视频 | 日韩中文字幕一区 | 欧美少妇xx | 精品久久久久久久久久久久 | 波多野结衣视频一区二区 | 久久久国产精品一区二区三区 | 婷婷深爱 | 97超碰网| 99精品国产福利在线观看免费 | 日韩色高清 | 日本一区二区三区免费看 | 欧美黑人性猛交 | 欧产日产国产69 | www亚洲视频 | 婷婷五月在线视频 | 婷婷在线不卡 | 日韩免费视频网站 | 国产精品 亚洲精品 | 日韩精品久久久免费观看夜色 | 亚洲国产日韩欧美在线 | 免费视频91 | 九九视频这里只有精品 | 精品二区视频 | 午夜精品导航 | 色吊丝在线永久观看最新版本 | 激情网色 | 青草草在线视频 | 激情丁香在线 | 美女视频永久黄网站免费观看国产 | 黄色软件视频网站 | 97国产在线观看 | 午夜在线免费观看视频 | 日本在线观看一区 | 丁香导航 | 成人三级网址 | 在线观看成人国产 | 国产精品久久一区二区无卡 | 国产黄色一级大片 | 亚洲国产精彩中文乱码av | 亚洲人成影院在线 | www操操 | 国产福利久久 | www成人av | 91欧美日韩国产 | 在线观看免费观看在线91 | av网站地址| 日韩成人免费电影 | 欧美日韩国产二区 | 99精品在线免费视频 | 欧美日韩啪啪 | 国产精品麻 | 国内亚洲精品 | 天天操天天干天天操天天干 | 在线黄色av电影 | 成年人免费在线播放 | 99精品国产99久久久久久97 | 中文字幕在线视频一区 | 在线视频你懂得 | 久久激情电影 | 99国产视频 | 97视频播放 | 色av男人的天堂免费在线 | 国产aaa毛片 | 国产小视频在线播放 | 成人精品视频久久久久 | 麻豆一二 | 免费a v在线 | 在线电影91 | www免费黄色 | 亚洲高清色综合 | 香蕉久草 | 亚洲一片黄 | 日韩欧美99| 91尤物国产尤物福利在线播放 | 久久这里只精品 | 亚洲成人av影片 | 国产做a爱一级久久 | 天天综合网在线 | 免费观看性生交大片3 | 极品中文字幕 | 中文字幕婷婷 | 欧美日产在线观看 | 欧美乱大交 | 二区视频在线 | www.久久久com | 在线精品视频免费播放 | 久久精品99久久久久久2456 | 福利视频一二区 | 五月婷婷综合网 | 69绿帽绿奴3pvideos | 韩国在线视频一区 | 人人舔人人爱 | 国产经典三级 | 91精品导航 |