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

安卓to鴻蒙系列:ButterKnife(二)

系統
想讀懂ButterKnife難度還是很大的,一方面自己菜,另一方面代碼量大,代碼結構復雜。而且很多概念不好理解(比如javapoet引入的各種類,綁定信息相關的幾個類BingdingSet、BingdingSet.Builder、xxxBingding等)。

想了解更多內容,請訪問:

51CTO和華為官方合作共建的鴻蒙技術社區

https://harmonyos.51cto.com

本文基于https://gitee.com/openharmony-tpc/butterknife 分析JakeWharton/butterknife的源碼,及移植到鴻蒙需要做的工作。

如果對apt的概念及實踐不熟悉,請先移步:安卓to鴻蒙系列:ButterKnife(一),然后再來閱讀本文,會事半功倍!

butterknife項目結構:

和我們在安卓to鴻蒙系列:ButterKnife(一)寫的“乞丐版BufferKnife”一樣,主要由三個module組成

  • butterknife_annotations//編譯時、運行時都用到

定義了注解

  • butterknife_compiler//編譯時用到

apt的主要實現部分。注解的解析、處理,生成模板文件

  • butterknife//運行時用到

對外的工具類,供用戶使用,完成注入操作。 butterknife_runtime 定義運行時用到的一些類及工具方法。

移植butterknife_annotations

直接對比openharmony-tpc和JakeWharton的這個module吧:

可以看到有增、刪、改。

其中增加和刪除的是一一對應的。

  1. BindBitmap vs BindPixelMap//安卓的Bitmap對應鴻蒙的PixelMap 
  2.  
  3. BindView vs BindComponent//安卓的View對應鴻蒙的Component 
  4.  
  5. BindViews vs BindComponents//同上 
  6.  
  7. BindDrawable vs BindElement //安卓的Drawable對應鴻蒙的ohos.agp.components.element.Element 

其它都是修改,以BindingString為例,只是去掉了鴻蒙沒有的注解StringRes、修改注釋。

ps:對于鴻蒙沒有的注解,還有一個辦法就是把androidx或support包下相應的文件copy進來,并且包名也保持一致,這樣我們就不需要修改BindingString這一類文件了。經過對比發現,可以減少不小的工作量。

還有一些修改稍復雜一些,以OnClick為例。只要寫過兩個平臺的代碼,還是很容易理解的,只是做相應的等價替換。

分析butterknife_compiler的源碼

優秀資源參考:

靜態分析

1.主要的幾個類

  • ButterKnifeProcessor//注解入口類,apt程序必須繼承AbstractProcessor,沒什么好說的。
  • BindingSet//從名字可知,這個類是綁定信息的集合。舉例:MainAbilitySlice對應一個BindingSet,也就對應一個xxxx_ViewBinding。

BindingSet的實例存在于編譯期,執行它的brewJava()方法生成xxxx_ViewBinding文件。

  • 各種XxxBinding,如:FieldViewBinding、MethodViewBinding、ResourceBinding的各種子類,表示某字段的綁定信息。

以MainAbilitySlice和FieldViewBinding為例,如下注解代碼:

  1. //MainAbilitySlice 
  2. @BindComponent(ResourceTable.Id_viewRoot) 
  3. DirectionalLayout mDlViewRoot; 

 生成一個FieldViewBinding實例,其值為:

  1. final class FieldViewBinding implements MemberViewBinding { 
  2.     private final String name;//"mDlViewRoot" 
  3.     private final TypeName type;//DirectionalLayout 
  4.     private final boolean required;//true 
  • ViewBinding//表示某個控件的綁定信息,其中包括field和method(對于各種事件綁定)。如下代碼所示:
  1. final class ViewBinding { 
  2.     private final Id id; 
  3.     private final Map<ListenerClass, Map<ListenerMethod, Set<MethodViewBinding>>> methodBindings; 
  4.     private final FieldViewBinding fieldBinding; 

2.ButterKnifeProcessor#process()方法相當注解執行的main方法(會進入多次),主要干了兩件事findAndParseTargets()和brewJava(),如下圖所示:

用到的工具:SequenceDiagram - IntelliJ IDEA插件,直接在插件市場搜索、安裝就行,用來生成方法調用時序圖

3.其中ButterKnifeProcessor#findAndParseTargets()的主要功能是找到并解析各個targets,如下圖所示:

ps:這個圖省略了很多parseXXX()方法,只保留了一個parseBindComponent(),因為功能類似。不然圖太長了。

由上圖可知findAndParseTargets()方法實現了以下三件事:

解析注入對象相關的注解parseXXX()

處理@BindComponent,@BindString 之類的組件或資源

  1. //添加注釋、刪掉多余代碼的parseBindComponent() 
  2. private void parseBindComponent(Element element, Map<TypeElement, BindingSet.Builder> builderMap, 
  3.                                 Set<TypeElement> erasedTargetNames) { 
  4.     //獲取當前元素element的類級別的元素,即XXXAbility 
  5.     TypeElement enclosingElement = (TypeElement) element.getEnclosingElement(); 
  6.     
  7.     //獲取@BindComponent的值,即ResourceTable.Id_xxx 
  8.     int id = element.getAnnotation(BindComponent.class).value(); 
  9.      
  10.     //根據當前元素的類級別的元素先從Map中獲取BindingSet的內部類Builder 
  11.     BindingSet.Builder builder = builderMap.get(enclosingElement); 
  12.     Id resourceId = elementToId(element, BindComponent.class, id); 
  13.      
  14.     //builder為空,說明當前類還沒有對應的value,需要new一個出來,并放到builderMap中 
  15.     //builder會被BindComponent和OnClick等共用一個(享元模式?),并且它們以XXXAbility分組放在builderMap中。 
  16.     if (builder != null) { 
  17.         String existingBindingName = builder.findExistingBindingName(resourceId); 
  18.         if (existingBindingName != null) { 
  19.             return
  20.         } 
  21.     } else { 
  22.         builder = getOrCreateBindingBuilder(builderMap, enclosingElement); 
  23.     } 
  24.  
  25.     //@BindComponent修飾的字段的簡單名稱,即變量名比如mTextView 
  26.     String name = simpleName.toString(); 
  27.     //@BindComponent修飾的字段的類型,比如Text 
  28.     TypeName type = TypeName.get(elementType); 
  29.     //是否被@Nullable修飾 
  30.     boolean required = isFieldRequired(element); 
  31.  
  32.     //調用BindingSet的內部類Builder中的addField方法封裝解析的信息 
  33.     builder.addField(resourceId, new FieldViewBinding(name, type, required)); 
  34.      
  35.     // Add the type-erased version to the valid binding targets set
  36.     //給所有含有自定義注解的類組成的Set集合中添加元素 
  37.     erasedTargetNames.add(enclosingElement); 
  • 解析事件綁定相關的注解findAndParseListener()

處理@OnClick,@OnItemClick,@OnTextChanged 之類的Listener

通過以上兩步,完成了注解信息的掃描收集,并將解析的信息保存到builderMap和erasedTargetNames兩個集合中;

  • findAllSupertypeBindings(),findParentType(),及findAndParseTargets()

第三步,對上面提到的builderMap和erasedTargetNames兩個集合中的信息進行重新整理,最終返回一個以TypeElement為key,BindingSet為vaule的bindingMap集合。

下面直接帖學習筆記ButterKnife的分析吧:

  1. private Map<TypeElement, BindingSet> findAndParseTargets1(RoundEnvironment env) { 
  2.     //這個不是最后返回的對象,這個只是BindingSet對應的Builder類,保存了BindComponent、BindString、OnClick等等相關的綁定信息 
  3.     Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>(); 
  4.     Set<TypeElement> erasedTargetNames = new LinkedHashSet<>(); 
  5.  
  6.     //隱藏代碼 @BindXxx-------------------- 
  7.  
  8.     //綁定界面上的View 
  9.     for (Element element : env.getElementsAnnotatedWith(BindComponent.class)) { 
  10.         parseBindComponent(element, builderMap, erasedTargetNames); 
  11.     } 
  12.  
  13.     //隱藏代碼 bindListener.--------------- 
  14.  
  15.     Map<TypeElement, ClasspathBindingSet> classpathBindings = 
  16.             findAllSupertypeBindings(builderMap, erasedTargetNames); 
  17.     //組合所有類的關系  組成 樹 
  18.     //這里注釋也寫了,用隊列的方式,將超類與子類綁定,從根開始 
  19.     // Associate superclass binders with their subclass binders. This is a queue-based tree walk 
  20.     // which starts at the roots (superclasses) and walks to the leafs (subclasses). 
  21.     //這個獲取的所有的“類”  放入了隊列 
  22.     Deque<Map.Entry<TypeElement, BindingSet.Builder>> entries = 
  23.             new ArrayDeque<>(builderMap.entrySet()); 
  24.     //即將返回的對象 
  25.     Map<TypeElement, BindingSet> bindingMap = new LinkedHashMap<>(); 
  26.     while (!entries.isEmpty()) { 
  27.         Map.Entry<TypeElement, BindingSet.Builder> entry = entries.removeFirst(); 
  28.  
  29.         TypeElement type = entry.getKey(); 
  30.         BindingSet.Builder builder = entry.getValue(); 
  31.         //拿隊列出來的第一個 在erasedTargetNames中查詢,父類是否在這個這個集合里 
  32.         //為什么要這樣? 因為可能 你在父類中 綁定了一個String 
  33.         //在子類中使用了這個String,所以必須先初始化父類的String 
  34.         //翻看生成好的代碼來看,初始化都是在構造函數中進行綁定的 
  35.         //所以考慮繼承情況,必須把所有的類進行一個關聯。 
  36.         TypeElement parentType = findParentType(type, erasedTargetNames, classpathBindings.keySet()); 
  37.         if (parentType == null) { 
  38.             //如果沒有父類,則直接 放入 
  39.             bindingMap.put(type, builder.build()); 
  40.         } else { 
  41.             //如果父類有綁定 
  42.             //再從bindingMap(即將返回的對象)中取看看 是否已經放進去了 
  43.             BindingInformationProvider parentBinding = bindingMap.get(parentType); 
  44.             if (parentBinding == null) { 
  45.                 //如果沒綁定進去 再從classpathBindings中取 一個父類 
  46.                 parentBinding = classpathBindings.get(parentType); 
  47.             } 
  48.             if (parentBinding != null) { 
  49.                 //如果這個父類不是空的 則和當前循環里的builder  子、父類綁定 
  50.                 builder.setParent(parentBinding); 
  51.                 //放入即將返回的map里 
  52.                 bindingMap.put(type, builder.build()); 
  53.             } else { 
  54.                 //翻譯是:有個超類綁定,但還沒有構建它,放到后面繼續排隊 
  55.                 // Has a superclass binding but we haven't built it yet. Re-enqueue for later. 
  56.                 //比如:子類繼承父類都有綁定,這時候子類需要關聯父類,父類還沒初始化, 
  57.                 //把子類放到隊尾,等父類初始化完成在進行關聯 
  58.                 entries.addLast(entry); 
  59.             } 
  60.         } 
  61.     } 
  62.     //返回,這時候這個map的key 就全是“類信息” 
  63.     //value就是獲取好的 當前類里面需要綁定的內容 
  64.     //并且 “類” 也已經做好了繼承關系 
  65.     return bindingMap; 

總結一下這個方法,就像它的方法名一 樣 “找到并解析各個targets” 。這里有個疑問,targets是什么呢? targets就是待注入的字段、待綁定事件的方法, 比如下面代碼中的mTextView就是target,即“待注入的字段”

  1. @BindComponent(ResourceTable.Id_tv_hello) 
  2. Text mTextView; 

動態分析

動態分析主要是驗證一下上面靜態分析的結論。對于比較復雜的代碼,需要debug跟一下代碼,查看運行時關鍵變量的值。

怎么調試butterknife_compiler?

參考:https://www.w3ma.com/how-to-debug-an-annotation-processor-in-android-studio/

1.新建一個remote debug,比如命名為aptDebug

因為apt過程在編譯期,所以需要remote debug。什么是remote debug,可以自己google一下。

2.在butterknife根目錄的命令行中運行gradlew --no-daemon -Dorg.gradle.debug=true :entry:clean :entry:compileDebugJavaWithJavac,編譯過程處于等待調試的狀態,如下如:

:entry:clean加上它是表示重新構建。

-Dorg.gradle.debug=true設置為true時,Gradle將在啟用遠程調試的情況下運行構建,偵聽端口5005。這等效于將-agentlib:jdwp = transport = dt_socket,server = y,suspend = y,address = 5005添加到 JVM命令行,它將掛起虛擬機,直到連接了調試器。

3.設置斷點,然后點擊小蟲子debug按鈕。

4.以debug parseBindComponent()為例:

小技巧:條件斷點在這里會提高調試的效率。自己google一下。

通過debug跟代碼,可知void parseBindComponent(Element element, Map<TypeElement, BindingSet.Builder> builderMap, Set<TypeElement> erasedTargetNames)方法的輸入element為@BindComponent注解的java元素,輸出builderMap和erasedTargetNames。

其中BinderingSet$Builder為BinderingSet的構建者,這里用到了Builder構建者設計模式。

其中BinderingSet記錄了模板類XXX_ViewBinding的生成規則。

在parseBindComponent方法中,調用Builder的addField添加了一條生成java文件的規則,即注入View。其它的parseXXX()方法類似。總結一下:

  1. parseBindComponent()//調用Builder的`addField`,注入View。 
  2. parseResourceXXX()//調用Builder的`addResource`,注入各種資源,如String、intfloat、Dimen、Color、Array、PixelMap等。 
  3. findAndParseListener()//調用Builder的`addMethod`,綁定各種事件。 

5.debug Map<TypeElement, BindingSet> findAndParseTargets()

先說上面提到 targets是什么呢?,通過跟代碼,可知targets是被注解的Element, 比如MainAblilitySlice中的mDlViewRoot

  1. @BindComponent(ResourceTable.Id_viewRoot) 
  2. DirectionalLayout mDlViewRoot; 

方法的返回值是類和它的模板類的一一對應。

  1. "com.example.butterknife.slice.MainAbilitySlice" -> "com.example.butterknife.slice.MainAbilitySlice_ViewBinding" 

日志分析:

debug的效率其實很低。聽說10倍程序員都愛打日志。所以,我們也要知道apt的messager的用法及注意事項有哪些?

雖然System.out.println();也可以打日志。但是messager會根據日志類型,把Kind.WARNING和Kind.ERROR類型的日志做統計,方便我們定位問題。

在ButterKnifeProcessor中有封裝messager的幾個方法:

  1. private void error(Element element, String message, Object... args) { 
  2.     printMessage(Kind.ERROR, element, message, args); 
  3.  
  4. private void note(Element element, String message, Object... args) { 
  5.     printMessage(Kind.NOTE, element, message, args); 
  6.  
  7. private void printMessage(Kind kind, Element element, String message, Object[] args) { 
  8.     if (args.length > 0) { 
  9.         message = String.format(message, args); 
  10.     } 
  11.  
  12.     processingEnv.getMessager().printMessage(kind, message, element); 

注意: 一定要執行gradlew --no-daemon :entry:clean :entry:compileDebugJavaWithJavac,compileDebugJavaWithJavac這個task。而且加上clean。不然看不到日志。

向apt程序傳參:

在ButterKnifeProcessor中覆寫了getSupportedOptions(),這樣我們可以向apt傳參了。

  1. @Override 
  2. public Set<String> getSupportedOptions() { 
  3.     ImmutableSet.Builder<String> builder = ImmutableSet.builder(); 
  4.     builder.add(OPTION_SDK_INT, OPTION_DEBUGGABLE); 
  5.     if (trees != null) { 
  6.         builder.add(IncrementalAnnotationProcessorType.ISOLATING.getProcessorOption()); 
  7.     } 
  8.     return builder.build(); 

 傳參方法:在entry中

  1. ohos { 
  2.     compileSdkVersion 5 
  3.     defaultConfig { 
  4.         compatibleSdkVersion 5 
  5.  
  6.         javaCompileOptions { 
  7.             annotationProcessorOptions { 
  8.                 arguments = ['butterknife.debuggable'"true"
  9.             } 
  10.         } 
  11.     } 

在init()方法中,我們可以取出傳入的值:

  1. @Override 
  2. public synchronized void init(ProcessingEnvironment env) { 
  3.     super.init(env); 
  4.     debuggable = !"false".equals(env.getOptions().get(OPTION_DEBUGGABLE)); 
  5.     env.getMessager().printMessage(Kind.NOTE, "--------------- debuggable = "+debuggable); 

移植butterknife_compiler

通過上面的代碼分析,我們知道在安卓和鴻蒙上butterknife_compiler的基本流程沒有變,所以移植工作要做的就是因為平臺class及api不同,做一些相應的調整。

對比代碼后,差異不大。簡單列舉一下:

  1. findViewById的修改
  2. Resource相關api的修改
  3. Font的修改(NORMAL變成REGULAR)
  4. BindAnim在鴻蒙版上暫不支持
  5. COLOR_STATE_LIST在鴻蒙版上暫不支持

移植butterknife_runtime

butterknife_runtime同時被butterknife_compiler和butterknife兩個module依賴,其中大多是一些工具類,同樣,做相應的api修改就可以。

對比代碼后,差異比較大。但是都集中的對資源Resource的加載差異上,以及androidx.annotation.UiThread之類的注解(直接刪掉就好)。

移植butterknife

butterknife這個module只有一個類ButterKnife,這個類的作用就是通過反射實例化XXX_ViewBinding,并提供一系列靜態方法如Unbinder bind(Ability target)來實現target中變量的注入和方法的綁定。同樣,做相應的api修改就可以。

歡迎有興趣的朋友可以完善entry中的用例

目前該庫有一些bug,比如:Unbinder bind(Ability target)注入Ability會失敗。我相信,bug不止這一個。

發現bug,提issue。我們一起將它完善。

總結

距離寫完安卓to鴻蒙系列:ButterKnife(一)已經有兩個多月,當時,寫一個乞丐版ButterKnife覺得還是很easy的。但是,想讀懂ButterKnife難度還是很大的,一方面自己菜,另一方面代碼量大,代碼結構復雜。而且很多概念不好理解(比如javapoet引入的各種類,綁定信息相關的幾個類BingdingSet、BingdingSet.Builder、xxxBingding等)。

想了解更多內容,請訪問:

51CTO和華為官方合作共建的鴻蒙技術社區

https://harmonyos.51cto.com

 

責任編輯:jianghua 來源: 鴻蒙社區
相關推薦

2021-05-11 14:43:16

鴻蒙HarmonyOS應用

2021-04-27 09:22:28

鴻蒙HarmonyOS應用

2021-04-26 09:46:10

鴻蒙HarmonyOS應用

2013-12-12 16:51:43

安卓進化AndroidGoogle

2019-06-20 16:07:12

鴻蒙安卓操作系統

2020-09-10 09:30:03

鴻蒙安卓操作系統

2014-08-04 14:21:22

安卓架構

2016-12-14 14:43:11

ButterknifeAndroid

2012-02-06 10:10:40

安卓iOS美國市場

2013-11-04 14:49:34

安卓

2018-02-09 08:59:47

安卓FuchsiaiOS

2019-07-12 16:00:25

華為禁令開發

2021-05-18 15:44:13

IOS安卓鴻蒙

2021-06-04 05:13:22

鴻蒙

2013-04-24 11:33:50

安卓

2014-12-09 11:15:06

郵箱安卓移動端

2013-10-17 10:17:41

安卓

2011-10-18 13:33:02

思亞諾CMMBDTV

2020-09-29 13:03:45

安卓應用開發工具開發

2021-02-25 10:40:00

數據
點贊
收藏

51CTO技術棧公眾號

一区二区高清不卡| 国产精品久免费的黄网站| 97色婷婷成人综合在线观看| 日韩美女久久久| 国产精品国模大尺度私拍| 国产成人无码一区二区三区在线| 精品大片一区二区| 日韩一区二区三区精品视频| 国产精品后入内射日本在线观看| 国产黄色片在线播放| 国产精品一区二区三区四区| 91av视频在线| 极品色av影院| 深爱激情综合| 精品久久99ma| 91亚洲免费视频| 手机在线观看av网站| 中文字幕欧美一区| 久久久久久国产精品一区| 在线观看免费黄色小视频| 激情久久久久久| 中文字幕亚洲欧美| 国产精品99久久久精品无码| 国产 日韩 欧美一区| 亚洲一级在线观看| 最新精品视频| 黄色免费在线播放| 成人av网址在线| 成人做爽爽免费视频| 免费黄色片视频| 在线亚洲欧美| 久久久久成人精品| 亚洲国产精品免费在线观看| 天堂俺去俺来也www久久婷婷| 日韩视频在线观看一区二区| 日本一二区免费| 浪潮色综合久久天堂| 五月激情丁香一区二区三区| 狠狠干视频网站| 日本三级视频在线观看| 国产视频911| 欧美连裤袜在线视频| 日本人妻熟妇久久久久久| 狠狠色综合色综合网络| 国产精品自在线| 日本妇乱大交xxxxx| 老鸭窝91久久精品色噜噜导演| 欧美福利在线观看| 国产av无码专区亚洲av毛网站| 日韩理论片av| 中文字幕av一区中文字幕天堂| 好吊日免费视频| 日韩精品免费一区二区三区竹菊| 精品久久久久久亚洲综合网 | 熟女少妇内射日韩亚洲| 女同久久另类99精品国产| 亚洲成年人在线播放| 国产精品入口日韩视频大尺度| 中文字幕一区二区三区精品 | 国内精品麻豆美女在线播放视频| 日韩一区二区三区四区五区六区| 小早川怜子一区二区三区| 亚洲图片小说区| 7777精品伊人久久久大香线蕉完整版 | 国产中文字幕久久| 999国产精品永久免费视频app| 日韩在线国产精品| 亚洲一级黄色录像| 91精品综合| 欧美乱妇高清无乱码| 真实国产乱子伦对白在线| 欧美aa国产视频| 欧美激情视频一区二区三区不卡| 国产精品变态另类虐交| 在线观看视频日韩| 欧美一级免费视频| 成人午夜精品视频| 国产精品自拍在线| 国产精品区一区二区三含羞草| 神马一区二区三区| 久久久久久久综合| 一区精品在线| 精品精品导航| 色婷婷久久久综合中文字幕| 182午夜在线观看| 在线视频亚洲欧美中文| 日韩高清不卡av| 丰满的亚洲女人毛茸茸| 亚洲成av人片乱码色午夜| 欧美极度另类性三渗透| 国产91国语对白在线| 亚洲不卡中文字幕无码| 成人久久网站| 日韩精品一区二区三区四区视频| 免费黄色三级网站| 久久免费精品视频在这里| 欧美另类老女人| 精品成人无码久久久久久| 精品一区二区久久久| 成人自拍爱视频| 国产一区二区三区福利| 亚洲欧美欧美一区二区三区| 老太脱裤子让老头玩xxxxx| 欧美aaa大片视频一二区| 日韩三级.com| 亚洲av成人无码久久精品| 一区二区影院| 国产精品第一第二| 国精品人妻无码一区二区三区喝尿| 久久亚洲综合色| 影音先锋成人资源网站| 成人做爰视频www网站小优视频| 欧美一区欧美二区| 国产美女永久免费无遮挡| 国内自拍一区| 国产日韩视频在线观看| 熟妇人妻中文av无码| 国产精品电影一区二区三区| 男人天堂网视频| 亚洲亚洲一区二区三区| 中文字幕日韩av| 久久不卡免费视频| 国产成人综合在线播放| 亚洲精品乱码视频| 97se综合| 日韩成人小视频| 国产性生活网站| 国产美女久久久久| 亚洲综合视频一区| 亚洲精品在线影院| 亚洲精品视频网上网址在线观看| 青娱乐国产在线视频| 久久激情五月婷婷| 日韩电影免费观看在| 日韩伦理在线一区| 亚洲国产欧美一区二区三区同亚洲 | 欧美精品www| 99久久精品国产色欲| 中文字幕一区二区在线播放| 国产视频一区二区三区在线播放| 林ゆな中文字幕一区二区| 欧美寡妇偷汉性猛交| 国产特级黄色片| 国产精品久久久久一区二区三区共| 国模吧无码一区二区三区| 久本草在线中文字幕亚洲| 欧美日韩国产成人高清视频| 国产精品一品二区三区的使用体验| 日本一区二区视频在线| 免费在线观看的毛片| 伊人久久大香线蕉| 国产成人综合久久| 国产日本在线| 欧美日韩精品免费观看视频 | 欧美 日韩 国产 成人 在线 91| 一级中文字幕一区二区| 美国黄色一级视频| 亚洲成人资源| 精品在线一区| 成人免费看黄| 伊人男人综合视频网| 国产成人麻豆免费观看| 国产免费观看久久| 中文字幕1234区| 午夜久久黄色| 国产伦视频一区二区三区| 国产777精品精品热热热一区二区| 亚洲国产精品久久久| 久久久久99精品成人片我成大片| 久久久夜色精品亚洲| 五月天亚洲视频| 91超碰国产精品| 国产精品久久九九| 悠悠资源网亚洲青| 中文字幕一精品亚洲无线一区| 中文字幕在线日亚洲9| 亚洲日本在线a| 国产白袜脚足j棉袜在线观看| 一区二区三区高清视频在线观看| 欧美精品成人一区二区在线观看| 日韩中文在线播放| 欧美成人在线网站| 四虎影院在线播放| 国产亚洲短视频| 香港日本韩国三级网站| 中文字幕亚洲综合久久五月天色无吗''| 99re6热在线精品视频播放速度| 丰满的护士2在线观看高清| 亚洲免费av电影| 91国内精品久久久| 午夜久久久久久电影| 九九热久久免费视频| 顶级嫩模精品视频在线看| 99久久国产宗和精品1上映| 一个色综合网| av资源站久久亚洲| 99久久伊人| 97在线视频国产| 久久综合之合合综合久久| 亚洲福利视频二区| 亚洲永久精品视频| 欧美午夜激情在线| 成人涩涩小片视频日本| 三级不卡在线观看| 国产激情在线看| 精品国产一区探花在线观看 | 综合久久综合久久| av无码av天天av天天爽| 黑人巨大精品欧美黑白配亚洲| 亚洲熟妇国产熟妇肥婆| 亚洲v在线看| 日韩精品欧美一区二区三区| 国产精品x8x8一区二区| 成人精品在线视频| 亚洲深夜视频| 欧美激情精品在线| 调教视频免费在线观看| 亚洲另类激情图| 欧美熟妇另类久久久久久不卡 | 国产精品久久久久久久av福利| 亚洲一区二区三区免费在线观看| 在线观看18视频网站| 欧美系列电影免费观看| 精品一区二区三区免费毛片| 日韩精品久久久久久久软件91| 国产福利视频一区二区| 超碰高清在线| 国内精品久久久| 欧美xxx黑人xxx水蜜桃| 另类视频在线观看| 99re热久久这里只有精品34| 亚洲天堂av图片| 极品白浆推特女神在线观看| 日韩精品在线视频观看| 亚洲人妻一区二区| 欧美精品一区二区三区四区| 精品国产av一区二区| 日韩一区二区中文字幕| 国产精品-色哟哟| 欧美乱妇15p| 91精品国产乱码久久久久| 欧美性videosxxxxx| 蜜臀99久久精品久久久久小说| 欧美日韩亚洲网| 美女又爽又黄免费视频| 色综合一个色综合| 91精品国产高清一区二区三密臀| 舔着乳尖日韩一区| 天堂网一区二区三区| 精品久久久久久中文字幕大豆网| 国产精品美女毛片真酒店| 亚洲成人tv网| 91精品国产综合久久久蜜臀九色| 日韩欧美视频一区二区三区| 久久久久久少妇| 色婷婷av一区二区三区大白胸| 久草视频一区二区| 欧美午夜视频网站| 国产普通话bbwbbwbbw| 欧美一区二区三区四区高清| 99草在线视频| 精品国产a毛片| 青青草手机在线| 国产午夜精品免费一区二区三区| av国产在线观看| 久久精品久久久久| 在线观看午夜av| 欧美激情小视频| 天堂8中文在线最新版在线| 国产精品1234| 外国成人毛片| av色综合网| 中文字幕亚洲影视| 一区二区三区四区国产| 欧美网站在线| 欧美视频第三页| 久久成人免费网| 91超薄肉色丝袜交足高跟凉鞋| 久久综合九色综合欧美亚洲| 国产又粗又硬视频| 一区二区三区日韩精品| 久久久久久久久久久影院| 欧美天堂一区二区三区| а√中文在线资源库| 日韩电影大片中文字幕| 日韩av中文| 国内精品伊人久久| 日韩制服一区| 国产精品区一区二区三含羞草| 国产一区不卡| 成人手机在线播放| 可以看av的网站久久看| 手机在线国产视频| 2023国产精品视频| 成人在线观看小视频| 欧美日韩国产页| 国产精品自产拍| 日韩精品视频中文在线观看| 老司机午夜在线| 欧美一级淫片播放口| 国产一区2区在线观看| 欧美久久综合性欧美| 欧美日韩福利| jizz欧美性11| 91视频你懂的| 99热精品免费| 欧美性色综合网| 色综合视频在线| 久久久av电影| 黑人一区二区三区| 欧美精品与人动性物交免费看| 亚洲高清二区| 国产大片一区二区三区| 欧美国产日韩亚洲一区| 日韩成人免费观看| 欧美tickling挠脚心丨vk| 69视频在线观看| 日本中文字幕久久看| 97视频一区| 青青视频免费在线观看| 狠狠色狠狠色综合系列| 九九热免费在线| 一本到高清视频免费精品| 日本激情一区二区| 美女av一区二区| 亚洲免费资源| 杨幂一区欧美专区| 日本中文字幕一区二区有限公司| 鲁大师私人影院在线观看| 亚洲永久免费视频| 精品毛片在线观看| 久久久精品影院| 欧美aaa级| 亚洲精品欧美精品| 日韩综合在线视频| 中文字幕 自拍| 色综合一区二区| 撸视在线观看免费视频| 国产69久久精品成人| 先锋影音国产精品| 日日橹狠狠爱欧美超碰| 97成人超碰视| 日本少妇xxxx动漫| 日韩成人av网址| 日本在线影院| 欧美日韩三区四区| 丝瓜av网站精品一区二区 | 精品久久国产| 波多野结衣天堂| 亚洲国产精品国自产拍av| 成人黄色免费网| 中文字幕亚洲天堂| 成人动漫视频在线观看| 成人在线观看毛片| 国产99久久久久久免费看农村| 久操免费在线视频| 亚洲成人av片在线观看| a天堂资源在线| 欧美18视频| 青椒成人免费视频| 婷婷激情四射网| 欧美va天堂va视频va在线| 超碰资源在线| 热舞福利精品大尺度视频| 日韩av网站在线观看| 又嫩又硬又黄又爽的视频| 91精品国产综合久久香蕉麻豆| h片在线播放| 韩国成人一区| 久久一区亚洲| 亚洲v国产v欧美v久久久久久| 欧美日韩一区二区在线观看| 久久99精品久久| 国产精品一 二 三| 久久av一区二区三区| 国产视频精品免费| 精品国产欧美一区二区| gay欧美网站| 国产精品波多野结衣| 不卡电影免费在线播放一区| 尤物视频免费观看| 久99久在线视频| 自拍亚洲一区| 男女污污视频网站| 精品久久久久久久大神国产| 国产对白叫床清晰在线播放| 91久久国产精品| 亚洲一区一卡| 亚洲免费999| 亚洲国产精品自拍| 成人在线视频成人| 国产欧美日韩综合一区在线观看| 丝袜国产日韩另类美女| 久久精品一级片| 亚洲最新中文字幕| 国内精品免费| 天堂在线一区二区三区| 一本大道久久a久久综合| caoporn免费在线视频| 秋霞在线观看一区二区三区|