日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

【Android 修炼手册】Gradle 篇 -- Gradle 的基本使用

發布時間:2023/12/15 Android 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Android 修炼手册】Gradle 篇 -- Gradle 的基本使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

預備知識

  • 基本的 android 開發知識
  • 了解 Android Studio 基本使用
  • 看完本文可以達到什么程度

  • 掌握 gradle 的基本使用
  • 了解 gradle 及 android gradle plugin
  • 了解 gradle 構建階段及生命周期回調
  • 掌握 Task,Transform 等概念
  • 學會自定義 task,自定義 gradle 插件
  • 如果您已經達到上面的程度,那么可以不用再看下文了,直接看最后的總結即可

    本文將從下面幾個部分進行講解:

    一、gradle 是什么

    官方解釋是: Gradle is an open-source build automation tool focused on flexibility and performance. Gradle build scripts are written using a Groovy or Kotlin DSL.
    可以從三個角度來理解

    1. gradle 是一個自動化構建工具
    gradle 是通過組織一系列 task 來最終完成自動化構建的,所以 task 是 gradle 里最重要的概念
    我們以生成一個可用的 apk 為例,整個過程要經過 資源的處理,javac 編譯,dex 打包,apk 打包,簽名等等步驟,每個步驟就對應到 gradle 里的一個 task

    gradle 可以類比做一條流水線,task 可以比作流水線上的機器人,每個機器人負責不同的事情,最終生成完整的構建產物

    2. gradle 腳本使用了 groovy 或者 kotlin DSL
    gradle 使用 groovy 或者 kotlin 編寫,不過目前還是 groovy 居多
    那什么是 DSL 呢?DSL 也就是 Domain Specific Language 的簡稱,是為了解決某一類任務專門設計的計算機語言
    DSL 相對應的是 GPL (General-Purpose Language),比如 java
    與 GPL 相比起來,DSL 使用簡單,定義比較簡潔,比起配置文件,DSL 又可以實現語言邏輯
    對 gradle 腳本來說,他實現了簡潔的定義,又有充分的語言邏輯,以 android {} 為例,這本身是一個函數調用,參數是一個閉包,但是這種定義方式明顯要簡潔很多

    3. gradle 基于 groovy 編寫,而 groovy 是基于 jvm 語言
    gradle 使用 groovy 編寫,groovy 是基于 jvm 的語言,所以本質上是面向對象的語言,面向對象語言的特點就是一切皆對象,所以,在 gradle 里,.gradle 腳本的本質就是類的定義,一些配置項的本質都是方法調用,參數是后面的 {} 閉包
    比如 build.gradle 對應 Project 類,buildScript 對應 Project.buildScript 方法

    二、gradle 項目分析

    關于 gradle 的項目層次,我們新建一個項目看一下,項目地址在 EasyGradle

    2.1 settings.gradle

    settings.gradle 是負責配置項目的腳本
    對應 Settings 類,gradle 構建過程中,會根據 settings.gradle 生成 Settings 的對象
    對應的可調用的方法在文檔里可以查找
    其中幾個主要的方法有:

    • include(projectPaths)
    • includeFlat(projectNames)
    • project(projectDir)

    一般在項目里見到的引用子模塊的方法,就是使用 include,這樣引用,子模塊位于根項目的下一級

    include ':app' 復制代碼

    如果想指定子模塊的位置,可以使用 project 方法獲取 Project 對象,設置其 projectDir 參數

    include ':app' project(':app').projectDir = new File('./app') 復制代碼

    2.2 rootproject/build.gradle

    build.gradle 負責整體項目的一些配置,對應的是 Project 類
    gradle 構建的時候,會根據 build.gradle 生成 Project 對象,所以在 build.gradle 里寫的 dsl,其實都是 Project 接口的一些方法,Project 其實是一個接口,真正的實現類是 DefaultProject
    build.gradle 里可以調用的方法在 Project 可以查到
    其中幾個主要方法有:

    • buildscript // 配置腳本的 classpath
    • allprojects // 配置項目及其子項目
    • respositories // 配置倉庫地址,后面的依賴都會去這里配置的地址查找
    • dependencies // 配置項目的依賴

    以 EasyGradle 項目來看

    buildscript { // 配置項目的 classpathrepositories { // 項目的倉庫地址,會按順序依次查找google()jcenter()mavenLocal()}dependencies { // 項目的依賴classpath 'com.android.tools.build:gradle:3.0.1'classpath 'com.zy.plugin:myplugin:0.0.1'} }allprojects { // 子項目的配置repositories {google()jcenter()mavenLocal()} } 復制代碼

    2.3 module/build.gradle

    build.gradle 是子項目的配置,對應的也是 Project 類
    子項目和根項目的配置是差不多的,不過在子項目里可以看到有一個明顯的區別,就是引用了一個插件 apply plugin "com.android.application",后面的 android dsl 就是 application 插件的 extension,關于 android plugin dsl 可以看 android-gradle-dsl
    其中幾個主要方法有:

    • compileSdkVersion // 指定編譯需要的 sdk 版本
    • defaultConfig // 指定默認的屬性,會運用到所有的 variants 上
    • buildTypes // 一些編譯屬性可以在這里配置,可配置的所有屬性在 這里
    • productFlavor // 配置項目的 flavor

    以 app 模塊的 build.gradle 來看

    apply plugin: 'com.android.application' // 引入 android gradle 插件android { // 配置 android gradle plugin 需要的內容compileSdkVersion 26defaultConfig { // 版本,applicationId 等配置applicationId "com.zy.easygradle"minSdkVersion 19targetSdkVersion 26versionCode 1versionName "1.0"}buildTypes { release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}compileOptions { // 指定 java 版本sourceCompatibility 1.8targetCompatibility 1.8}// flavor 相關配置flavorDimensions "size", "color"productFlavors {big {dimension "size"}small {dimension "size"}blue {dimension "color"}red {dimension "color"}} }// 項目需要的依賴 dependencies {implementation fileTree(dir: 'libs', include: ['*.jar']) // jar 包依賴implementation 'com.android.support:appcompat-v7:26.1.0' // 遠程倉庫依賴implementation 'com.android.support.constraint:constraint-layout:1.1.3'implementation project(':module1') // 項目依賴 } 復制代碼

    2.4 依賴

    在 gradle 3.4 里引入了新的依賴配置,如下:

    新配置棄用配置行為作用
    implementationcompile依賴項在編譯時對模塊可用,并且僅在運行時對模塊的消費者可用。 對于大型多項目構建,使用 implementation 而不是 api/compile 可以顯著縮短構建時間,因為它可以減少構建系統需要重新編譯的項目量。 大多數應用和測試模塊都應使用此配置。implementation 只會暴露給直接依賴的模塊,使用此配置,在模塊修改以后,只會重新編譯直接依賴的模塊,間接依賴的模塊不需要改動
    apicompile依賴項在編譯時對模塊可用,并且在編譯時和運行時還對模塊的消費者可用。 此配置的行為類似于 compile(現在已棄用),一般情況下,您應當僅在庫模塊中使用它。 應用模塊應使用 implementation,除非您想要將其 API 公開給單獨的測試模塊。api 會暴露給間接依賴的模塊,使用此配置,在模塊修改以后,模塊的直接依賴和間接依賴的模塊都需要重新編譯
    compileOnlyprovided依賴項僅在編譯時對模塊可用,并且在編譯或運行時對其消費者不可用。 此配置的行為類似于 provided(現在已棄用)。只在編譯期間依賴模塊,打包以后運行時不會依賴,可以用來解決一些庫沖突的問題
    runtimeOnlyapk依賴項僅在運行時對模塊及其消費者可用。 此配置的行為類似于 apk(現在已棄用)。只在運行時依賴模塊,編譯時不依賴

    還是以 EasyGradle 為例,看一下各個依賴的不同: 項目里有三個模塊:app,module1, module2
    模塊 app 中有一個類 ModuleApi
    模塊 module1 中有一個類 Module1Api
    模塊 module2 中有一個類 Module2Api
    其依賴關系如下:

    implementation 依賴
    當 module1 使用 implementation 依賴 module2 時,在 app 模塊中無法引用到 Module2Api 類

    api 依賴
    當 module1 使用 api 依賴 module2 時,在 app 模塊中可以正常引用到 Module2Api 類,如下圖

    compileOnly 依賴
    當 module1 使用 compileOnly 依賴 module2 時,在編譯階段 app 模塊無法引用到 Module2Api 類,module1 中正常引用,但是在運行時會報錯

    反編譯打包好的 apk,可以看到 Module2Api 是沒有被打包到 apk 里的

    runtimeOnly 依賴
    當 module1 使用 runtimeOnly 依賴 module2 時,在編譯階段,module1 也無法引用到 Module2Api

    2.5 flavor

    在介紹下面的流程之前,先明確幾個概念,flavor,dimension,variant
    在 android gradle plugin 3.x 之后,每個 flavor 必須對應一個 dimension,可以理解為 flavor 的分組,然后不同 dimension 里的 flavor 兩兩組合形成一個 variant
    舉個例子 如下配置:

    flavorDimensions "size", "color"productFlavors {big {dimension "size"}small {dimension "size"}blue {dimension "color"}red {dimension "color"} } 復制代碼

    那么生成的 variant 對應的就是 bigBlue,bigRed,smallBlue,smallRed
    每個 variant 可以對應的使用 variantImplementation 來引入特定的依賴,比如:bigBlueImplementation,只有在 編譯 bigBlue variant的時候才會引入

    三、gradle wrapper

    gradlew / gradlew.bat 這個文件用來下載特定版本的 gradle 然后執行的,就不需要開發者在本地再安裝 gradle 了。這樣做有什么好處呢?開發者在本地安裝 gradle,會碰到的問題是不同項目使用不同版本的 gradle 怎么處理,用 wrapper 就很好的解決了這個問題,可以在不同項目里使用不同的 gradle 版本。gradle wrapper 一般下載在 GRADLE_CACHE/wrapper/dists 目錄下

    gradle/wrapper/gradle-wrapper.properties 是一些 gradlewrapper 的配置,其中用的比較多的就是 distributionUrl,可以執行 gradle 的下載地址和版本
    gradle/wrapper/gradle-wrapper.jar 是 gradlewrapper 運行需要的依賴包

    四、gradle init.gradle

    在 gradle 里,有一種 init.gradle 比較特殊,這種腳本會在每個項目 build 之前先被調用,可以在其中做一些整體的初始化操作,比如配置 log 輸出等等
    使用 init.gradle 的方法:

  • 通過 --init-script 指定 init.gradle 位置 eg: gradlew --init-script initdir/init.gradle
  • init.gradle 文件放在 USER_HOME/.gradle/ 目錄下
  • .gradle 腳本放在 USER_HOME/.gradle/init.d/ 目錄下
  • .gradle 腳本放在 GRDALE_HOME/init.d/ 目錄下
  • 五、gradle 生命周期及回調

    gradle 構建分為三個階段
    初始化階段
    初始化階段主要做的事情是有哪些項目需要被構建,然后為對應的項目創建 Project 對象

    配置階段
    配置階段主要做的事情是對上一步創建的項目進行配置,這時候會執行 build.gradle 腳本,并且會生成要執行的 task

    執行階段
    執行階段主要做的事情就是執行 task,進行主要的構建工作

    gradle 在構建過程中,會提供一些列回調接口,方便在不同的階段做一些事情,主要的接口有下面幾個

    gradle.addBuildListener(new BuildListener() {@Overridevoid buildStarted(Gradle gradle) {println('構建開始')// 這個回調一般不會調用,因為我們注冊的時機太晚,注冊的時候構建已經開始了,是 gradle 內部使用的}@Overridevoid settingsEvaluated(Settings settings) {println('settings 文件解析完成')}@Overridevoid projectsLoaded(Gradle gradle) {println('項目加載完成')gradle.rootProject.subprojects.each { pro ->pro.beforeEvaluate {println("${pro.name} 項目配置之前調用")}pro.afterEvaluate{println("${pro.name} 項目配置之后調用")}}}@Overridevoid projectsEvaluated(Gradle gradle) {println('項目解析完成')}@Overridevoid buildFinished(BuildResult result) {println('構建完成')} })gradle.taskGraph.whenReady {println("task 圖構建完成") } gradle.taskGraph.beforeTask {println("每個 task 執行前會調這個接口") } gradle.taskGraph.afterTask {println("每個 task 執行完成會調這個接口") } 復制代碼

    六、自定義 task

    默認創建的 task 繼承自 DefaultTask 如何聲明一個 task

    task myTask {println 'myTask in configuration'doLast {println 'myTask in run'} }class MyTask extends DefaultTask {@Input Boolean myInputs@Output @TaskActionvoid start() {} }tasks.create("mytask").doLast { } 復制代碼

    Task 的一些重要方法分類如下:

    • Task 行為
      Task.doFirst
      Task.doLast

    • Task 依賴順序
      Task.dependsOn
      Task.mustRunAfter
      Task.shouldRunAfter
      Task.finalizedBy

    • Task 的分組描述
      Task.group
      Task.description

    • Task 是否可用
      Task.enabled

    • Task 輸入輸出
      gradle 會比較 task 的 inputs 和 outputs 來決定 task 是否是最新的,如果 inputs 和 outputs 沒有變化,則認為 task 是最新的,task 就會跳過不執行
      Task.inputs
      Task.outputs

    • Task 是否執行
      可以通過指定 Task.upToDateWhen = false 來強制 task 執行 Task.upToDateWhen

    比如要指定 Task 之間的依賴順序,寫法如下:

    task task1 {doLast {println('task2')} } task task2 {doLast {println('task2')} } task1.finalizedBy(task2) task1.dependsOn(task2) task1.mustRunAfter(task2) task1.shouldRunAfter(task2) task1.finalizedBy(task2) 復制代碼

    七、Android transform

    android gradle plugin 提供了 transform api 用來在 .class to dex 過程中對 class 進行處理,可以理解為一種特殊的 Task,因為 transform 最終也會轉化為 Task 去執行
    要實現 transform 需要繼承 com.android.build.api.transform.Transform 并實現其方法,實現了 Transform 以后,要想應用,就調用 project.android.registerTransform()

    public class MyTransform extends Transform {@Overridepublic String getName() {// 返回 transform 的名稱,最終的名稱會是 transformClassesWithMyTransformForDebug 這種形式 return "MyTransform";}@Overridepublic Set<QualifiedContent.ContentType> getInputTypes() {/**返回需要處理的數據類型 有 下面幾種類型可選public static final Set<ContentType> CONTENT_CLASS = ImmutableSet.of(CLASSES);public static final Set<ContentType> CONTENT_JARS = ImmutableSet.of(CLASSES, RESOURCES);public static final Set<ContentType> CONTENT_RESOURCES = ImmutableSet.of(RESOURCES);public static final Set<ContentType> CONTENT_NATIVE_LIBS = ImmutableSet.of(NATIVE_LIBS);public static final Set<ContentType> CONTENT_DEX = ImmutableSet.of(ExtendedContentType.DEX);public static final Set<ContentType> DATA_BINDING_ARTIFACT = ImmutableSet.of(ExtendedContentType.DATA_BINDING);*/return TransformManager.CONTENT_CLASS;}@Overridepublic Set<? super QualifiedContent.Scope> getScopes() {/**返回需要處理內容的范圍,有下面幾種類型PROJECT(1), 只處理項目的內容SUB_PROJECTS(4), 只處理子項目EXTERNAL_LIBRARIES(16), 只處理外部庫TESTED_CODE(32), 只處理當前 variant 對應的測試代碼PROVIDED_ONLY(64), 處理依賴@DeprecatedPROJECT_LOCAL_DEPS(2),@DeprecatedSUB_PROJECTS_LOCAL_DEPS(8);*/return Sets.immutableEnumSet(QualifiedContent.Scope.PROJECT);}@Overridepublic boolean isIncremental() {// 是否增量,如果返回 true,TransformInput 會包括一份修改的文件列表,返回 false,會進行全量編譯,刪除上一次的輸出內容return false;}@Overridevoid transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {// 在這里處理 classsuper.transform(transformInvocation)// 在 transform 里,如果沒有任何修改,也要把 input 的內容輸出到 output,否則會報錯for (TransformInput input : transformInvocation.inputs) {input.directoryInputs.each { dir ->// 獲取對應的輸出目錄File output = transformInvocation.outputProvider.getContentLocation(dir.name, dir.contentTypes, dir.scopes, Format.DIRECTORY)dir.changedFiles // 增量模式下修改的文件dir.file // 獲取輸入的目錄FileUtils.copyDirectory(dir.file, output) // input 內容輸出到 output}input.jarInputs.each { jar ->// 獲取對應的輸出 jarFile output = transformInvocation.outputProvider.getContentLocation(jar.name, jar.contentTypes, jar.scopes, Format.JAR)jar.file // 獲取輸入的 jar 文件FileUtils.copyFile(jar.file, output) // input 內容輸出到 output}}} }// 注冊 transform android.registerTransform(new MyTransform()) 復制代碼

    在 transform 中的處理,一般會涉及到 class 文件的修改,操縱字節碼的工具一般是 javasist 和 asm 居多,這兩個工具在這里先不介紹了。后面有機會會展開說一下

    八、自己寫 plugin

    gradle 的插件可以看作是一系列 task 的集合
    在 android 工程的 build.gradle 腳本里,第一行就是 apply plugin: 'com.android.application',這個就是引入 android gradle 插件,插件里有 android 打包相關的 task
    關于 android gradle plugin 的源碼分析,在后面會講到,現在先看看如何實現一個自己的 plugin

    8.1 初始化工程

  • 在 android studio 中創建一個 java module
  • 在 src/main 目錄下創建 groovy 目錄,然后創建自己的包名和插件類
  • 在 src/main 目錄下創建 resources/META-INFO/gradle-plugins 目錄,創建 ,myplugin.properties 文件,文件里內容是
  • implementation-class=com.zy.plugin.MyPlugin // 這里是自己的插件類 復制代碼
  • 修改 build.gradle 文件
  • // 引入 groovy 和 java 插件 apply plugin: 'groovy' apply plugin: 'java'buildscript {repositories {mavenLocal()maven { url 'http://depot.sankuai.com/nexus/content/groups/public/' }maven { url 'https://maven.google.com' }jcenter()} }repositories {mavenLocal()maven {url "http://mvn.dianpingoa.com/android-nova"}maven {url 'http://depot.sankuai.com/nexus/content/groups/public/'}maven { url 'https://maven.google.com' } }dependencies {compile gradleApi()compile localGroovy()compile 'com.android.tools.build:gradle:3.0.1' }復制代碼

    現在為止,項目結構是這個樣子的

    8.2 創建 Plugin

    在剛才創建的插件類里,就可以寫插件的代碼了。插件類繼承 Plugin,并實現 apply 接口,apply 就是在 build.gradle 里 apply plugin 'xxx' 的時候要調用的接口了
    插件開發可以使用 groovy 和 java,使用 groovy 的話可以有更多的語法糖,開發起來更方便一些

    package com.zy.pluginimport org.gradle.api.Plugin import org.gradle.api.Projectclass MyPlugin implements Plugin<Project> {@Overridevoid apply(Project project) {println("apply my plugin")} } 復制代碼

    8.3 創建插件的 task

    我們再定義一個 task 類 MyTask,繼承自 DefaultTask,簡單的輸出一些信息

    package com.zy.pluginimport org.gradle.api.DefaultTask import org.gradle.api.tasks.TaskActionclass MyTask extends DefaultTask {@TaskActionvoid action() {println('my task run')} } 復制代碼

    然后在 plugin 中注冊這個 task

    class MyPlugin implements Plugin<Project> {@Overridevoid apply(Project project) {println("apply my plugin")project.tasks.create("mytask", MyTask.class)} } 復制代碼

    8.4 本地安裝插件

    這樣一個簡單的插件就開發好了,如何使用呢
    我們首先需要在 build.gradle 中引入 maven 插件,并且配置 install 相關的屬性

    apply plugin: 'maven'install {repositories.mavenInstaller {pom.version = '0.0.1' // 配置插件版本號pom.artifactId = 'myplugin' // 配置插件標識pom.groupId = 'com.zy.plugin' // 配置插件組織} } 復制代碼

    之后執行 ./gradlew install 便會把插件安裝在本地 maven 倉庫
    之后在使用的地方引入我們插件的 classpath

    classpath 'com.zy.plugin:myplugin:0.0.1' 復制代碼

    之后加載插件

    apply plugin; 'myplugin' // 這里的 myplugin 是前面說的 myplugin.properties 的名字 復制代碼

    然后運行 ./gradlew tasks --all | grep mytask,就可以看到我們在 plugin 里新增的 task 了
    ./gradlew mytasks 就可以執行 task 了

    8.5 打包發布

    在插件 build.gradle 里新增上傳的配置如下

    uploadArchives {repositories {mavenDeployer {repository(url: "mavenUrl")pom.version = '0.0.1'pom.artifactId = 'myplugin'}} } 復制代碼

    運行 ./gradlew uploadArchives 就可以了

    8.6 調試插件

    那么開發插件的時候如何調試呢?
    1.首先在 as 中新增一個 remote 配置

    2.之后在執行 task 的時候增加下面的參數

    ./gradlew app:mytask -Dorg.gradle.debug=true 復制代碼

    此時可以看到 gradle 在等待 debug 進程連接

    3.之后在插件代碼中打好斷點,在 as 中點擊 debug 按鈕,就可以調試插件代碼了

    九、重點總結

    主要要點如下圖:

    其中一定要掌握的如下:

  • gradle dsl 查詢地址 docs.gradle.org/current/dsl…
  • android gradle plugin dsl 查詢地址 google.github.io/android-gra…
  • gradle 構建生命周期和回調
  • implementation / api
  • flavor
  • 自定義 Task
  • 自定義 Transform 和 自定義插件可以作為擴展內容
  • 【Android 修煉手冊】系列內容 每周五更新
    歡迎關注下面賬號,獲取更新:
    微信搜索公眾號: ZYLAB
    Github
    掘金

    總結

    以上是生活随笔為你收集整理的【Android 修炼手册】Gradle 篇 -- Gradle 的基本使用的全部內容,希望文章能夠幫你解決所遇到的問題。

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

    主站蜘蛛池模板: 中文字幕av一区 | 韩国三级在线 | 91久久精品国产 | 中国大陆高清aⅴ毛片 | 亚洲精品乱码久久久久久黑人 | 久久亚洲精品无码va白人极品 | 亚洲成人一二三 | 看欧美一级片 | 精品人妻一区二区色欲产成人 | 囯产精品久久久久久 | jizzjizz日本免费视频 | 国产成人在线免费视频 | 欧美最猛黑人xxxx黑人猛交 | 亚洲码国产精品高潮在线 | 阿v天堂在线 | 国产chinese | 熟女肥臀白浆大屁股一区二区 | 一区二区欧美视频 | 日韩av片免费观看 | 91蜜桃在线观看 | 亚洲一区二区三区免费看 | 精品久久BBBBB精品人妻 | 欧美日韩a级 | 亚洲欧洲成人精品久久一码二码 | 激情偷乱人成视频在线观看 | 日韩av在线导航 | 少妇又色又紧又黄又刺激免费 | 国产精品7777777 | 国产激情一区二区三区视频免樱桃 | 午夜精品久久久久久久99热浪潮 | 亚洲成a人v | 国产农村av | 国产精品第72页 | 日韩av图片 | 看毛片看毛片 | 久久香蕉精品 | 亚洲av无一区二区三区久久 | 欧美午夜精品久久久久久浪潮 | 致命弯道8在线观看免费高清完整 | 深夜福利影院 | 中国爆后菊女人的视频 | 一级片在线观看视频 | 久久久精品网站 | 久久9966 | 人人妻人人澡人人爽国产一区 | 中文字幕av不卡 | 九色porny自拍视频在线播放 | 9999国产精品 | 一级大片儿 | 国产69精品一区二区 | 国内自拍xxxx18 | 天天爽天天| 少妇被躁爽到高潮无码文 | 亚洲人成免费电影 | 热久久最新网址 | 无人码人妻一区二区三区免费 | 久久视频精品 | 黄色香蕉视频 | 国产日韩欧美在线观看 | 精品国产乱码久久久久久预案 | 国产一卡二卡三卡四卡 | 操操操综合| 性高跟鞋xxxxhd人妖 | 在线欧美色 | 亚洲综合五月 | 日本一区二区三区视频在线播放 | 黄色网免费 | 黑人巨大精品人妻一区二区 | 久久久久久久久久免费视频 | 在线观看黄网址 | 成年男女免费视频网站 | 中文无码av一区二区三区 | 亚洲国产毛片 | 三上悠亚中文字幕在线播放 | 少妇被躁爽到高潮无码人狍大战 | 超碰黄色 | 麻豆天天躁天天揉揉av | 国产资源在线观看 | 天堂在线免费观看视频 | 成人爽a毛片一区二区 | 韩国福利一区 | 性欧美另类 | 久久久国产精华液999999 | wwwww国产| 婷婷五月在线视频 | 国产精品久久在线 | 超碰福利在线观看 | 男女插孔视频 | 国产女同91疯狂高潮互磨 | 免费一级特黄3大片视频 | 爱爱视频免费网站 | 亚洲无码国产精品 | a人片| 小视频在线 | 国产精品呻吟久久 | 爱插视频 | 天天摸夜夜爽 | 天天摸日日摸 | 久久青草视频 |