<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                [TOC] 前篇我們詳細地分析了OKHttp的核心源碼,如果對OKHttp內部機制不了解的可以看看[Android主流三方庫源碼分析(一、深入理解OKHttp源碼)](https://jsonchao.github.io/2018/12/01/Android%E4%B8%BB%E6%B5%81%E4%B8%89%E6%96%B9%E5%BA%93%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%EF%BC%88%E4%B8%80%E3%80%81%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3OKHttp%E6%BA%90%E7%A0%81%EF%BC%89/)。這篇,將會來深入地分析下目前Android最優秀的網絡封裝框架Retrofit的源碼流程。 ### 一、基本使用流程 #### 1、定義HTTP API,用于描述請求 ~~~ public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user); } ~~~ #### 2、創建Retrofit并生成API的實現(注意:方法上面的注解表示請求的接口部分,返回類型是請求的返回值類型,方法的參數即是請求的參數) ~~~ // 1.Retrofit構建過程 Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); // 2.創建網絡請求接口類實例過程 GitHubService service = retrofit.create(GitHubService.class); ~~~ #### 3、調用API方法,生成Call,執行請求 ~~~ // 3.生成并執行請求過程 Call<List<Repo>> repos = service.listRepos("octocat"); repos.execute() or repos.enqueue() ~~~ Retrofit的基本使用流程很簡潔,但是簡潔并不代表簡單,Retrofit為了實現這種簡潔的使用流程,內部使用了優秀的架構設計和大量的設計模式,在我分析過Retrofit最新版的源碼和大量優秀的Retrofit源碼分析文章后,我發現,要想真正理解Retrofit內部的核心源碼流程和設計思想,首先,需要對這九大設計模式有一定的了解,如下: ~~~ 1.Retrofit構建過程 建造者模式、工廠方法模式 2.創建網絡請求接口實例過程 外觀模式、代理模式、單例模式、策略模式、裝飾模式(建造者模式) 3.生成并執行請求過程 適配器模式(代理模式、裝飾模式) ~~~ 其次,需要對OKHttp源碼有一定的了解,如果不了解的可以看看這篇[Android主流三方庫源碼分析(一、深入理解OKHttp源碼)](https://jsonchao.github.io/2018/12/01/Android%E4%B8%BB%E6%B5%81%E4%B8%89%E6%96%B9%E5%BA%93%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%EF%BC%88%E4%B8%80%E3%80%81%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3OKHttp%E6%BA%90%E7%A0%81%EF%BC%89/)。最后,讓我們按以上流程去深入Retrofit源碼內部,領悟它帶給我們的**設計之美**。 ### 二、Retrofit構建過程 #### 1、Retrofit核心對象解析 首先Retrofit中有一個全局變量非常關鍵,在V2.5之前的版本,使用的是LinkedHashMap(),它是一個網絡請求配置對象,是由網絡請求接口中方法注解進行解析后得到的。 ~~~ public final class Retrofit { // 網絡請求配置對象,存儲網絡請求相關的配置,如網絡請求的方法、數據轉換器、網絡請求適配器、網絡請求工廠、基地址等 private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>(); ~~~ Retrofit使用了建造者模式通過內部類Builder類建立一個Retrofit實例,如下: ~~~ public static final class Builder { // 平臺類型對象(Platform -> Android) private final Platform platform; // 網絡請求工廠,默認使用OkHttpCall(工廠方法模式) private @Nullable okhttp3.Call.Factory callFactory; // 網絡請求的url地址 private @Nullable HttpUrl baseUrl; // 數據轉換器工廠的集合 private final List<Converter.Factory> converterFactories = new ArrayList<>(); // 網絡請求適配器工廠的集合,默認是ExecutorCallAdapterFactory private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(); // 回調方法執行器,在 Android 上默認是封裝了 handler 的 MainThreadExecutor, 默認作用是:切換線程(子線程 -> 主線程) private @Nullable Executor callbackExecutor; // 一個開關,為true則會緩存創建的ServiceMethod private boolean validateEagerly; ~~~ #### 2、Builder內部構造 下面看看Builder內部構造做了什么。 ~~~ public static final class Builder { ... Builder(Platform platform) { this.platform = platform; } public Builder() { this(Platform.get()); } ... } class Platform { private static final Platform PLATFORM = findPlatform(); static Platform get() { return PLATFORM; } private static Platform findPlatform() { try { // 使用JVM加載類的方式判斷是否是Android平臺 Class.forName("android.os.Build"); if (Build.VERSION.SDK_INT != 0) { return new Android(); } } catch (ClassNotFoundException ignored) { } try { // 同時支持Java平臺 Class.forName("java.util.Optional"); return new Java8(); } catch (ClassNotFoundException ignored) { } return new Platform(); } static class Android extends Platform { ... @Override public Executor defaultCallbackExecutor() { //切換線程(子線程 -> 主線程) return new MainThreadExecutor(); } // 創建默認的網絡請求適配器工廠,如果是Android7.0或Java8上,則使 // 用了并發包中的CompletableFuture保證了回調的同步 // 在Retrofit中提供了四種CallAdapterFactory(策略模式): // ExecutorCallAdapterFactory(默認)、GuavaCallAdapterFactory、 // va8CallAdapterFactory、RxJavaCallAdapterFactory @Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories( @Nullable Executor callbackExecutor) { if (callbackExecutor == null) throw new AssertionError(); ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor); return Build.VERSION.SDK_INT >= 24 ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory) : singletonList(executorFactory); } ... @Override List<? extends Converter.Factory> defaultConverterFactories() { return Build.VERSION.SDK_INT >= 24 ? singletonList(OptionalConverterFactory.INSTANCE) : Collections.<Converter.Factory>emptyList(); } ... static class MainThreadExecutor implements Executor { // 獲取Android 主線程的Handler private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { // 在UI線程對網絡請求返回數據處理 handler.post(r); } } } ~~~ 可以看到,在Builder內部構造時設置了默認Platform、callAdapterFactories和callbackExecutor。 #### 3、添加baseUrl 很簡單,就是將String類型的url轉換為OkHttp的HttpUrl過程如下: ~~~ /** * Set the API base URL. * * @see #baseUrl(HttpUrl) */ public Builder baseUrl(String baseUrl) { checkNotNull(baseUrl, "baseUrl == null"); return baseUrl(HttpUrl.get(baseUrl)); } public Builder baseUrl(HttpUrl baseUrl) { checkNotNull(baseUrl, "baseUrl == null"); List<String> pathSegments = baseUrl.pathSegments(); if (!"".equals(pathSegments.get(pathSegments.size() - 1))) { throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl); } this.baseUrl = baseUrl; return this; } ~~~ #### 4、添加GsonConverterFactory 首先,看到GsonConverterFactory.creat()的源碼。 ~~~ public final class GsonConverterFactory extends Converter.Factory { public static GsonConverterFactory create() { return create(new Gson()); } public static GsonConverterFactory create(Gson gson) { if (gson == null) throw new NullPointerException("gson == null"); return new GsonConverterFactory(gson); } private final Gson gson; // 創建了一個含有Gson對象實例的GsonConverterFactory private GsonConverterFactory(Gson gson) { this.gson = gson; } ~~~ 然后,看看addConverterFactory()方法內部。 ~~~ public Builder addConverterFactory(Converter.Factory factory) { converterFactories.add(checkNotNull(factory, "factory null")); return this; } ~~~ 可知,這一步是將一個含有Gson對象實例的GsonConverterFactory放入到了數據轉換器工廠converterFactories里。 #### 5、build過程 ~~~ public Retrofit build() { if (baseUrl == null) { throw new IllegalStateException("Base URL required."); } okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { // 默認使用okhttp callFactory = new OkHttpClient(); } Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { // Android默認的callbackExecutor callbackExecutor = platform.defaultCallbackExecutor(); } // Make a defensive copy of the adapters and add the defaultCall adapter. List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories); // 添加默認適配器工廠在集合尾部 callAdapterFactories.addAll(platform.defaultCallAdapterFactorisca llbackExecutor)); // Make a defensive copy of the converters. List<Converter.Factory> converterFactories = new ArrayList<>( 1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize()); // Add the built-in converter factory first. This prevents overriding its behavior but also // ensures correct behavior when using converters thatconsumeall types. converterFactories.add(new BuiltInConverters()); converterFactories.addAll(this.converterFactories); converterFactories.addAll(platform.defaultConverterFactories(); return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories), unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly); } ~~~ 可以看到,最終我們在Builder類中看到的6大核心對象都已經配置到Retrofit對象中了。 ### 三、創建網絡請求接口實例過程 retrofit.create()使用了外觀模式和代理模式創建了網絡請求的接口實例,我們分析下create方法。 ~~~ public <T> T create(final Class<T> service) { Utils.validateServiceInterface(service); if (validateEagerly) { // 判斷是否需要提前緩存ServiceMethod對象 eagerlyValidateMethods(service); } // 使用動態代理拿到請求接口所有注解配置后,創建網絡請求接口實例 return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); private final Object[] emptyArgs = new Object[0]; @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { // If the method is a method from Object then defer to normal invocation. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } return loadServiceMethod(method).invoke(args != null ? args : emptyArgs); } }); } private void eagerlyValidateMethods(Class<?> service) { Platform platform = Platform.get(); for (Method method : service.getDeclaredMethods()) { if (!platform.isDefaultMethod(method)) { loadServiceMethod(method); } } } ~~~ 繼續看看loadServiceMethod的內部流程 ~~~ ServiceMethod<?> loadServiceMethod(Method method) { ServiceMethod<?> result = serviceMethodCache.get(method); if (result != null) return result; synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { // 解析注解配置得到了ServiceMethod result = ServiceMethod.parseAnnotations(this, method); // 可以看到,最終加入到ConcurrentHashMap緩存中 serviceMethodCache.put(method, result); } } return result; } abstract class ServiceMethod<T> { static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) { // 通過RequestFactory解析注解配置(工廠模式、內部使用了建造者模式) RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); Type returnType = method.getGenericReturnType(); if (Utils.hasUnresolvableType(returnType)) { throw methodError(method, "Method return type must not include a type variable or wildcard: %s", returnType); } if (returnType == void.class) { throw methodError(method, "Service methods cannot return void."); } // 最終是通過HttpServiceMethod構建的請求方法 return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); } abstract T invoke(Object[] args); } ~~~ #### 以下為請求構造核心流程 根據RequestFactory#Builder構造方法和parseAnnotations方法的源碼,可知的它的作用就是用來解析注解配置的。 ~~~ Builder(Retrofit retrofit, Method method) { this.retrofit = retrofit; this.method = method; // 獲取網絡請求接口方法里的注釋 this.methodAnnotations = method.getAnnotations(); // 獲取網絡請求接口方法里的參數類型 this.parameterTypes = method.getGenericParameterTypes(); // 獲取網絡請求接口方法里的注解內容 this.parameterAnnotationsArray = method.getParameterAnnotations(); } ~~~ 接著看HttpServiceMethod.parseAnnotations()的內部流程。 ~~~ static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) { //1.根據網絡請求接口方法的返回值和注解類型, // 從Retrofit對象中獲取對應的網絡請求適配器 CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit,method); // 得到響應類型 Type responseType = callAdapter.responseType(); ... //2.根據網絡請求接口方法的返回值和注解類型從Retrofit對象中獲取對應的數據轉換器 Converter<ResponseBody, ResponseT>responseConverter = createResponseConverter(retrofit,method, responseType); okhttp3.Call.Factory callFactory = retrofit.callFactory; return newHttpServiceMethod<>(requestFactory, callFactory, callAdapter,responseConverter); } ~~~ ##### 1.createCallAdapter(retrofit, method) ~~~ private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter( Retrofit retrofit, Method method) { // 獲取網絡請求接口里方法的返回值類型 Type returnType = method.getGenericReturnType(); // 獲取網絡請求接口接口里的注解 Annotation[] annotations = method.getAnnotations(); try { //noinspection unchecked return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw methodError(method, e, "Unable to create call adapter for %s", returnType); } } public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) { return nextCallAdapter(null, returnType, annotations); } public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) { ... int start = callAdapterFactories.indexOf(skipPast) + 1; // 遍歷 CallAdapter.Factory 集合尋找合適的工廠 for (int i = start, count = callAdapterFactories.size(); i <count; i++) { CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this); if (adapter != null) { return adapter; } } } ~~~ ##### 2.createResponseConverter(Retrofit retrofit, Method method, Type responseType) ~~~ private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter( Retrofit retrofit, Method method, Type responseType) { Annotation[] annotations = method.getAnnotations(); try { return retrofit.responseBodyConverter(responseType,annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw methodError(method, e, "Unable to create converter for%s", responseType); } } public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) { return nextResponseBodyConverter(null, type, annotations); } public <T> Converter<ResponseBody, T> nextResponseBodyConverter( @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) { ... int start = converterFactories.indexOf(skipPast) + 1; // 遍歷 Converter.Factory 集合并尋找合適的工廠, 這里是GsonResponseBodyConverter for (int i = start, count = converterFactories.size(); i < count; i++) { Converter<ResponseBody, ?> converter = converterFactories.get(i).responseBodyConverter(type, annotations, this); if (converter != null) { //noinspection unchecked return (Converter<ResponseBody, T>) converter; } } ~~~ #### 最終,執行HttpServiceMethod的invoke方法 ~~~ @Override ReturnT invoke(Object[] args) { return callAdapter.adapt( new OkHttpCall<>(requestFactory, args, callFactory, responseConverter)); } ~~~ 最終在adapt中創建了一個ExecutorCallbackCall對象,它是一個裝飾者,而在它內部真正去執行網絡請求的還是OkHttpCall。 ### 四、創建網絡請求接口類實例并執行請求過程 #### 1、service.listRepos() ~~~ 1、Call<List<Repo>> repos = service.listRepos("octocat"); ~~~ service對象是動態代理對象Proxy.newProxyInstance(),當調用getCall()時會被 它攔截,然后調用自身的InvocationHandler#invoke(),得到最終的Call對象。 #### 2、同步執行流程 repos.execute() ~~~ @Override public Response<T> execute() throws IOException { okhttp3.Call call; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; if (creationFailure != null) { if (creationFailure instanceof IOException) { throw (IOException) creationFailure; } else if (creationFailure instanceof RuntimeException) { throw (RuntimeException) creationFailure; } else { throw (Error) creationFailure; } } call = rawCall; if (call == null) { try { // 創建一個OkHttp的Request對象請求 call = rawCall = createRawCall(); } catch (IOException | RuntimeException | Error e) { throwIfFatal(e); // Do not assign a fatal error to creationFailure. creationFailure = e; throw e; } } } if (canceled) { call.cancel(); } // 調用OkHttpCall的execute()發送網絡請求(同步), // 并解析網絡請求返回的數據 return parseResponse(call.execute()); } private okhttp3.Call createRawCall() throws IOException { // 創建 一個okhttp3.Request okhttp3.Call call = callFactory.newCall(requestFactory.create(args)); if (call == null) { throw new NullPointerException("Call.Factory returned null."); } return call; } Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body(); // Remove the body's source (the only stateful object) so we can pass the response along. rawResponse = rawResponse.newBuilder() .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())) .build(); // 根據響應返回的狀態碼進行處理 int code = rawResponse.code(); if (code < 200 || code >= 300) { try { // Buffer the entire body to avoid future I/O. ResponseBody bufferedBody = Utils.buffer(rawBody); return Response.error(bufferedBody, rawResponse); } finally { rawBody.close(); } } if (code == 204 || code == 205) { rawBody.close(); return Response.success(null, rawResponse); } ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody); try { // 將響應體轉為Java對象 T body = responseConverter.convert(catchingBody); return Response.success(body, rawResponse); } catch (RuntimeException e) { // If the underlying source threw an exception, propagate that rather than indicating it was // a runtime exception. catchingBody.throwIfCaught(); throw e; } } ~~~ #### 3、異步請求流程 reponse.enqueque ~~~ @Override public void enqueue(final Callback<T> callback) { // 使用靜態代理 delegate進行異步請求 delegate.enqueue(new Callback<T>() { @Override public void onResponse(Call<T> call, finalResponse<T>response) { // 線程切換,在主線程顯示結果 callbackExecutor.execute(new Runnable() { @Override public void run() { if (delegate.isCanceled()) { callback.onFailure(ExecutorCallbackCall.this, newIOException("Canceled")); } else { callback.onResponse(ExecutorCallbackCall.this,respons); } } }); } @Override public void onFailure(Call<T> call, final Throwable t) { callbackExecutor.execute(new Runnable() { @Override public void run() { callback.onFailure(ExecutorCallbackCall.this, t); } }); } }); } ~~~ 看看 delegate.enqueue 內部流程。 ~~~ @Override public void enqueue(final Callback<T> callback) { okhttp3.Call call; Throwable failure; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; call = rawCall; failure = creationFailure; if (call == null && failure == null) { try { // 創建OkHttp的Request對象,再封裝成OkHttp.call // 方法同發送同步請求,此處上面已分析 call = rawCall = createRawCall(); } catch (Throwable t) { failure = creationFailure = t; } } @Override public void enqueue(final Callback<T> callback) { checkNotNull(callback, "callback == null"); okhttp3.Call call; Throwable failure; ... call.enqueue(new okhttp3.Callback() { @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) { Response<T> response; try { // 此處上面已分析 response = parseResponse(rawResponse); } catch (Throwable e) { throwIfFatal(e); callFailure(e); return; } try { callback.onResponse(OkHttpCall.this, response); } catch (Throwable t) { t.printStackTrace(); } } @Override public void onFailure(okhttp3.Call call, IOException e) { callFailure(e); } private void callFailure(Throwable e) { try { callback.onFailure(OkHttpCall.this, e); } catch (Throwable t) { t.printStackTrace(); } } }); } ~~~ 如果你看到這里的話,恭喜你,你已經對Retrofit已經有一個比較深入的了解了,但是,筆者還是要建議大家自己主動配合著Retrofit最新版的源碼一步步去徹底地認識它,只有這樣,你才能看到它真實的內心,最后附上一張Stay的Retrofit源碼流程圖,要注意的是,這是V2.5之前版本的流程,但是,在看完上面的源碼分析后,我們知道,主體流程是沒有變化的。 ![](https://img.kancloud.cn/b5/35/b5357a71da1b6d9e5d4d0d51d53788d4_891x842.jpg) ### 五、總結 從本質上來說,Retrofit雖然只是一個RESTful 的HTTP 網絡請求框架的封裝庫。但是,它內部通過 大量的設計模式 封裝了 OkHttp,讓使用者感到它非常簡潔、易懂。它內部主要是用動態代理的方式,動態將網絡請求接口的注解解析成HTTP請求,最后執行請求的過程。好了,至此,我們的Android主流三方庫源碼分析的網絡庫分析部分已經完畢。接下來,將為大家帶來最流行的圖片加載框架Glide的源碼分析,敬請期待~ ##### 參考鏈接: * * * 1、Retrofit V2.5.0源碼 2、Android進階之光 3、[Android:手把手帶你 深入讀懂 Retrofit 2.0 源碼](https://www.jianshu.com/p/0c055ad46b6c) 4、[Retrofit分析-漂亮的解耦套路](https://www.jianshu.com/p/45cb536be2f4) 鏈接:https://juejin.im/post/5e1fb9386fb9a0300a4501a6
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看