精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

深入理解 Gradle Tooling API

開發 前端
本文首先從現代 IDE 與構建系統的結合方式出發,引出 Gradle Tooling API,介紹了它對于 Gradle 構建系統的特殊意義,然后通過 Tooling API 具體的 API 及調用示例介紹了它的主要功能,最后在原理分析方面,結合源碼著重分析了跨進程通信與版本兼容原理,這也是 Tooling API 中非常重要的兩個機制。

1. 簡介

構建系統是用來從源代碼生成目標產物的自動化工具,目標產物包括庫、可執行文件、生成的腳本等,構建系統一般會提供平臺相關的可執行程序,外部通過執行命令的形式觸發構建,如 GUN Make、Ant、CMake、Gradle 等等。Gradle 是一個靈活而強大的開源構建系統,它提供了跨平臺的可執行程序,供外部在命令行窗口通過命令執行 Gradle 構建,如 ./gradlew assemble 命令觸發 Gradle 構建任務。

現代成熟的 IDE 中會把需要的構建系統集成進來,結合多種命令行工具,封裝為一套自動化的構建工具,并提供構建視圖工具,提高開發人員的生產力。在 IntelliJ IDEA 中,可以通過 Gradle 視圖工具觸發執行 Gradle 任務,但它并不是通過封裝命令行工具來實現的,而是集成了 Gradle 專門提供的編程 SDK - Gradle Tooling API,通過此 API 可以將 Gradle 構建能力嵌入到 IDE 或其他工具軟件中:

Gradle 為什么要專門提供一個 Tooling API 供外部集成調用,而不是像其他構建系統一樣,只提供基于可執行程序的命令方式呢?Tooling API 是對 Gradle 的一個重大擴展,它提供了比命令方式更可控、更深入的構建控制能力,可以讓 IDE 和其他工具更方便、緊密地和 Gradle 能力結合。Tooling API 接口可以直接返回構建結果,無需像命令方式一樣再手動解析命令行程序的日志輸出,并且可以獨立于版本運行,這意味著相同版本的 Tooling API 可以處理不同 Gradle 版本的構建,同時向前和向后兼容。

2. 接口功能及調用示例

2.1 接口功能

Tooling API 提供了執行和監控構建、查詢構建信息等功能:

  • 查詢構建信息,包括項目結構、項目依賴項、外部依賴項和項目任務等;
  • 執行構建任務并監聽構建的進度信息;
  • 取消正在執行的構建任務;
  • 自動下載與項目匹配的 Gradle 版本;

關鍵 API 如下:

2.2 調用示例

查詢項目結構和任務

try (ProjectConnection connection = GradleConnector.newConnector()
.forProjectDirectory(new File("someFolder"))
.connect()) {
GradleProject rootProject = connection.getModel(GradleProject.class);
Set<? extends GradleProject> subProject = rootProject.getChildren();
Set<? extends GradleTask> tasks = rootProject.getTasks();
}

如上文 API 介紹,首先通過 Tooling API 的入口類 GradleConnector 創建一個到參與構建工程的連接 ProjectConnection ,然后通過 getModel(Class modelType) 獲取此工程的結構信息模型 GradleProject,該模型包含我們要查詢的項目結構、項目任務等信息。

執行構建任務

String[] gradleTasks = new String[]{"clean", "app:assembleDebug"};
try (ProjectConnection connection = GradleConnector.newConnector()
.forProjectDirectory(new File("someFolder"))
.connect()) {
BuildLauncher build = connection.newBuild();
build.forTasks(gradleTasks)
.addProgressListener(progressListener)
.setColorOutput(true)
.setJvmArguments(jvmArguments);
build.run();
}

此例中通過 ProjectConnection 的 newBuild() 方法創建了一個用于執行構建任務的 BuildLauncher,然后通過 forTasks(String... tasks) 配置要執行的 Gradle 任務以及配置執行進度監聽等等,最后通過 run() 觸發執行任務。

3. 原理分析

3.1 如何與 Gradle 構建進程通信?

Gradle Tooling API 并不具備真正的 Gradle 構建能力,而是提供了調用本機 Gradle 程序的入口,方便以編碼形式與 Gradle 通信,在我們自己的工具程序中通過 API 觸發調用 Gradle 構建能力后,還需要和真正的 Gradle 構建程序進行跨進程通信。不論是通過 Gradle Tooling API 與 Gradle 交互的 IDE 或工具程序,還是以 command 形式與 Gradle 交互的命令行窗口程序,這種跨進程調用 Gradle 構建程序的客戶端程序,都是一個 Gradle client,真正執行任務的 Gradle 構建程序才是 Gradle build process.

Gradle daemon process 是長期存在的 Gradle build process,通過規避構建 Gradle JVM 環境和內存緩存提高構建速度,對于集成 Gradle Tooling API 的 Gradle client,會始終啟用 daemon process。也就是說,集成了 Gradle Tooling API 的工具程序,會始終與 daemon process 跨進程通信,調用 Gradle 構建能力。Gradle daemon process 是動態創建的,Gradle client 若要連接到動態創建的 daemon process,就需要通過服務注冊和服務發現機制,將 daemon process 注冊記錄下來并開放查詢,DaemonRegistry 就提供了這樣的機制。

客戶端 - Gradle Client

下面以獲取工程結構信息為切入點,從源碼角度分析 Gradle Tooling API 的跨進程通信機制:

try (ProjectConnection connection = GradleConnector.newConnector()
.forProjectDirectory(new File("someFolder"))
.connect()) {
GradleProject rootProject = connection.getModel(GradleProject.class);
}

從代碼上看,雖然 ProjectConnection 像是建立了一個到 daemon process 的鏈接,但并沒有,而是在 getModel(Class modelType) 方法中才會真正去建立與 daemon process 的鏈接,此方法內部,會從 Tooling API 側調用到 Gradle 源碼中,最后在 DefaultDaemonConnector.java 中查找可用的 daemon process:

public DaemonClientConnection connect(ExplainingSpec<DaemonContext> constraint) {
final Pair<Collection<DaemonInfo>, Collection<DaemonInfo>> idleBusy = partitionByState(daemonRegistry.getAll(), Idle);
final Collection<DaemonInfo> idleDaemons = idleBusy.getLeft();
final Collection<DaemonInfo> busyDaemons = idleBusy.getRight();
// Check to see if there are any compatible idle daemons
DaemonClientConnection connection = connectToIdleDaemon(idleDaemons, constraint);
if (connection != null) {
return connection;
}
// Check to see if there are any compatible canceled daemons and wait to see if one becomes idle
connection = connectToCanceledDaemon(busyDaemons, constraint);
if (connection != null) {
return connection;
}
// No compatible daemons available - start a new daemon
handleStopEvents(idleDaemons, busyDaemons);
return startDaemon(constraint);
}

通過以上 daemon process 查找邏輯及相關代碼,可以得出:

  1. Daemon process 包括 Idle、Busy、Canceled、StopRequested、Stopped、Broken 六種狀態;
  2. 通過 daemon process 模式執行 Gradle 構建時,會依次嘗試查找 Idle、Canceled 狀態且環境兼容的 daemon process,如果沒有找到,就新建一個與 Gradle client 環境兼容的 daemon process;
  3. 所有的 Daemon process 記錄在 DaemonRegistry.java 注冊表中,供 Gradle client 獲取;
  4. Daemon process 的環境兼容判斷包括 Gradle 版本、文件編碼、JVM heap size 等屬性;
  5. 獲取到一個兼容的 daemon process 后,會通過 Socket 鏈接到 daemon process 監聽的端口,然后通過 Socket 與 daemon process 通信;

服務端 - Daemon process

當一個 Gradle client 調用 Gradle 構建能力時,會觸發 daemon process 的創建,進程入口函數在 GradleDaemon.java 中,然后會轉到 DaemonMain.java 中初始化 process,最后在 TcpIncomingConnector.java 中開啟 Socket Server 并綁定監聽一個指定的端口:

public ConnectionAcceptor accept(Action<ConnectCompletion> action, boolean allowRemote) {
final ServerSocketChannel serverSocket;
int localPort;
try {
serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(addressFactory.getLocalBindingAddress(), 0));
localPort = serverSocket.socket().getLocalPort();
} catch (Exception e) {
throw UncheckedException.throwAsUncheckedException(e);
}
...
}

隨后會在 DaemonRegistryUpdater.java 中將 daemon process 記錄到注冊表中:

public void onStart(Address connectorAddress) {
LOGGER.info("{}{}", DaemonMessages.ADVERTISING_DAEMON, connectorAddress);
LOGGER.debug("Advertised daemon context: {}", daemonContext);
this.connectorAddress = connectorAddress;
daemonRegistry.store(new DaemonInfo(connectorAddress, daemonContext, token, Busy));
}

這樣 Gradle client 就可以在注冊表中獲取到兼容的 daemon process 及其端口,從而與 daemon process 建立連接實現通信,具體流程如下圖:

總結梳理一下 Tooling API 與 Gradle Daemon process 的連接建立流程:

  1. Tooling API 本身代碼量并不是太多,調用獲取項目信息接口經過 ModelProducer 抽象封裝后,會進入到 Gradle 源碼中,但還屬于 Gradle client 進程中;
  2. 在 DefaultDaemonConnector 中會嘗試從 DaemonRegistry 獲取可用的、兼容的 daemon process,如果沒有,就新建一個 daemon process;
  3. Daemon process 啟動后會通過 Socket 綁定監聽到固定端口,然后將監聽端口等自身信息記錄到 DaemonRegistry 中,供 Gradle client 查詢、獲取以及建立連接;

3.2 如何實現向前和向后兼容?

Tooling API 支持 Gradle 2.6 及更高版本,即某一版本的 Tooling API 與其他版本 Gradle 向前和向后兼容,支持調用舊版或新版 Gradle 進行 Gradle 構建,但 Tooling API 所包含的接口功能并非適用于所有 Gradle 版本;Gradle 5.0 及更高版本對 Tooling API 版本也有要求,需要 Tooling API 3.0 及更高版本。Gradle 和 Tooling API 不同版本之間是如何實現兼容的呢?

思考一個問題,如果我們有兩個軟件:主軟件 A 和專門用于調用 A 的工具軟件 B,如何才能實現 A、B 之間最大程度且優雅的版本兼容?下面深入分析 Tooling API 和 Gradle 源碼,看看 Gradle 在版本兼容方面采取了哪些值得關注的技術方案。

Gradle 版本適配

在 Gradle Tooling API 源碼倉庫中,有一張介紹獲取項目信息調用鏈的流程圖:

我們只關注圖中的 DefaultConnection- 從 Tooling API 調用到 Gradle launcher 模塊的關鍵類:

  • DefaultConnection has entry points to accept calls from different ToolingAPI versions

Tooling API 側最終在 DefaultToolingImplementationLoader.java 中通過自定義 URLClassLoader 加載 DefaultConnection,自定義 URLClassLoader 類加載路徑指定了對應 Gradle 版本 lib 下的 jar 包,從而可以實現加載不同 Gradle 版本的 DefaultConnection:

private ClassLoader createImplementationClassLoader(Distribution distribution, ProgressLoggerFactory progressLoggerFactory, InternalBuildProgressListener progressListener, ConnectionParameters connectionParameters, BuildCancellationToken cancellationToken) {
ClassPath implementationClasspath = distribution.getToolingImplementationClasspath(progressLoggerFactory, progressListener, connectionParameters, cancellationToken);
LOGGER.debug("Using tooling provider classpath: {}", implementationClasspath);
FilteringClassLoader.Spec filterSpec = new FilteringClassLoader.Spec();
filterSpec.allowPackage("org.gradle.tooling.internal.protocol");
filterSpec.allowClass(JavaVersion.class);
FilteringClassLoader filteringClassLoader = new FilteringClassLoader(classLoader, filterSpec);
return new VisitableURLClassLoader("tooling-implementation-loader", filteringClassLoader, implementationClasspath);
}

Tooling API 通過自定義 Java 類加載器調用到本機指定版本的 Gradle 源碼,需要注意的是,雖然 DefaultConnection 已經是 Gradle 側的源碼,但還屬于 Gradle client 端進程,即 IDE 等工具軟件程序中。

模型類適配

通過 getModel(Class modelType) 方法可以從 Gradle daemon process 中獲取工程結構信息模型 GradleProject,而不同 Gradle 版本可能有不同的 GradleProject 定義,如何在同一版本 Tooling API 中兼容多個版本的信息模型結構呢?

Tooling API 在請求獲取信息模型前,會在 VersionDetails.java 中根據 Gradle 版本判斷是否支持獲取該模型,若支持,才會向 daemon process 發出獲取請求。daemon process 將對應版本的信息模型返回后,在 Tooling API 的 ProtocolToModelAdapter.java 中會對其封裝一層動態代理,最終以 Proxy 形式返回:

private static <T> T createView(Class<T> targetType, Object sourceObject, ViewDecoration decoration, ViewGraphDetails graphDetails) {
......
// Create a proxy
InvocationHandlerImpl handler = new InvocationHandlerImpl(targetType, sourceObject, decorationsForThisType, graphDetails);
Object proxy = Proxy.newProxyInstance(viewType.getClassLoader(), new Class<?>[]{viewType}, handler);
handler.attachProxy(proxy);
return viewType.cast(proxy);
}

最終 Tooling API 返回的 GradleProject 僅僅是一個動態代理接口,如下:

public interface GradleProject extends HierarchicalElement, BuildableElement, ProjectModel {
......
File getBuildDirectory() throws UnsupportedMethodException;
}

可以看到,即使是支持的信息模型,其中的某些內容也可能由于 Gradle 版本不匹配而不支持獲取,調用會拋出 UnsupportedMethodException 異常。

通過動態代理接口方式,實現了適配不同版本的模型類,但這種方式也帶來一個缺點,在 Tooling API 側由于只能拿到模型信息的接口,并不是真正的模型實體類,那后續對整個模型信息類做序列化或傳遞時,就需要再做一層轉換,構造出一個真正包含內容的實體類,Android sdktools 庫中就針對 AndroidProject 模型,構造了的真正包含內容的實體類 IdeAndroidProjectImpl。

4. 總結

本文首先從現代 IDE 與構建系統的結合方式出發,引出 Gradle Tooling API,介紹了它對于 Gradle 構建系統的特殊意義,然后通過 Tooling API 具體的 API 及調用示例介紹了它的主要功能,最后在原理分析方面,結合源碼著重分析了跨進程通信與版本兼容原理,這也是 Tooling API 中非常重要的兩個機制。

通過對 Gradle Tooling API 的分析學習,可以對 Tooling API 整體的架構原理深度掌握,從而更好地基于它開發具有 Gradle 能力的工具軟件,另外還可以學習到一些類似技術架構場景下的方法論:在需要與程序運行時動態創建的服務通訊時,一般可以引入服務注冊和服務發現機制去實現對動態服務的查詢、連接;作為一個供外部接入的工具程序,在同類程序都僅提供命令行方式時,我們要敢于打破常規、提供一種全新的方式,從而可以更大程度給其他軟件賦能,實現雙方共贏。

5. 參考文章

  • org.gradle.tooling (Gradle API 7.2)https://docs.gradle.org/current/javadoc/org/gradle/tooling/package-summary.html
  • Gradle & Third-party Toolshttps://docs.gradle.org/current/userguide/third_party_integration.html#embedding
  • Gradle | Gradle Featureshttps://gradle.org/features/#embed-gradle-with-tooling-api
  • The Gradle Daemonhttps://docs.gradle.org/current/userguide/gradle_daemon.html
責任編輯:未麗燕 來源: 字節跳動技術團隊
相關推薦

2016-12-08 15:36:59

HashMap數據結構hash函數

2020-07-21 08:26:08

SpringSecurity過濾器

2010-06-01 15:25:27

JavaCLASSPATH

2009-09-25 09:14:35

Hibernate日志

2021-02-17 11:25:33

前端JavaScriptthis

2023-10-19 11:12:15

Netty代碼

2013-09-22 14:57:19

AtWood

2017-08-15 13:05:58

Serverless架構開發運維

2025-05-06 00:43:00

MySQL日志文件MIXED 3

2020-09-23 10:00:26

Redis數據庫命令

2017-01-10 08:48:21

2024-02-21 21:14:20

編程語言開發Golang

2025-06-05 05:51:33

2019-06-25 10:32:19

UDP編程通信

2024-10-12 15:18:05

PythonAPI操作系統

2022-11-04 09:43:05

Java線程

2024-03-12 00:00:00

Sora技術數據

2021-04-20 23:25:16

執行函數變量

2017-01-13 22:42:15

iosswift

2021-05-13 21:27:24

ThreadLocal多線程多線程并發安全
點贊
收藏

51CTO技術棧公眾號

日本在线观看| 日韩乱码人妻无码中文字幕| www一区二区三区| 亚洲激情在线播放| 久久久久久久久久久久久久一区| 区一区二在线观看| 国产精品久久久久久麻豆一区软件 | а√天堂中文资源在线bt| 久久久久久日产精品| 成人在线一区二区| 久久久精品福利| 亚洲自拍偷拍网| 亚洲精品视频中文字幕| 亚洲精品永久视频| 欧美一区久久久| 一区二区免费在线| 日韩一区二区电影在线观看| 性一交一乱一色一视频麻豆| 日韩高清一级片| 欧美精品激情blacked18| 日韩丰满少妇无码内射| 97品白浆高清久久久久久| 欧美影院午夜播放| 日韩国产欧美亚洲| 黄色网址视频在线观看| 久久久久久免费网| 99一区二区| 国产成人麻豆免费观看| aa国产精品| 欧美伦理91i| 99国产精品无码| 国产剧情一区| 日韩av一区在线观看| 美女被艹视频网站| 性高爱久久久久久久久| 欧美日韩另类字幕中文| 日韩专区第三页| 男人天堂手机在线| 亚洲国产精品精华液2区45| 黑人巨大精品欧美一区二区小视频 | 在线不卡一区二区| 四虎永久在线精品无码视频| 波多野结衣中文字幕久久| 亚洲欧美区自拍先锋| 亚洲精品一区二区三| 麻豆国产在线播放| 99re这里只有精品首页| 成人免费视频网站| www.亚洲天堂.com| 国产精品亚洲第一| 1卡2卡3卡精品视频| 国产精品热久久| 麻豆精品在线看| 国产精品人成电影| 中文 欧美 日韩| 奇米精品一区二区三区在线观看一| 欧美一区二区大胆人体摄影专业网站| 日韩av综合在线| 激情欧美一区| 91av在线播放| 99超碰在线观看| 美女91精品| 国产精国产精品| 国产精品久久久久久免费观看| 亚洲视频免费观看| 成人性生交大片免费看96| 黄频网站在线观看| 欧美黄色免费| 美女久久久久久久久久久| 国产在线观看免费视频软件| 日韩欧美二区| 美女扒开尿口让男人操亚洲视频网站| 99久久99久久精品国产| 女人香蕉久久**毛片精品| 欧美日韩国产成人| 五月天综合在线| 噜噜噜在线观看免费视频日韩| 清纯唯美亚洲激情| 中文字幕免费观看视频| 国产麻豆一精品一av一免费| 国产91精品一区二区绿帽| 欧美一级淫片aaaaaa| 91小视频在线免费看| 日韩高清专区| caoporn97在线视频| 香蕉乱码成人久久天堂爱免费| 日日鲁鲁鲁夜夜爽爽狠狠视频97| 韩国成人漫画| 777色狠狠一区二区三区| 亚洲国产精品第一页| 亚洲美女久久| 日韩视频免费观看| 黄网站免费在线| 久久久噜噜噜久久狠狠50岁| 成人免费视频a| 污视频在线免费| 国产精品久久久久精k8| 人人妻人人澡人人爽欧美一区双| 东京一区二区| 欧美一区三区四区| 国产精品1000部啪视频| 亚洲影视一区二区三区| 国产97免费视| 成人午夜福利视频| 国产精品乱子久久久久| 欧美大片在线播放| 色8久久久久| 日韩高清av在线| 99自拍视频在线| 免费看的黄色欧美网站| 91九色蝌蚪成人| 黄视频在线观看免费| 亚洲精品日产精品乱码不卡| 成人在线看视频| 9l视频自拍九色9l视频成人| 最近2019好看的中文字幕免费| 国产一级一级片| 久久福利资源站| 日本一区二区三区在线视频| 日本h片在线观看| 欧美日韩在线播| 久久福利小视频| 一本一道久久综合狠狠老| 国产精品xxx视频| 三级网站免费观看| 亚洲精品免费电影| 伊人影院综合在线| 国产不卡av一区二区| 久久青草精品视频免费观看| 国产日韩欧美视频在线观看| 日本一区二区久久| 国产激情在线观看视频| 日韩成人av在线资源| 欧美尺度大的性做爰视频| 在线视频播放大全| 国产三级久久久| 欧美在线观看成人| 色婷婷狠狠五月综合天色拍| 久久久久久成人精品| 成人高潮片免费视频| 日韩一区日韩二区| 久热精品在线播放| 精品美女视频| 国产精品久久久久久av下载红粉| 欧美在线观看在线观看| 欧美日韩国产综合视频在线观看中文| 最新国产精品自拍| 欧美日韩视频| 成人av男人的天堂| av电影在线地址| 亚洲成人黄色在线| 国产在线综合网| av电影天堂一区二区在线| 精品一二三四五区| 成人av资源网址| 欧美精品videossex88| www.激情五月.com| 亚洲丶国产丶欧美一区二区三区| 日批免费观看视频| 99国产一区| 欧美日韩亚洲在线| 123成人网| 精品国产网站地址| 99精品在线视频观看| 亚洲一区欧美一区| 国产又粗又长又爽| 老鸭窝毛片一区二区三区 | 中文字幕网址在线| 亚洲人成精品久久久久| 韩国三级在线看| 99亚洲伊人久久精品影院红桃| 欧美精品尤物在线| av久久网站| 久久中文字幕在线| 亚洲欧美另类一区| 欧美性猛交xxxx乱大交极品| 538精品视频| 国产尤物一区二区| 美女日批免费视频| 欧美综合在线视频观看| 成人免费网站在线| brazzers在线观看| 亚洲深夜福利网站| 99热这里只有精| 黑人巨大精品欧美一区二区一视频| 国产传媒国产传媒| 日韩高清成人在线| 欧美亚洲高清| 欧美日本国产视频| 国产av 一区二区三区| 成人综合在线视频| 欧美日韩亚洲综合| 中文字幕av一区中文字幕天堂| jizz日本在线播放| 国产成人综合在线| 国产综合免费视频| 中文字幕在线网址| 农村妇女一区二区| 一道本无吗dⅴd在线播放一区| 伊人色综合久久久| 亚洲一区欧美一区| 国产性猛交xx乱| 成人看片黄a免费看在线| www.色偷偷.com| 欧美激情1区2区3区| 欧美日韩精品免费观看| 中文字幕第69页| 国产成人精品视频免费| 国产美女亚洲精品7777| 久久久人成影片一区二区三区观看 | 亚洲视频一区在线观看| 中文亚洲视频在线| 波多野结衣黄色| 自拍偷自拍亚洲精品播放| 在线观看国产精品日韩av| 亚洲精品福利在线| 久久久久亚洲av成人网人人软件| 亚洲精品一级| 中文字幕99| 国产精品亚洲片在线播放| 99九九视频| 57pao成人永久免费| 日本中文字幕不卡免费| 99热这里只有精品66| 欧美 日韩 综合| 日本福利一区| 欧美亚洲成人精品| 免费在线国产视频| 亚洲无线码在线一区观看| 成人免费视频国产| 欧美一区欧美二区| 中文字幕久久久久| 欧美在线色视频| 亚洲国产精品毛片av不卡在线| 中文字幕一区二区三区四区久久| 国产精品电影久久久久电影网| 蜜桃视频m3u8在线观看| 欧美激情区在线播放| 麻豆免费在线观看| 日韩一区二区久久久| sese一区| 中文字幕亚洲欧美日韩2019| 电影在线高清| 一区二区三区美女xx视频| 黄色小视频在线免费观看| 亚洲欧美成人精品| 你懂的免费在线观看| 日韩精品免费看| av女名字大全列表| 日韩毛片在线看| 青青草视频免费在线观看| 亚洲精品一区二区三区精华液 | 欧美美女bb生活片| 一道本在线视频| 91麻豆精品国产91久久久久久久久 | 色偷偷88欧美精品久久久| 加勒比在线一区| 欧美日韩精品系列| 国产精品久久婷婷| 日韩一区二区三区四区五区六区 | 国产精品白丝jk黑袜喷水| 亚洲天堂网站在线| 福利一区福利二区| 国产精品久久久久久在线观看| 9久草视频在线视频精品| 国产艳俗歌舞表演hd| 久久九九99视频| 精品一区二区三孕妇视频| 中文字幕在线观看不卡视频| 国产高潮流白浆| 亚洲高清视频中文字幕| 日本一区二区免费电影| 欧美亚洲一区二区在线| 国产精品伊人久久| 精品福利av导航| 国产高清在线观看| 久久国产精品久久久久久| 爱福利在线视频| 91精品国产高清自在线| 99九九久久| 91精品婷婷国产综合久久蝌蚪| 女仆av观看一区| 亚洲午夜在线观看| 激情偷拍久久| 黄色免费网址大全| 国产成人免费在线视频| 97超碰在线免费观看| 中文字幕制服丝袜成人av| 久久久久无码精品国产| 日韩欧美一区二区在线| 国产免费无遮挡| 亚洲第一综合天堂另类专| 国产视频网站在线| 欧美第一黄色网| 嫩草伊人久久精品少妇av杨幂| 成人欧美一区二区三区黑人免费| 一区二区三区视频免费观看| 精品嫩模一区二区三区| 性高湖久久久久久久久| 在线观看视频在线观看| 久久这里都是精品| 青娱乐91视频| 欧美日韩亚洲另类| 天堂91在线| 欧美大尺度在线观看| 久久青青视频| 岛国一区二区三区高清视频| 欧美熟乱15p| 欧美 国产 综合| 国产麻豆9l精品三级站| 亚洲国产日韩一区无码精品久久久| 亚洲精品一二三| 国产一级片一区二区| 亚洲国产一区二区三区四区| 黄色片网站在线观看| 日韩美女在线观看| 国产精品一区二区三区美女| 国产精品jizz在线观看老狼| 午夜亚洲激情| 大桥未久恸哭の女教师| 亚洲免费电影在线| 一区精品在线观看| 亚洲美女av黄| 国产美女高潮在线观看| 成人18视频| 欧美国产91| 久久久久久久久久一区| 国产欧美日韩在线| 国产午夜免费福利| 亚洲精品久久久久中文字幕二区 | 91麻豆精品国产91久久久平台| 熟妇人妻va精品中文字幕| 99热国产精品| 日韩福利片在线观看| 精品日韩99亚洲| 日本三级在线观看网站| 91久久久久久久久久久| 91欧美在线| 国产高清999| 亚洲人成影院在线观看| 国产精品久久免费| 超碰97人人做人人爱少妇| 国产高清亚洲| 国产成人三级视频| 国产精品一区二区久久精品爱涩| 欧美日韩色视频| 欧美一区二区精品在线| 国产激情在线| 亚洲a级在线观看| 女生裸体视频一区二区三区| 一个人看的视频www| 一区二区三区91| 色哟哟国产精品色哟哟| 久久免费视频在线观看| 青青久久av| 欧美激情精品久久久久久小说| 国产色综合一区| 在线观看一二三区| 日韩在线www| 我要色综合中文字幕| 日韩欧美不卡在线| 久久免费看少妇高潮| 在线观看毛片视频| www日韩欧美| 视频精品一区| 午夜精品久久久久久久无码| 久久蜜桃一区二区| 亚洲视频在线观看一区二区| 久久夜精品香蕉| 超碰在线一区| 女性隐私黄www网站视频| 国产精品婷婷午夜在线观看| 国产草草影院ccyycom| 午夜精品久久久99热福利| 一区二区三区日本久久久| 亚洲一级免费在线观看| 一区二区三区四区中文字幕| 成人乱码一区二区三区| 日韩美女免费线视频| 欧美gayvideo| 久久久久久久人妻无码中文字幕爆| 福利微拍一区二区| 色网站在线看| 国产伦精品一区二区| 日韩精品亚洲一区二区三区免费| 三级在线观看免费大全| 日韩av在线网站| 亚洲国产天堂| 少妇无码av无码专区在线观看| 国产喷白浆一区二区三区| 中文字幕一区二区三区四区免费看| 欧美成人免费一级人片100| 亚洲黄页在线观看| 天天爽夜夜爽视频| 色成人在线视频| 日本一本在线免费福利| 欧美日韩在线一二三| 国产很黄免费观看久久| 在线免费观看av网址| 精品中文字幕在线|