<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android 組件化多module依賴優(yōu)雅方案(建議收藏)

          共 10852字,需瀏覽 22分鐘

           ·

          2021-12-23 01:32

          作者:leobert-lan, 鏈接:https://juejin.cn/post/6925629544946892813

          背景

          如果沒有記錯(cuò),15年那會(huì)Android項(xiàng)目逐步轉(zhuǎn)向使用Gradle構(gòu)建,時(shí)至今日,組件化已經(jīng)不再是一個(gè)新穎的話題。

          雖然我將這篇文章放在了Gradle分類中,但是我們知道,使用gradle構(gòu)建的后端項(xiàng)目, 熱點(diǎn)聚焦在:實(shí)現(xiàn)微服務(wù)化,項(xiàng)目是拆開的,決定了依賴庫已經(jīng)是靜態(tài)jar包,和我們 要討論的場景是不一致的。所以我們還是在Android領(lǐng)域中討論這個(gè)問題。

          在各種方案的組件化實(shí)施中,一定會(huì)將部分功能模塊拆分,進(jìn)行l(wèi)ibrary下沉。于是,就有了處理依賴的場景。相信大家思考過這樣一個(gè)問題:如果下沉的library也提前編譯好靜態(tài)aar包,我們的項(xiàng)目編譯時(shí)間會(huì)縮短。

          毋庸置疑,這樣做會(huì)直接從源頭解決 編譯時(shí)間長的問題,就是減少編譯內(nèi)容。但是,項(xiàng)目合并在一起,難免就想在開發(fā)下層library時(shí),直接用上層業(yè)務(wù)集成進(jìn)行冒煙。ps:這個(gè)做法并不好,應(yīng)當(dāng)為library配置好冒煙測試環(huán)境,雖然會(huì)耗費(fèi)掉一定的時(shí)間。

          理想歸理想,最終還是會(huì)敗給現(xiàn)實(shí),這個(gè)問題就變成了魚和熊掌想要兼得的問題。

          為了讓閱讀的目標(biāo)更加明確,我們先思考一個(gè)問題:

          這樣一個(gè)項(xiàng)目依賴關(guān)系,如果做到改動(dòng)B 的內(nèi)容,卻不需要重新編譯A,運(yùn)行APP,驗(yàn)證B的修改 我們下面會(huì)進(jìn)行一定地展開,來體悟這個(gè)問題。

          為什么使用遠(yuǎn)程倉庫中的依賴包比使用本地靜態(tài)aar要方便

          我們知道,對于一個(gè)module,我們對其進(jìn)行編譯生成靜態(tài)aar包,只會(huì)處理它自身的內(nèi)容。那么他的依賴是如何傳遞的?

          通過pom文件

          舉個(gè)例子:

          我們新建一個(gè)module,看一下依賴:

          dependencies?{

          ????implementation?"org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
          ????implementation?'androidx.core:core-ktx:1.3.2'
          ????implementation?'androidx.appcompat:appcompat:1.2.0'
          ????implementation?'com.google.android.material:material:1.2.1'
          ????testImplementation?'junit:junit:4.+'
          ????androidTestImplementation?'androidx.test.ext:junit:1.1.2'
          ????androidTestImplementation?'androidx.test.espresso:espresso-core:3.3.0'
          }

          利用maven plugin?進(jìn)行發(fā)布,會(huì)有任務(wù)生成pom文件,如下:


          <project?xsi:schemaLocation="http://maven.apache.org/POM/4.0.0?http://maven.apache.org/xsd/maven-4.0.0.xsd"?xmlns="http://maven.apache.org/POM/4.0.0"
          ????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

          ??<modelVersion>4.0.0modelVersion>
          ??<groupId>leobertgroupId>
          ??<artifactId>BartifactId>
          ??<version>1.0.0version>
          ??<packaging>aarpackaging>
          ??<dependencies>
          ????<dependency>
          ??????<groupId>org.jetbrains.kotlingroupId>
          ??????<artifactId>kotlin-stdlibartifactId>
          ??????<version>1.4.21version>
          ??????<scope>compilescope>
          ????dependency>
          ????<dependency>
          ??????<groupId>androidx.coregroupId>
          ??????<artifactId>core-ktxartifactId>
          ??????<version>1.3.2version>
          ??????<scope>compilescope>
          ????dependency>
          ????<dependency>
          ??????<groupId>androidx.appcompatgroupId>
          ??????<artifactId>appcompatartifactId>
          ??????<version>1.2.0version>
          ??????<scope>compilescope>
          ????dependency>
          ????<dependency>
          ??????<groupId>com.google.android.materialgroupId>
          ??????<artifactId>materialartifactId>
          ??????<version>1.2.1version>
          ??????<scope>compilescope>
          ????dependency>
          ??dependencies>
          project>

          我們發(fā)現(xiàn),關(guān)于測試相關(guān)的依賴并沒有被收錄到pom文件中。這很合理,測試代碼是針對該module的,并不需要提供給使用方,其依賴自然也不需要傳遞。我們知道,AGP中現(xiàn)在有4種聲明依賴的方式(除去testXXX這種變種)

          • api
          • implementation
          • compileOnly
          • runtimeOnly

          runtimeOnly對應(yīng)以前的apk方式聲明依賴,我們直接忽略掉,測試一下生成的pom文件。

          dependencies?{

          ????api?"org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
          ????implementation?'androidx.core:core-ktx:1.3.2'
          ????compileOnly?'androidx.appcompat:appcompat:1.2.0'
          ????compileOnly?'com.google.android.material:material:1.2.1'


          ????testImplementation?'junit:junit:4.+'
          ????androidTestImplementation?'androidx.test.ext:junit:1.1.2'
          ????androidTestImplementation?'androidx.test.espresso:espresso-core:3.3.0'
          }

          <project?xsi:schemaLocation="http://maven.apache.org/POM/4.0.0?http://maven.apache.org/xsd/maven-4.0.0.xsd"?xmlns="http://maven.apache.org/POM/4.0.0"
          ????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

          ??<modelVersion>4.0.0modelVersion>
          ??<groupId>leobertgroupId>
          ??<artifactId>BartifactId>
          ??<version>1.0.0version>
          ??<packaging>aarpackaging>
          ??<dependencies>
          ????<dependency>
          ??????<groupId>org.jetbrains.kotlingroupId>
          ??????<artifactId>kotlin-stdlibartifactId>
          ??????<version>1.4.21version>
          ??????<scope>compilescope>
          ????dependency>
          ????<dependency>
          ??????<groupId>androidx.coregroupId>
          ??????<artifactId>core-ktxartifactId>
          ??????<version>1.3.2version>
          ??????<scope>compilescope>
          ????dependency>
          ??dependencies>
          project>

          使用compileOnly方式的并沒有被收錄到pom文件中,而apiimplementation?方式,在pom文件中,都表現(xiàn)為 采用compile的方案應(yīng)用依賴。

          ps:apiimplementation在編碼期的不同,不是我們討論的重點(diǎn),略。

          回到我們開始的問題,將library發(fā)布時(shí),按照約定,會(huì)將library本身的依賴收錄到pom文件中。相應(yīng)的,使用方使用 倉庫中的依賴項(xiàng)時(shí),gradle會(huì)拉取其對應(yīng)的pom文件,并添加依賴。

          所以,如果我們直接使用一個(gè)編譯好的靜態(tài)包,而丟棄了他對應(yīng)的pom文件時(shí),可能會(huì)丟失依賴,出現(xiàn)打包失敗或者運(yùn)行異常。這意味著我們需要人為維護(hù)依賴傳遞

          我們記住這些內(nèi)容,并先放到一邊。

          下沉后,library會(huì)有多個(gè)層級

          例如圖中:APP => A => B, 即APP依賴A,A依賴B,而A和B都是library

          我們知道,對于B,并不會(huì)有什么說法,只會(huì)出現(xiàn)在A和APP

          如果不使用靜態(tài)包,那么A會(huì)聲明:

          api?project(':B')
          //或者
          implementation?project(':B')

          我們先看一下,這樣生成的library-Apom文件


          <project?xsi:schemaLocation="http://maven.apache.org/POM/4.0.0?http://maven.apache.org/xsd/maven-4.0.0.xsd"?xmlns="http://maven.apache.org/POM/4.0.0"
          ????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

          ??<modelVersion>4.0.0modelVersion>
          ??<groupId>leobertgroupId>
          ??<artifactId>AartifactId>
          ??<version>1.0.0version>
          ??<packaging>aarpackaging>
          ??<dependencies>
          ????<dependency>
          ??????<groupId>DemogroupId>
          ??????<artifactId>BartifactId>
          ??????<version>unspecifiedversion>
          ??????<scope>compilescope>
          ????dependency>
          ??dependencies>
          project>

          會(huì)得到groupID是項(xiàng)目名,artifactIdmodule名,version是未知的一個(gè)依賴項(xiàng)。假如我將A編譯為靜態(tài)包并發(fā)布到倉庫,并運(yùn)用了pom中的依賴描述,一定會(huì)得到無法找到:Demo-B-unspecified.pom?的問題。

          當(dāng)然,這個(gè)問題可以通過在APP中重新聲明 B的依賴 來解決。

          這意味著,我們需要時(shí)刻保持警惕,維護(hù)各個(gè)module的依賴。否則,我們無法同時(shí)享受:靜態(tài)包減少編譯 & 隨心的修改局部并集成測試

          這顯然是一件不人道主義的事情。

          反思一下,對于A而言,它需要B,但僅在兩個(gè)時(shí)機(jī)需要:

          • 編譯時(shí)受檢,完成編譯
          • 運(yùn)行時(shí)

          作為一個(gè)library,它本身并不對應(yīng)運(yùn)行時(shí),所以,compileOnly?是其聲明對B的依賴的最佳方式。這意味著,最終對應(yīng)運(yùn)行時(shí) 的內(nèi)容,即APP,需要在編譯時(shí)加入 對B的依賴。在原先?A使用Api方式聲明對B的依賴時(shí),是通過gradle分析pom文件實(shí)現(xiàn)的依賴加入。而現(xiàn)在,需要人為維護(hù),只需要實(shí)現(xiàn) 人道主義,就可以魚和熊掌兼得。

          反思依賴傳遞的本質(zhì)

          一般我們會(huì)像下面的演示代碼一樣聲明依賴:

          //APP:
          implementation?project('A')
          implementation?project('Foo')

          //A:
          implementation?project('B')
          implementation?project('Bar')

          因?yàn)橐蕾噦鬟f性,APP其實(shí)依賴了AFooBBar。其實(shí)就是一顆樹中,除去根節(jié)點(diǎn)的節(jié)點(diǎn)集合。而對于一個(gè)非根節(jié)點(diǎn),它被依賴的形式只有兩種:

          • 靜態(tài)包,不需要重新編譯,節(jié)約編譯時(shí)間
          • module,需要再次編譯,可以運(yùn)用最新改動(dòng)

          我們可以定義這樣一個(gè)鍵值對信息:

          project.ext.depRules?=?[
          ????????"B":?"p",
          ????????"A":?"a"
          ]

          "p"代表使用project,"a"代表使用靜態(tài)包。

          并將這顆樹的內(nèi)容表達(dá)出來:我們先忽略掉Foo和Bar

          project.ext.deps?=?[
          ????????"A"??:?[
          ????????????????"B":?[
          ????????????????????????"p":?project(':B'),
          ????????????????????????"a":?'leobert:B:1.0.0'
          ????????????????]
          ????????],
          ????????"APP":?[
          ????????????????"A":?[
          ????????????????????????"p":?project(':A'),
          ????????????????????????"a":?'leobert:A:1.0.0'
          ????????????????]
          ????????]
          ].with(true)?{
          ????A.each?{?e?->
          ????????APP.put(e.key,?e.value)
          ????}
          }

          以A為例,我們可以通過代碼實(shí)現(xiàn)動(dòng)態(tài)添加依賴:

          project.afterEvaluate?{?p?->
          ????????println("handle?deps?for:"?+?p)
          ????????deps.A.each?{?e?->
          ????????????def?rule?=?depRules.get(e.key)
          ????????????println("find?deps?of?A:?rule?is"?+?rule?+?"?,dep?is:"?+?e.value.get(rule).toString())
          ????????????project.dependencies.add("compileOnly",?e.value.get(rule))
          ????????}
          ????}

          同理,對于APP:

          project.afterEvaluate?{?p->
          ????????println("handle?deps?for:"?+?p)
          ????????deps.APP.each?{?e?->
          ????????????def?rule?=?depRules.get(e.key)
          ????????????println("find?deps?of?App:rule?is"?+?rule?+?"?,dep?is:"?+?e.value.get(rule).toString())
          ????????????project.dependencies.add("implementation",?e.value.get(rule))
          ????????}
          ????}

          查看輸出:

          Configure?project?:A
          handle?deps?for:project?':A'
          find?deps?of?A:?rule?isp?,dep?is:project?':B'
          Configure?project?:app
          handle?deps?for:project?':app'
          find?deps?of?App:rule?isa?,dep?is:leobert:A:1.0.0
          find?deps?of?App:rule?isp?,dep?is:project?':B'

          這樣,我們就可以通過修改對應(yīng)節(jié)點(diǎn)的依賴方式配置而實(shí)現(xiàn)魚和熊掌兼得。不再受pom文件的約束。當(dāng)時(shí),我們回到上面說的不人道主義之處,我們通過了with?函數(shù),將A自身的依賴信息,注入到APP中。

          但是當(dāng)樹的規(guī)模變大時(shí),人為維護(hù)就很累了。這是必須要解決的,當(dāng)然,這很容易解決。我們直接使用遞歸處理即可

          貼近人的直觀感受才優(yōu)雅,逐步實(shí)現(xiàn)人道主義 我們添加一個(gè)全局閉包:

          ext.utils?=?[
          ????????applyDependency:?{?project,?e?->
          ????????????def?rule?=?depRules.get(e.key)
          ????????????println("find?deps?of?App:rule?is?"?+?rule?+?"?,dep?is:"?+?e.value.get(rule).toString())
          ????????????project.dependencies.add("implementation",?e.value.get(rule))

          ????????????try?{
          ????????????????println("try?to?add?sub?deps?of:"?+?e.key)
          ????????????????def?sub?=?deps.get(e.key)
          ????????????????if?(sub?!=?null?&&?sub.get("isEnd")?!=?true)?{
          ????????????????????sub.each?{?se?->
          ????????????????????????ext.utils.applyDependency(project,?se)
          ????????????????????}
          ????????????????}
          ????????????}?catch?(Exception?ignore)?{

          ????????????}
          ????????}
          ]

          注意,因?yàn)槲覀兌x的依賴信息是:moduleName-> (moduleName -> (scopeName-> depInfo))?的方式。

          這導(dǎo)致我們判斷末端節(jié)點(diǎn)有一定的困難,即遞歸的尾部判斷存在困難,我們需要人為標(biāo)記一下末端節(jié)點(diǎn) 這時(shí),我們只需描述一下樹即可:同樣忽略Foo,Bar

          project.ext.deps?=?[
          ????????"A"??:?[
          ????????????????"B":?[
          ????????????????????????"isEnd":?true,
          ????????????????????????"p"????:?project(':B'),
          ????????????????????????"a"????:?'leobert:B:1.0.0'
          ????????????????]
          ????????],
          ????????"APP":?[
          ????????????????"A":?[
          ????????????????????????"p":?project(':A'),
          ????????????????????????"a":?'leobert:A:1.0.0'
          ????????????????]
          ????????]
          ]

          問題基本得到解決了,但是并不優(yōu)雅。

          優(yōu)雅,優(yōu)雅,優(yōu)雅

          我們不妨再修改一下對依賴樹的描述方式,將節(jié)點(diǎn)信息和樹結(jié)構(gòu)分開,重新改進(jìn):

          更人道主義的依賴描述

          project.ext.deps?=?[
          ????????"A"??:?["B"],
          ????????"app":?["A"]
          ]

          project.ext.modules?=?[
          ????????"A":?[
          ????????????????"p":?project(':A'),
          ????????????????"a":?'leobert:A:1.0.0'
          ????????],
          ????????"B":?[
          ????????????????"p"????:?project(':B'),
          ????????????????"a"????:?'leobert:B:1.0.0'
          ????????]
          ]

          project.ext.depRules?=?[
          ????????"B":?"p",
          ????????"A":?"a"
          ]

          抽象添加依賴的過程,遞歸處理每一個(gè)節(jié)點(diǎn)的依賴收集,并向宿主module添加,當(dāng)某個(gè)節(jié)點(diǎn)在ext.deps中沒有任何依賴時(shí),歸:

          ext.utils?=?[
          ????????????applyDependency:?{?project,?scope,?e?->
          ????????????????def?rule?=?depRules.get(e)
          ????????????????def?eInfo?=?ext.modules.get(e)
          ????????????????println("find?deps?of?"?+?project?+?":rule?is?"?+?rule?+?"?,dep?is:"?+?eInfo.get(rule).toString())
          ????????????????project.dependencies.add(scope,?eInfo.get(rule))

          ????????????????def?sub?=?deps.get(e)?//list?deps?of?e
          ????????????????println("try?to?add?sub?deps?of:"?+?e?+?"?--->?"?+?sub)

          ????????????????if?(sub?!=?null?&&?!sub.isEmpty())?{
          ????????????????????sub.each?{?dOfE?->
          ????????????????????????ext.utils.applyDependency(project,?scope,?dOfE)
          ????????????????????}
          ????????????????}
          ????????????}
          ????]

          每個(gè)module只需要指定自己的scope

          //:app
          project.afterEvaluate?{?p?->
          ????println("handle?deps?for:"?+?p)
          ????deps.get(p.name).each?{?e?->
          ????????rootProject.ext.utils.applyDependency(p,"implementation",e)
          ????}
          }

          //:A
          project.afterEvaluate?{?p?->
          ????println("handle?deps?for:"?+?p.name)
          ????deps.get(p.name).each?{?e?->
          ????????rootProject.ext.utils.applyDependency(p,"compileOnly",e)
          ????}
          }

          只要不是獨(dú)立運(yùn)行的module,就是compileOnly,否則就是?implementation。輸出也容易拍錯(cuò):

          >?Configure?project?:A
          handle?deps?for:A
          find?deps?of?project?':A':rule?is?p?,dep?is:project?':B'
          try?to?add?sub?deps?of:B?--->?null

          >?Configure?project?:app
          handle?deps?for:project?':app'
          find?deps?of?project?':app':rule?is?a?,dep?is:leobert:A:1.0.0
          try?to?add?sub?deps?of:A?--->?[B]
          find?deps?of?project?':app':rule?is?p?,dep?is:project?':B'
          try?to?add?sub?deps?of:B?--->?null

          測試一個(gè)復(fù)雜場景 我們再上圖的基礎(chǔ)上,讓BFoo依賴Base

          project.ext.deps?=?[
          ????????"app":?["A",?"Foo"],
          ????????"A"??:?["B",?"Bar"],
          ????????"Foo":?["Base"],
          ????????"B"??:?["Base"],
          ]

          project.ext.modules?=?[
          ????????"A":?[
          ????????????????"p":?project(':A'),
          ????????????????"a":?'leobert:A:1.0.0'
          ????????],
          ????????"B":?[
          ????????????????"p":?project(':B'),
          ????????????????"a":?'leobert:B:1.0.0'
          ????????],
          ????????"Foo":?[
          ????????????????"p":?project(':Foo'),
          ????????],
          ????????"Bar":?[
          ????????????????"p":?project(':Bar'),
          ????????],
          ????????"Base":?[
          ????????????????"p":?project(':Base'),
          ????????]
          ]

          project.ext.depRules?=?[
          ????????"B"???:?"p",
          ????????"A"???:?"a",
          ????????"Foo"?:?"p",
          ????????"Bar"?:?"p",
          ????????"Base":?"p"
          ]
          >?Configure?project?:A
          handle?deps?for:A
          find?deps?of?project?':A':rule?is?p?,dep?is:project?':B'
          try?to?add?sub?deps?of:B?--->?[Base]
          find?deps?of?project?':A':rule?is?p?,dep?is:project?':Base'
          try?to?add?sub?deps?of:Base?--->?null
          find?deps?of?project?':A':rule?is?p?,dep?is:project?':Bar'
          try?to?add?sub?deps?of:Bar?--->?null

          >?Configure?project?:app
          handle?deps?for:project?':app'
          find?deps?of?project?':app':rule?is?a?,dep?is:leobert:A:1.0.0
          try?to?add?sub?deps?of:A?--->?[B,?Bar]
          find?deps?of?project?':app':rule?is?p?,dep?is:project?':B'
          try?to?add?sub?deps?of:B?--->?[Base]
          find?deps?of?project?':app':rule?is?p?,dep?is:project?':Base'
          try?to?add?sub?deps?of:Base?--->?null
          find?deps?of?project?':app':rule?is?p?,dep?is:project?':Bar'
          try?to?add?sub?deps?of:Bar?--->?null
          find?deps?of?project?':app':rule?is?p?,dep?is:project?':Foo'
          try?to?add?sub?deps?of:Foo?--->?[Base]
          find?deps?of?project?':app':rule?is?p?,dep?is:project?':Base'
          try?to?add?sub?deps?of:Base?--->?null

          >?Configure?project?:Bar
          handle?deps?for:Bar

          >?Configure?project?:Base
          handle?deps?for:Base

          >?Configure?project?:Foo
          handle?deps?for:Foo
          find?deps?of?project?':Foo':rule?is?p?,dep?is:project?':Base'
          try?to?add?sub?deps?of:Base?--->?null

          隨著,樹規(guī)模的增大,閱讀依賴關(guān)系還算明顯,但是閱讀日志,又不太優(yōu)雅了。

          總結(jié)和展望

          我們通過探尋,發(fā)現(xiàn)了一種可以 魚和熊掌兼得 地依賴處理方式,讓我們在Android領(lǐng)域組件化場景下(單項(xiàng)目,多module),能夠靈活地切換:

          • 靜態(tài)包依賴,縮短編譯時(shí)間
          • 項(xiàng)目依賴,快速部署變更進(jìn)行集成測試

          對了,上面我們沒有重點(diǎn)提到如何切換,其實(shí)非常地簡單:

          只需要修改?project.ext.depRules?中對應(yīng)的配置項(xiàng)即可。

          如果后面還有閑情逸致的話,可以再寫一個(gè)studio的插件,獲取?dependency.gradle?的信息, 輸出可視化的依賴樹;rule配置,直接做成多個(gè)開關(guān),優(yōu)雅,永不過時(shí)。





          ? 耗時(shí)2年,Android進(jìn)階三部曲第三部《Android進(jìn)階指北》出版!

          ? 『BATcoder』做了多年安卓還沒編譯過源碼?一個(gè)視頻帶你玩轉(zhuǎn)!

          ? 『BATcoder』我去!安裝Ubuntu還有坑?

          ? 重生!進(jìn)階三部曲第一部《Android進(jìn)階之光》第2版 出版!

          ?BATcoder技術(shù)群,讓一部分人先進(jìn)大廠

          大家,我是劉望舒,騰訊TVP,著有三本業(yè)內(nèi)知名暢銷書,連續(xù)四年蟬聯(lián)電子工業(yè)出版社年度優(yōu)秀作者,百度百科收錄的資深技術(shù)專家。


          想要加入?BATcoder技術(shù)群,公號(hào)回復(fù)BAT?即可。

          為了防止失聯(lián),歡迎關(guān)注我的小號(hào)


          ??微信改了推送機(jī)制,真愛請星標(biāo)本公號(hào)??
          瀏覽 65
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  大陆91视频 | 国产老女人操逼视频 | 手机av免费 | 国产免费操逼视频 | 欧洲色图亚洲色图 |