成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Android進階之Dialog對應的Context必須是Activity嗎?從源碼詳細分析

移動開發 Android
創建Dialog的時候知道在Dialog的構造方法中需要一個上下文環境,而對這個“上下文”沒有具體的概念結果導致程序報錯,于是發現Dialog需要的上下文環境只能是activity。

[[419839]]

前言

創建Dialog的時候知道在Dialog的構造方法中需要一個上下文環境,而對這個“上下文”沒有具體的概念結果導致程序報錯,

于是發現Dialog需要的上下文環境只能是activity。

所以接下來這篇文章將會從源碼的角度來徹底的理順這個問題;

一、Dialog創建失敗

在Dialog的構造方法中傳入一個Application的上下文環境。看看程序是否報錯:

  1. Dialog dialog = new Dialog(getApplication()); 
  2.      TextView textView = new TextView(this); 
  3.      textView.setText("使用Application創建Dialog"); 
  4.      dialog.setContentView(textView); 
  5.      dialog.show(); 

運行程序,程序不出意外的崩潰了,我們來看下報錯信息:

  1. Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application 
  2.     at android.view.ViewRootImpl.setView(ViewRootImpl.java:517) 
  3.     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:301) 
  4.     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:215) 
  5.     at android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:140) 

這段錯誤日志,有兩點我們需要注意一下

  • 程序報了一個BadTokenException異常;
  • 程序報錯是在ViewRootImpl的setView方法中;
  • 我們一定很疑惑BadTokenException到底是個啥,在說明這個之前我們首先需要了解Token,在了解了Token的概念之后,再結合ViewRootImpl的setView方法,就能理解BadTokenException這個到底是什么,怎么產生的;

二、Token分析

1、token詳解

Token直譯成中文是令牌的意思,android系統中將其作為一種安全機制,其本質是一個Binder對象,在跨進程的通行中充當驗證碼的作用。比如:在activity的啟動過程及界面繪制的過程中會涉及到ActivityManagerService,應用程序,WindowManagerService三個進程間的通信,此時Token在這3個進程中充當一個身份驗證的功能,ActivityManagerService與WindowManagerService通過應用程序的activity傳過來的Token來分辨到底是控制應用程序的哪個activity。具體來說就是:

  • 在啟動Activity的流程當中,首先,ActivityManagerService會創建ActivityRecord由其本身來管理,同時會為這個ActivityRecord創建一個IApplication(本質上就是一個Binder)。
  • ActivityManagerService將這個binder對象傳遞給WindowManagerService,讓WindowManagerService記錄下這個Binder。
  • 當ActivityManagerService這邊完成數據結構的添加之后,會返回給ActivityThread一個ActivityClientRecord數據結構,中間就包含了Token這個Binder對象。
  • ActivityThread這邊拿到這個Token的Binder對象之后,就需要讓WindowManagerService去在界面上添加一個對應窗口,在添加窗口傳給WindowManagerService的數據中WindowManager.LayoutParams這里面就包含了Token。
  • 最終WindowManagerService在添加窗口的時候,就需要將這個Token的Binder和之前ActivityManagerService保存在里面的Binder做比較,驗證通過說明是合法的,否則,就會拋出BadTokenException這個異常。
  • 到這里,我們就知道BadTokenException是怎么回事了,然后接下來分析為什么使用Application上下文會報BadTokenException異常,而Activity上下文則不會
圖片

2、為什么非要一個Token

因為在WMS那邊需要根據這個Token來確定Window的位置(不是說坐標),如果沒有Token的話,就不知道這個窗口應該放到哪個容器上了;

因為非Activity的Context它的WindowManger沒有ParentWindow,導致在WMS那邊找不到對應的容器,也就是不知道要把Dialog的Window放置在何處。

還有一個原因是沒有SYSTEM_ALERT_WINDOW權限(當然要加權限啦,DisplayArea.Tokens的子容器,級別比普通應用的Window高,也就是會顯示在普通應用Window的前面,如果不加權限控制的話,被濫用還得了)。

在獲得SYSTEM_ALERT_WINDOW權限并將Dialog的Window.type指定為SYSTEM_WINDOW之后能正常顯示,是因為WMS會為SYSTEM_WINDOW類型的窗口專門創建一個WindowToken(這下就有容器了),并放置在DisplayArea.Tokens里面(這下知道放在哪里了);

三、創建dialog流程分析

1、activity的界面最后是通過ViewRootImpl的setView方法連接WindowManagerService,從而讓WindowManagerService將界面繪制到手機屏幕上。而從上面的異常日志中其實也可以看出,Dialog的界面也是通過ViewRootImpl的setView連接WindowManagerService,從而完成界面的繪制的。

我們首先來看Dialog的構造方法。不管一個參數的構造方法。兩個參數的構造方法,最終都會調用到3個參數的構造方法:

  1. Dialog(@NonNull Context context, @StyleRes int themeResId, boolean  
  2. createContextThemeWrapper) { 
  3.         ...... 
  4.         //1.創建一個WindowManagerImpl對象 
  5.         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 
  6.         //2.創建一個PhoneWindow對象 
  7.         final Window w = new PhoneWindow(mContext); 
  8.         mWindow = w; 
  9.         //3.使dialog能夠響應用戶的事件 
  10.         w.setCallback(this); 
  11.         w.setOnWindowDismissedCallback(this); 
  12.         //4.為window對象設置WindowManager 
  13.         w.setWindowManager(mWindowManager, nullnull); 
  14.         w.setGravity(Gravity.CENTER); 
  15.         mListenersHandler = new ListenersHandler(this); 
  16.     } 

這段代碼可以看出dialog的創建實質上和activity界面的創建沒什么兩樣,都需要完成一個應用窗口Window的創建,和一個應用窗口視圖對象管理者WindowManagerImpl的創建。

然后Dialog同樣有一個setContentView方法:

  1. public void setContentView(@LayoutRes int layoutResID) { 
  2.         mWindow.setContentView(layoutResID); 
  3.     } 
  4. 依然是調用PhoneWindow的setContentView方法。再接著我們來看下dialog的show方法: 
  5. public void show() { 
  6.         ...... 
  7.         //1.得到通過setView方法封裝好的DecorView  
  8.         mDecor = mWindow.getDecorView(); 
  9.         ...... 
  10.        //2.得到創建PhoneWindow時已經初始化的成員變量WindowManager.LayoutParams 
  11.         WindowManager.LayoutParams l = mWindow.getAttributes(); 
  12.         if ((l.softInputMode 
  13.                 & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) { 
  14.             WindowManager.LayoutParams nl = new WindowManager.LayoutParams(); 
  15.             nl.copyFrom(l); 
  16.             nl.softInputMode |= 
  17.                     WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; 
  18.             l = nl; 
  19.         } 
  20.         try { 
  21.             //3.通過WindowManagerImpl添加DecorView到屏幕 
  22.             mWindowManager.addView(mDecor, l); 
  23.             mShowing = true
  24.             sendShowMessage(); 
  25.         } finally { 
  26.         } 
  27.     } 

這段代碼和activity的makeVisable方法類似,這里也不多說了,注釋已經大概的寫清楚了。然后調用WindowManagerImpl的addView方法:

  1. @Override 
  2.     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { 
  3.         applyDefaultToken(params); 
  4.         mGlobal.addView(view, params, mDisplay, mParentWindow); 
  5.     } 
  6. 接著調用了WindowManagerGlobal的addView方法: 
  7. public void addView(View view, ViewGroup.LayoutParams params, 
  8.             Display display, Window parentWindow) { 
  9.         ...... 
  10.         //1.將傳進來的ViewGroup.LayoutParams類型的params轉成  
  11. WindowManager.LayoutParams類型的wparams  
  12.         final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)  
  13. params; 
  14.        //2.如果WindowManagerImpl是在activity的方法中被創建則不為空 
  15.         if (parentWindow != null) { 
  16.             parentWindow.adjustLayoutParamsForSubWindow(wparams); 
  17.         } else { 
  18.         ...... 
  19.         } 
  20.         ViewRootImpl root; 
  21.         View panelParentView = null
  22.         synchronized (mLock) { 
  23.         ...... 
  24.             root = new ViewRootImpl(view.getContext(), display); 
  25.             view.setLayoutParams(wparams); 
  26.             //3.將視圖對象view,ViewRootImpl以及wparams分別存入相應集合的對應位置 
  27.             mViews.add(view); 
  28.             mRoots.add(root); 
  29.             mParams.add(wparams); 
  30.         } 
  31.         // do this last because it fires off messages to start doing things 
  32.         try { 
  33.             //4.通過ViewRootImpl聯系WindowManagerService將view繪制到屏幕上 
  34.             root.setView(view, wparams, panelParentView); 
  35.         } catch (RuntimeException e) { 
  36.             // BadTokenException or InvalidDisplayException, clean up. 
  37.             synchronized (mLock) { 
  38.                 final int index = findViewLocked(viewfalse); 
  39.                 if (index >= 0) { 
  40.                     removeViewLocked(indextrue); 
  41.                 } 
  42.             } 
  43.             throw e; 
  44.         } 
  45.     } 
  1. //2.如果WindowManagerImpl是在activity的方法中被創建則不為空    
  2.   if (parentWindow != null) { 
  3.            parentWindow.adjustLayoutParamsForSubWindow(wparams); 
  4.        } else { 
  5.        ...... 
  6.        } 

2、這里會首先判斷一個類型為Window的parentWindow 是否為空,如果不為空會通過Window的adjustLayoutParamsForSubWindow方法調整一個類型為WindowManager.LayoutParams的變量wparams的一些屬性值。應用程序請求WindowManagerService服務時會傳入一個Token,其實那個Token就會通過Window的adjustLayoutParamsForSubWindow方法存放在wparams的token變量中,也就是說如果沒有調用Window的adjustLayoutParamsForSubWindow方法就會導致wparams的token變量為空。然后我們接下來看一下wparams的token變量是如何賦值的:

  1. void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) { 
  2.         CharSequence curTitle = wp.getTitle(); 
  3.         if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && 
  4.             wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { 
  5.         ...... 
  6.         } else { 
  7.             if (wp.token == null) { 
  8.                 wp.token = mContainer == null ? mAppToken : mContainer.mAppToken; 
  9.             } 
  10.         ...... 
  11.         } 
  12.         if (wp.packageName == null) { 
  13.             wp.packageName = mContext.getPackageName(); 
  14.         } 
  15.         if (mHardwareAccelerated) { 
  16.             wp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 
  17.         } 

這里我們可以看到這段代碼首先會做一個判斷如果wp.type的值有沒有位于WindowManager.LayoutParams.FIRST_SUB_WINDOW與WindowManager.LayoutParams.LAST_SUB_WINDOW之間,如果沒有則會給wp.token賦值。wp.type代表窗口類型,有3種級別,分別為系統級,應用級以及子窗口級。而這里是判斷是否為子窗口級級別。而Dialog的WindowManager.LayoutParams.type默認是應用級的,因此會走else分支,給wp.token賦值mAppToken。至于mAppToken是什么,我們待會再來分析。

3、看WindowManagerGlobal的addView方法的,會調用ViewRootImpl的setView方法,我們來看一下,ViewRootImpl是如何連接WindowManagerService傳遞token的:

  1. public void setView(View view, WindowManager.LayoutParams attrs, View  
  2. panelParentView) { 
  3.         synchronized (this) { 
  4.             if (mView == null) { 
  5.                 mView = view
  6.                 try { 
  7.                     ...... 
  8.                     //1.通過binder對象mWindowSession調用WindowManagerService的接口請求 
  9.                     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, 
  10.                             getHostVisibility(), mDisplay.getDisplayId(), 
  11.                             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, 
  12.                             mAttachInfo.mOutsets, mInputChannel); 
  13.                 } catch (RemoteException e) { 
  14.                     ...... 
  15.                     throw new RuntimeException("Adding window failed", e); 
  16.                 } finally { 
  17.                     if (restore) { 
  18.                         attrs.restore(); 
  19.                     } 
  20.                 } 
  21.                     ...... 
  22.                 if (res < WindowManagerGlobal.ADD_OKAY) { 
  23.                     ...... 
  24.                     switch (res) { 
  25.                         case WindowManagerGlobal.ADD_BAD_APP_TOKEN: 
  26.                         case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN: 
  27.                             throw new WindowManager.BadTokenException( 
  28.                                     "Unable to add window -- token " + attrs.token 
  29.                                     + " is not valid; is your activity running?"); 
  30.                         //2.如果請求失?。╰oken驗證失?。﹦t拋出BadTokenException異常 
  31.                         case WindowManagerGlobal.ADD_NOT_APP_TOKEN: 
  32.                             throw new WindowManager.BadTokenException( 
  33.                                     "Unable to add window -- token " + attrs.token 
  34.                                     + " is not for an application"); 
  35.                         case WindowManagerGlobal.ADD_APP_EXITING: 
  36.                             throw new WindowManager.BadTokenException( 
  37.                                     "Unable to add window -- app for token " +  
  38. attrs.token 
  39.                                     + " is exiting"); 
  40.                         case WindowManagerGlobal.ADD_DUPLICATE_ADD: 
  41.                             throw new WindowManager.BadTokenException( 
  42.                                     "Unable to add window -- window " + mWindow 
  43.                                     + " has already been added"); 
  44.                         case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED: 
  45.                             // Silently ignore -- we would have just removed it 
  46.                             // right away, anyway. 
  47.                             return
  48.                         case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON: 
  49.                             throw new WindowManager.BadTokenException( 
  50.                                     "Unable to add window " + mWindow + 
  51.                                     " -- another window of this type already  
  52. exists"); 
  53.                         case WindowManagerGlobal.ADD_PERMISSION_DENIED: 
  54.                             throw new WindowManager.BadTokenException( 
  55.                                     "Unable to add window " + mWindow + 
  56.                                     " -- permission denied for this window type"); 
  57.                         case WindowManagerGlobal.ADD_INVALID_DISPLAY: 
  58.                             throw new WindowManager.InvalidDisplayException( 
  59.                                     "Unable to add window " + mWindow + 
  60.                                     " -- the specified display can not be found"); 
  61.                         case WindowManagerGlobal.ADD_INVALID_TYPE: 
  62.                             throw new WindowManager.InvalidDisplayException( 
  63.                                     "Unable to add window " + mWindow 
  64.                                     + " -- the specified window type is not valid"); 
  65.                     } 
  66.                     throw new RuntimeException( 
  67.                             "Unable to add window -- unknown error code " + res); 
  68.                 } 
  69.         ...... 
  70.             } 
  71.         } 
  72.     } 

這段代碼有兩處需要注意:

  • 會通過一個mWindowSession的binder對象請求WindowManagerService服務,傳遞一個類型為WindowManager.LayoutParams的變量mWindowAttributes到WindowManagerService,mWindowAttributes里面裝有代表當前activity的token對象。然后通過WindowManagerService服務創建屏幕視圖。
  • 會根據請求WindowManagerService服務的返回結果判斷是否請求成功,如果請求失敗會拋出異常,注釋的地方就是在文章開頭示例拋出的異常。此時attrs.token為空。如果創建dialog的上下文環境改為activity的為什么就不為空呢?

四、分析創建Dialog的上下文Activity為何與眾不同

1、上文的分析中可以看出attrs.token的賦值在Window的adjustLayoutParamsForSubWindow方法中。而Dialog默認的WindowManager.LayoutParams.type是應用級別的,因此,如果能進入這個方法內,attrs.token肯定能被賦值?,F在只有一種情況,如果不是activity的上下文環境就沒有進入到這個方法內。這時我們再看WindowManagerGlobal的addView方法的:

  1. public void addView(View view, ViewGroup.LayoutParams params, 
  2.             Display display, Window parentWindow) { 
  3.         ...... 
  4.        //2.如果WindowManagerImpl是在activity的方法中被創建則不為空 
  5.         if (parentWindow != null) { 
  6.             parentWindow.adjustLayoutParamsForSubWindow(wparams); 
  7.         } else { 
  8.         ...... 
  9.         } 
  10.         ...... 
  11.     } 

從這里看出如果Window類型的parentWindow為空,就不會進入adjustLayoutParamsForSubWindow方法。從而可以得出結論如果不是activity的上下文環境WindowManagerGlobal的第四個參數parentWindow為空。緊接著我們再來分析為什么其他的上下文會導致parentWindow為空。

WindowManagerGlobal調用addView方法在WindowManagerImpl的addView方法中:

  1.  @Override 
  2.     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { 
  3.         applyDefaultToken(params); 
  4.         mGlobal.addView(view, params, mDisplay, mParentWindow); 
  5.     } 
  6. WindowManagerImpl的addView方法在Dialog的首位方法中調用: 
  7. public void show() { 
  8.         ...... 
  9.         try { 
  10.             mWindowManager.addView(mDecor, l); 
  11.             mShowing = true
  12.             sendShowMessage(); 
  13.         } finally { 
  14.         } 
  15.     } 

對比這兩個方法??梢钥闯鯳indowManagerImpl的addView方法調用WindowManagerGlobal的addView方法是多出來了兩個參數mDisplay, mParentWindow,我們只看后一個,多了一個Window類型的mParentWindow,可以一mParentWindow并不是在Dialog的show方法中賦值的。那么它在哪賦值呢?在WindowManagerImpl類中搜索mParentWindow發現它在WindowManagerImpl的兩個參數的構造方法中被賦值。從這里我們可以猜測,如果是使用的activity上下文,那么在創建WindowManagerImpl實例的時候用的是兩個參數的構造方法,而其他的上下文是用的一個參數的構造方法?,F在問題就集中到了WindowManagerImpl是如何被創建的了。

我們再回過頭來看Dialog的構造方法中WindowManagerImpl是如何創建的:

  1. Dialog(@NonNull Context context, @StyleRes int themeResId, boolean  
  2. createContextThemeWrapper) { 
  3.         ...... 
  4.         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 
  5.         ...... 
  6.     } 
  7. 然后分別查看activity的getSystemService方法,和Application的getSystemService方法: 
  8. activity的getSystemService方法 
  9. @Override 
  10.    public Object getSystemService(@ServiceName @NonNull String name) { 
  11.        ...... 
  12.        if (WINDOW_SERVICE.equals(name)) { 
  13.            return mWindowManager; 
  14.        } else if (SEARCH_SERVICE.equals(name)) { 
  15.            ensureSearchManager(); 
  16.            return mSearchManager; 
  17.        } 
  18.        return super.getSystemService(name); 
  19.    } 

在這個方法中直接返回了activity的mWindowManager對象,activity的mWindowManager對象在activity的attach方法中:

  1. final void attach(Context context, ActivityThread aThread, 
  2.             Instrumentation instr, IBinder token, int ident, 
  3.             Application application, Intent intent, ActivityInfo info, 
  4.             CharSequence title, Activity parent, String id, 
  5.             NonConfigurationInstances lastNonConfigurationInstances, 
  6.             Configuration config, String referrer, IVoiceInteractor voiceInteractor) { 
  7.       ...... 
  8.       mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(), 
  9.                 (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); 
  10.        ......   
  11.     } 

2、我們再看Window的setWindowManager方法:

  1. public void setWindowManager(WindowManager wm, IBinder appToken, String appName, 
  2.             boolean hardwareAccelerated) { 
  3.         //1.將ActivityManagerService傳過來的Token保存到mAppToken中 
  4.         mAppToken = appToken; 
  5.         //2.創建WindowManagerImpl 
  6.         mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); 
  7.     } 

這段代碼兩個地方需要注意,一是前ActivityManagerService傳過來的Token賦值給Winow的mAppToken,這個token最后會保存到attr.token,具體操作在Window的adjustLayoutParamsForSubWindow方法中。二是調用WindowManagerImpl的createLocalWindowManager方法創建WindowManagerImpl:

  1. public WindowManagerImpl createLocalWindowManager(Window parentWindow) { 
  2.         return new WindowManagerImpl(mDisplay, parentWindow); 
  3.     } 

到這里就可以看出如果創建Dialog的上下文是activity,則會調用WindowManagerImpl兩個參數的構造方法,從而導致parentWindow不為空。

3、Application的getSystemService方法:

由于Application是Context的子類,所以Application的getSystemService最終會調到ContextImpl的getSystemService方法

  1. @Override 
  2.     public Object getSystemService(String name) { 
  3.         return SystemServiceRegistry.getSystemService(this, name); 
  4.     } 
  5. 直接調用了SystemServiceRegistry的getSystemService方法,這個方法又會得到匿名內部類CachedServiceFetcher<WindowManager>的createService方法的返回值。 
  6.  @Override 
  7.             public WindowManager createService(ContextImpl ctx) { 
  8.                 return new WindowManagerImpl(ctx.getDisplay()); 
  9.             }}); 

從這個方法中可以看出上下文為Application時,調用的是WindowManagerImpl的一個參數的構造方法,從而parentWindow為空;

總結

  • 創建dialog時,如果傳入構造方法不是一個activity類型的上下文,則導致WindowManagerImpl類型為Window的變量mParentWindow,從而導致WindowManagerGlobal的addView不會調用Window的adjustLayoutParamsForSubWindow方法,從而不會給attr.token賦值,導致在WindowManagerService服務中的身份驗證失敗,拋出BadTokenException異常;
  • Show一個普通的Dialog需要的并不是Activity本身,而是一個容器的token,我們平時會傳Activity,只不過是Activity剛好對應WMS那邊的一個WindowState的容器而已;

本文轉載自微信公眾號「Android開發編程」

 

責任編輯:姜華 來源: Android開發編程
相關推薦

2021-08-28 07:48:04

AndroidActivityRecActivitySta

2021-08-19 06:58:49

Android場景FirstActivi

2009-06-18 14:00:51

2009-03-24 08:30:54

AndroidGoogle移動os

2009-09-25 14:23:39

2009-09-28 10:39:01

Hibernate基礎

2009-03-24 09:17:58

驅動GSMAndroid

2013-12-04 10:21:38

AndroidAndroidMani

2010-02-06 15:19:35

2009-12-03 17:41:40

PHP應用發展

2021-10-25 19:52:52

IntentFilte

2010-04-26 18:17:19

Oracle存儲過程

2009-09-14 13:50:35

LINQ編程模型

2009-09-08 15:56:50

Linq使用Group

2010-01-06 13:50:37

.NET Framew

2009-11-20 13:11:44

Oracle XML數

2009-09-09 09:48:43

Linq延遲加載

2009-09-14 16:21:34

LINQ To XML

2009-10-10 13:52:57

VB Update方法

2010-03-08 17:18:46

Linux du命令
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一级看片免费视频囗交动图 | 日韩电影在线 | 欧洲亚洲一区二区三区 | 国产九九av| 99久久免费精品国产免费高清 | 日韩欧美国产不卡 | 午夜免费视频 | 午夜男人视频 | 日韩免费av | 亚洲狠狠爱一区二区三区 | 91成人在线 | 狠狠视频 | 欧美精品一区三区 | 日韩欧美高清 | 激情五月婷婷综合 | 综合久久久 | 欧美日韩亚洲国产 | 999热精品 | 亚洲色欲色欲www | 亚洲av毛片 | 亚洲精品一区在线 | 久久久久精 | 日韩黄a | 国产色婷婷精品综合在线播放 | 久久综合一区 | 色婷婷综合网站 | 噜久寡妇噜噜久久寡妇 | 99伊人| 色综久久 | 成年人免费看 | 国产精品久久av | 91精品国产乱码久久久久久 | 最新日韩在线视频 | 久久国产福利 | 欧美亚洲国产精品 | 欧美激情视频一区二区三区在线播放 | 99久久99 | 成人精品国产免费网站 | 欧美日韩亚洲二区 | 亚洲精品一二三区 | 亚洲福利网 |