亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

AsyncTask陷阱之:Handler,Looper與MessageQueue的詳解

2020-04-11 12:23:27
字體:
來源:轉載
供稿:網友
AsyncTask的隱蔽陷阱
先來看一個實例
這個例子很簡單,展示了AsyncTask的一種極端用法,挺怪的。
復制代碼 代碼如下:

public class AsyncTaskTrapActivity extends Activity {
    private SimpleAsyncTask asynctask;
    private Looper myLooper;
    private TextView status;

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        asynctask = null;
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                myLooper = Looper.myLooper();
                status = new TextView(getApplication());
                asynctask = new SimpleAsyncTask(status);
                Looper.loop();
            }
        }).start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
        setContentView((TextView) status, params);
        asynctask.execute();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        myLooper.quit();
    }

    private class SimpleAsyncTask extends AsyncTask<Void, Integer, Void> {
        private TextView mStatusPanel;

        public SimpleAsyncTask(TextView text) {
            mStatusPanel = text;
        }

        @Override
        protected Void doInBackground(Void... params) {
            int prog = 1;
            while (prog < 101) {
                SystemClock.sleep(1000);
                publishProgress(prog);
                prog++;
            }
            return null;
        }

        // Not Okay, will crash, said it cannot touch TextView
        @Override
        protected void onPostExecute(Void result) {
            mStatusPanel.setText("Welcome back.");
        }

        // Okay, because it is called in #execute() which is called in Main thread, so it runs in Main Thread.
        @Override
        protected void onPreExecute() {
            mStatusPanel.setText("Before we go, let me tell you something buried in my heart for years...");
        }

        // Not okay, will crash, said it cannot touch TextView
        @Override
        protected void onProgressUpdate(Integer... values) {
            mStatusPanel.setText("On our way..." + values[0].toString());
        }
    }
}

這個例子在Android2.3中無法正常運行,在執行onProgressUpdate()和onPostExecute()時會報出異常



復制代碼 代碼如下:

11-03 09:13:10.501: E/AndroidRuntime(762): FATAL EXCEPTION: Thread-10
11-03 09:13:10.501: E/AndroidRuntime(762): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
11-03 09:13:10.501: E/AndroidRuntime(762):  at android.view.ViewRoot.checkThread(ViewRoot.java:2990)
11-03 09:13:10.501: E/AndroidRuntime(762):  at android.view.ViewRoot.requestLayout(ViewRoot.java:670)
11-03 09:13:10.501: E/AndroidRuntime(762):  at android.view.View.requestLayout(View.java:8316)
11-03 09:13:10.501: E/AndroidRuntime(762):  at android.view.View.requestLayout(View.java:8316)
11-03 09:13:10.501: E/AndroidRuntime(762):  at android.view.View.requestLayout(View.java:8316)
11-03 09:13:10.501: E/AndroidRuntime(762):  at android.view.View.requestLayout(View.java:8316)
11-03 09:13:10.501: E/AndroidRuntime(762):  at android.widget.TextView.checkForRelayout(TextView.java:6477)
11-03 09:13:10.501: E/AndroidRuntime(762):  at android.widget.TextView.setText(TextView.java:3220)
11-03 09:13:10.501: E/AndroidRuntime(762):  at android.widget.TextView.setText(TextView.java:3085)
11-03 09:13:10.501: E/AndroidRuntime(762):  at android.widget.TextView.setText(TextView.java:3060)
11-03 09:13:10.501: E/AndroidRuntime(762):  at com.hilton.effectiveandroid.os.AsyncTaskTrapActivity$SimpleAsyncTask.onProgressUpdate(AsyncTaskTrapActivity.java:110)
11-03 09:13:10.501: E/AndroidRuntime(762):  at com.hilton.effectiveandroid.os.AsyncTaskTrapActivity$SimpleAsyncTask.onProgressUpdate(AsyncTaskTrapActivity.java:1)
11-03 09:13:10.501: E/AndroidRuntime(762):  at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:466)
11-03 09:13:10.501: E/AndroidRuntime(762):  at android.os.Handler.dispatchMessage(Handler.java:130)
11-03 09:13:10.501: E/AndroidRuntime(762):  at android.os.Looper.loop(Looper.java:351)
11-03 09:13:10.501: E/AndroidRuntime(762):  at com.hilton.effectiveandroid.os.AsyncTaskTrapActivity$1.run(AsyncTaskTrapActivity.java:56)
11-03 09:13:10.501: E/AndroidRuntime(762):  at java.lang.Thread.run(Thread.java:1050)
11-03 09:13:32.823: E/dalvikvm(762): [DVM] mmap return base = 4585e000

但在Android4.0及以上的版本中運行就正常(3.0版本未測試)。



從2.3運行時的Stacktrace來看原因是在非UI線程中操作了UI組件。不對呀,神奇啊,AsyncTask#onProgressUpdate()和AsyncTask#onPostExecute()的文檔明明寫著這二個回調是在UI線程里面的嘛,怎么還會報出這樣的異常呢!
原因分析
AsyncTask設計出來執行異步任務卻又能與主線程通訊,它的內部有一個InternalHandler是繼承自Handler的靜態成員sHandler,這個sHandler就是用來與主線程通訊的。看下這個對象的聲明:private static final InternalHandler sHandler = new InternalHandler();而InternalHandler又是繼承自Handler的。所以本質上講sHandler就是一個Handler對象。Handler是用來與線程通訊用的,它必須與Looper和線程綁定一起使用,創建Handler時必須指定Looper,如果不指定Looper對象則使用調用棧所在的線程,如果調用棧線程沒有Looper會報出異常??磥磉@個sHandler是與調用new InternalHandler()的線程所綁定,它又是靜態私有的,也就是與第一次創建AsyncTask對象的線程綁定。所以,如果是在主線程中創建的AsyncTask對象,那么其sHandler就與主線程綁定,這是正常的情況。在此例子中AsyncTask是在衍生線程里創建的,所以其sHandler就與衍生線程綁定,因此,它自然不能操作UI元素,會在onProgressUpdate()和onPostExecute()中拋出異常。

以上例子有異常的原因就是在衍生線程中創建了SimpleAsyncTask對象。至于為什么在4.0版本上沒有問題,是因為4.0中在ActivityThread.main()方法中,會進行BindApplication的動作,這時會用AsyncTask對象,也會創建sHandler對象,這是主線程所以sHandler是與主線程綁定的。后面再創建AsyncTask對象時,因為sHandler已經初始化完了,不會再次初始化。至于什么是BindApplication,為什么會進行BindApplication的動作不影響這個問題的討論。
AsyncTask的缺陷及修改方法
這其實是AsyncTask的隱藏的Bug,它不應該這么依賴開發者,應該強加條件限制,以保證第一次AsyncTask對象是在主線程中創建:
1. 在InternalHandler的構造中檢查當前線程是否為主線程,然后拋出異常,顯然這并不是最佳實踐。
復制代碼 代碼如下:

new InternalHandler() {

復制代碼 代碼如下:

     if (Looper.myLooper() != Looper.getMainLooper()) {
  throw new RuntimeException("AsyncTask must be initialized in main thread");
     }

復制代碼 代碼如下:

11-03 08:56:07.055: E/AndroidRuntime(890): FATAL EXCEPTION: Thread-10
11-03 08:56:07.055: E/AndroidRuntime(890): java.lang.ExceptionInInitializerError
11-03 08:56:07.055: E/AndroidRuntime(890):  at com.hilton.effectiveandroid.os.AsyncTaskTrapActivity$1.run(AsyncTaskTrapActivity.java:55)
11-03 08:56:07.055: E/AndroidRuntime(890):  at java.lang.Thread.run(Thread.java:1050)
11-03 08:56:07.055: E/AndroidRuntime(890): Caused by: java.lang.RuntimeException: AsyncTask must be initialized in main thread
11-03 08:56:07.055: E/AndroidRuntime(890):  at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:455)
11-03 08:56:07.055: E/AndroidRuntime(890):  at android.os.AsyncTask.<clinit>(AsyncTask.java:183)
11-03 08:56:07.055: E/AndroidRuntime(890):  ... 2 more

2. 更好的做法是在InternalHandler構造時把主線程的MainLooper傳給
復制代碼 代碼如下:

 new IntentHandler() {
     super(Looper.getMainLooper());
 }

會有人這樣寫嗎,你會問?通常情況是不會的,沒有人會故意在衍生線程中創建AsyncTask。但是假如有一個叫Worker的類,用來完成異步任務從網絡上下載圖片,然后顯示,還有一個WorkerScheduler來分配任務,WorkerScheduler也是運行在單獨線程中,Worker用AsyncTask來實現,WorkScheduler會在接收到請求時創建Worker去完成請求,這時就會出現在WorkerScheduler線程中---衍生線程---創建AsyncTask對象。這種Bug極其隱蔽,很難發現。
如何限制調用者的線程
正常情況下一個Java應用一個進程,且有一個線程,入口即是main方法。安卓應用程序本質上也是Java應用程序,它的主入口在ActivityThread.main(),在main()方法中會調用Looper.prepareMainLooper(),這就初始化了主線程的Looper,且Looper中保存有主線程的Looper對象mMainLooper,它也提供了方法來獲取主線程的Looper,getMainLooper()。所以如果需要創建一個與主線程綁定的Handler,就可以用new Handler(Looper.getMainLooper())來保證它確實與主線程綁定。
如果想要保證某些方法僅能在主線程中調用就可以檢查調用者的Looper對象:
復制代碼 代碼如下:

 if (Looper.myLooper() != Looper.getMainLooper()) {
    throw new RuntimeException("This method can only be called in main thread");
 }

Handler,Looper,MessageQueue機制
線程與線程間的交互協作
線程與線程之間雖然共享內存空間,也即可以訪問進程的堆空間,但是線程有自己的棧,運行在一個線程中的方法調用全部都是在線程自己的調用棧中。通俗來講西線程就是一個run()方法及其內部所調用的方法。這里面的所有方法調用都是獨立于其他線程的,由于方法調用的關系,一個方法調用另外的方法,那么另外的方法也發生在調用者的線程里。所以,線程是時序上的概念,本質上是一列方法調用。
那么線程之間要想協作,或者想改變某個方法所在的線程(為了不阻塞自己線程),就只能是向另外一個線程發送一個消息,然后return;另外線程收到消息后就去執行某些操作。如果是簡單的操作可以用一個變量來標識,比如A線程主需要B線程做某些事時,可以把某個對象obj設置值,B則當看到obj != null時就去做事,這種線程交互協作在《Java編程思想》中有大量示例。
Android中的ITC-Inter Thread Communication
注意:當然Handler也可以用做一個線程內部的消息循環,不必非與另外的線程通信,但這里重點討論的是線程與線程之間的事情。

Android當中做了一個特別的限制就是非主線程不能操作UI元素,而一個應用程序是不可能不創衍生線程的,這樣一來主線程與衍生線程之間就必須進行通信。由于這種通信很頻繁,所以不可能全用變量來標識,程序將變得十分混亂。這個時候消息隊列就變得有十分有必要,也就是在每個線程中建立一個消息隊列。當A需要B時,A向B發一個消息,此過程實質為把消息加入到B的消息隊列中,A就此return,B并不專門等待某個消息,而是循環的查看其消息隊列,看到有消息后就去執行。

整套ITC的基本思想是:定義一個消息對象,把需要的數據放入其中,把消息的處理的方法也定義好作為回調放到消息中,然后把這個消息發送另一個線程上;另外的線程在循環處理其隊列里的消息,看到消息時就對消息調用附在其上的回調來處理消息。這樣一來可以看出,這僅僅是改變了處理消息的執行時序:正常是當場處理,這種則是封裝成一個消息丟給另外的線程,在某個不確定的時間被執行;另外的線程也僅提供CPU時序,對于消息是什么和消息如何處理它完全不干預。簡言之就是把一個方法放到另外一個線程里去調用,進而這個方法的調用者的調用棧(call stack)結束,這個方法的調用棧轉移到了另外的線程中。
那么這個機制改變的到底是什么呢?從上面看它僅是讓一個方法(消息的處理)安排到了另外一個線程里去做(異步處理),不是立刻馬上同步的做,它改變的是CPU的執行時序(execution sequence)。
那么消息隊列存放在哪里呢?不能放在堆空間里(直接new MessageQueue()),這樣的話對象的引用容易丟失,針對線程來講也不易維護。Java支持線程的本地存儲ThreadLocal,通過ThreadLocal對象可以把對象放到線程的空間上,每個線程都有了屬于自己的對象。因此,可以為每個需要通信的線程創建一個消息隊列并放到其本地存儲中。
基于這個模型還可以擴展,比如給消息定義優先級等。



MessageQueue
以隊列的方式來存儲消息,主要是二個操作一個是入列enqueueMessage,一個是出列next(),需要保證的是線程安全,因為入列通常是另外的線程在調用。
MessageQueue是一個十分接近底層的機制,所以不方便開發者直接使用,要想使用此MessageQueue必須做二個方面工作,一個是目標線程端:創建,與線程關聯,運轉起來;另一個就是隊列線程的客戶端:創建消息,定義回調處理,發送消息到隊列。Looper和Handler就是對MessageQueue的封裝:Looper是給目標線程用的:用途是創建MessageQueue,將MessageQueue與線程關聯起來,并讓MessageQueue運轉起來,且Looper有保護機制,讓一個線程僅能創建一個MessageQueue對象;而Handler則是給隊列客戶端用的:用來創建消息,定義回調和發送消息。
因為Looper對象封裝了目標隊列線程及其隊列,所以對隊列線程的客戶端來講,Looper對象就代表著一個擁有MessageQueue的線程,和這個線程的MessageQueue。也即當你構建Handler對象時用的是Looper對象,而當你檢驗某個線程是否是預期線程時也用Looper對象。
Looper內幕
Looper的任務是創建消息隊列MessageQueue,放到線程的ThreadLocal中(與線程關聯),并且讓MessageQueue運轉起來,處于Ready的狀態,并要提供供接口以停止消息循環。它主要有四個接口:
public static void Looper.prepare()
這個方法是為線程創建一個Looper對象和MessageQueue對象,并把Looper對象通過ThreadLocal放到線程空間里去。需要注意的是這個方法每個線程只能調用一次,通常的做法是在線程run()方法的第一句,但只要保證在loop()前面即可。
•public static void Looper.loop()
這個方法要在prepare()這后調用,是讓線程的MessageQueue運轉起來,一旦調用此方法,線程便會無限循環下去(while (true){...}),無Message時休眠,有Message入隊時喚醒處理,直到quit()調用為止。它的簡化實現就是:
復制代碼 代碼如下:

loop() {
   while (true) {
      Message msg = mQueue.next();
      if msg is a quit message, then
         return;
      msg.processMessage(msg)
   }
}

public void Looper.quit()
讓線程結束MessageQueue的循環,終止循環,run()方法會結束,線程也會停止,因此它是對象的方法,意即終止某個Looper對象。一定要記得在不需要線程的時候調用此方法,否則線程是不會終止退出的,進程也就會一直運行,占用著資源。如果有大量的線程未退出,進程最終會崩掉。
public static Looper Looper.myLooper()
這個是獲得調用者所在線程所擁有的Looper對象的方法。
還有二個接口是與主線程有關的:
一個是專門為主線程準備的
public static void Looper.prepareMainLooper();
這個方法只給主線程初始化Looper用的,它僅在ActivityThread.main()方法中調用,其他地方或其他線程不可以調用,如果在主線程中調用會有異常拋出,因為一個線程只能創建一個Looper對象。但是如在其他線程中調用此方法,會改變mainLooper,接下來的getMainLooper就會返回它而非真正的主線程的Looper對象,這不會有異常拋出,也不會有明顯的錯誤,但是程序將不能正常工作,因為原本設計在主線程中運行的方法將轉到這個線程里面,會產生很詭異的Bug。這里Looper.prepareMainThread()的方法中應該加上判斷:
復制代碼 代碼如下:

public void prepareMainLooper() {
    if (getMainLooper() != null) {
         throw new RuntimeException("Looper.prepareMainthread() can ONLY be called by Frameworks");
     }
     //...
}

以防止其他線程非法調用,光靠文檔約束力遠不夠。
•另外一個就是獲取主線程Looper的接口:
public static Looper Looper.getMainLooper()
這個主要用在檢查線程合法性,也即保證某些方法只能在主線程里面調用。但這并不保險,如上面所說,如果一個衍生線程調用了prepareMainLooper()就會把真正的mMainLooper改變,此衍生線程就可以通過上述檢測,導致getMainLooper() != myLooper()的檢測變得不靠譜了。所以ViewRoot的方法是用Thread來檢測:mThread != Thread.currentThread();其mThread是在系統創建ViewRoot時通過Thread.currentThread()獲得的,這樣的方法來檢測是否是主線程更加靠譜一些,因為它沒有依賴外部而是相信自己保存的Thread的引用。
Message對象
消息Message是僅是一個數據結構,是信息的載體,它與隊列機制是無關的,封裝著要執行的動作和執行動作的必要信息,what, arg1, arg2, obj可以用來傳送數據;而Message的回調則必須通過Handler來定義,為什么呢?因為Message僅是一個載體,它不能自己跑到目標MessageQueue上面去,它必須由Handler來操作,把Message放到目標隊列上去,既然它需要Handler來統一的放到MessageQueue上,也可以讓Handler來統一定義處理消息的回調。需要注意的是同一個Message對象只能使用一次,因為在處理完消息后會把消息回收掉,所以Message對象僅能使用一次,嘗試再次使用時MessageQueue會拋出異常。
Handler對象
它被設計出來目的就是方便隊列線程客戶端的操作,隱藏直接操作MessageQueue的復雜性。Handler最主要的作用是把消息發送到與此Handler綁定的線程的MessageQueue上,因此在構建Handler的時候必須指定一個Looper對象,如果不指定則通過Looper獲取調用者線程的Looper對象。它有很多重載的send*Message和post方法,可以以多種方式來向目標隊列發送消息,廷時發送,或者放到隊列的頭部等等;
它還有二個作用,一個是創建Message對象通過obtain*系統方法,另一個就是定義處理Message的回調mCallback和handleMessage,由于一個Handler可能不止發送一個消息,而這些消息通常共享此Handler的回調方法,所以在handleMessage或者mCallback中就要區分這些不同的消息,通常是以Message.what來區分,當然也可以用其他字段,只要能區別出不同的Message即可。需要指明的是,消息隊列中的消息本身是獨立的,互不相干的,消息的命名空間是在Handler對象之中的,因為Message是由Handler發送和處理的,所以只有同一個Handler對象需要區別不同的Message對象。廣義上講,如果一個消息自己定義有處理方法,那么所有的消息都是互不相干的,當從隊列取出消息時就調用其上的回調方法,不會有命名上的沖突,但由Handler發出的消息的回調處理方法都是Handler.handleMessage或Handler.mCallback,所以就會有影響了,但影響的范圍也令局限在同一個Handler對象。

因為Handler的作用是向目標隊列發送消息和定義處理消息的回調(處理消息),它僅是依賴于線程的MessageQueue,所以Handler可以有任意多個,都綁定到某個MessageQueue上,它并沒有個數限制。而MessageQueue是有個數限制的,每個線程只能有一個,MessageQueue通過Looper創建,Looper存儲在線程的ThreadLocal中,Looper里作了限制,每個線程只能創建一個。但是Handler無此限制,Handler的創建通過其構造函數,只需要提供一個Looper對象即可,所以它沒有個數限制。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久热精品在线视频| 国产精品视频网站| 国产欧美一区二区三区久久人妖| 欧美性猛交xxxx乱大交| 中文字幕亚洲自拍| 国产精品久久久久久搜索| 色哟哟亚洲精品一区二区| 欧美激情视频一区| 亚洲白虎美女被爆操| 欧美综合一区第一页| 日韩国产欧美精品在线| 91国语精品自产拍在线观看性色| 日韩在线观看免费高清完整版| 久久久久久12| 欧美日韩加勒比精品一区| 国产欧美日韩综合精品| 精品久久久久久久久久久| 亚洲欧洲自拍偷拍| 亚洲级视频在线观看免费1级| 欧美亚洲成人免费| 最近2019免费中文字幕视频三| 日本免费久久高清视频| 黄色成人av在线| 精品视频久久久久久久| 国产日本欧美一区| 亚洲社区在线观看| 国产丝袜精品第一页| 国产视频久久久| 亚洲精品自拍第一页| 日韩一区二区欧美| 日韩av在线免费观看| 日韩激情在线视频| 欧美大片在线看| 亚洲欧美在线第一页| 国产精品久久在线观看| 成人97在线观看视频| 91网站在线免费观看| 日韩av有码在线| 国产香蕉精品视频一区二区三区| 久久国产精品99国产精| 欧美日韩一区二区三区在线免费观看| 亚洲va电影大全| 亚洲日韩中文字幕在线播放| 国产日韩精品在线观看| 欧美日韩一区二区精品| 精品久久久久久亚洲国产300| 亚洲成人网在线观看| 日韩大胆人体377p| 欧美精品久久久久久久免费观看| 日韩中文字在线| 亚洲自拍偷拍区| 成人av在线亚洲| 精品一区二区三区电影| 91高清在线免费观看| 国产一区玩具在线观看| 国产91精品久久久久| 日本精品久久久久久久| 亚洲国产中文字幕在线观看| 国产精品一久久香蕉国产线看观看| 成人a在线视频| 精品国产乱码久久久久久虫虫漫画| 久久久久久久电影一区| 亚洲最大福利网站| 国内精品久久久久伊人av| 久久理论片午夜琪琪电影网| 亚洲白拍色综合图区| 欧美成人免费网| 国产a级全部精品| 欧美激情网站在线观看| 中日韩美女免费视频网站在线观看| 国产精品久久久久久久久久久久| 欧美极品xxxx| 狠狠色狠狠色综合日日小说| 久久久久久久久久婷婷| 久久人人爽人人爽人人片av高请| 亚洲自拍小视频免费观看| 欧美极品美女视频网站在线观看免费| 最近的2019中文字幕免费一页| 成人精品视频99在线观看免费| 日韩精品欧美激情| 精品亚洲一区二区三区四区五区| 国产精品高清在线观看| 91热福利电影| 国产综合福利在线| 亚洲欧洲美洲在线综合| 26uuu久久噜噜噜噜| 国产香蕉精品视频一区二区三区| 亚洲天堂男人天堂| 欧美日韩色婷婷| 亚洲黄色有码视频| 精品国产1区2区| 日本一区二区在线免费播放| 久久久国产一区二区| 精品日韩视频在线观看| 日韩欧美在线第一页| 成人性教育视频在线观看| 91精品综合久久久久久五月天| 日韩精品免费一线在线观看| 亚洲精品一区二区网址| 中文字幕九色91在线| 欧美国产精品va在线观看| 国产一区二区三区高清在线观看| 精品久久久久久久久久久久久| 久久久av网站| 国产日韩av在线| 91久久久国产精品| 国产一区二区视频在线观看| 亚洲精品中文字幕女同| 国语自产精品视频在免费| 久久精品国产电影| www亚洲精品| 日韩激情片免费| 精品久久国产精品| 国产亚洲精品久久久优势| 精品久久久久久久久久国产| 国产一区二区三区精品久久久| 久久九九全国免费精品观看| 伊人久久五月天| 26uuu国产精品视频| 91av成人在线| 日韩欧美精品在线观看| 色综合天天狠天天透天天伊人| 欧美专区在线播放| 黑人精品xxx一区一二区| 最近2019年日本中文免费字幕| 久久久国产精品免费| 欧美又大又硬又粗bbbbb| 欧美精品成人在线| 亚洲成人av在线| 午夜精品久久久久久久白皮肤| 国产精品第一视频| 欧美另类老女人| 欧美贵妇videos办公室| 亚洲欧美成人一区二区在线电影| 欧美久久精品一级黑人c片| 亚洲成人中文字幕| 97国产精品视频人人做人人爱| 久久精品中文字幕| 中文字幕一区日韩电影| 这里只有精品视频| 亚洲女人天堂网| 成人羞羞国产免费| 国产69精品99久久久久久宅男| 久久久久久国产精品三级玉女聊斋| 亚洲免费影视第一页| 国产精品视频网站| 亚洲国产另类 国产精品国产免费| 日韩中文理论片| 91精品国产高清久久久久久久久| 日韩暖暖在线视频| 欧美国产日本高清在线| 在线观看精品国产视频| 2020国产精品视频| 欧美第一黄网免费网站| 久久久av亚洲男天堂| 欧美性猛交视频| 色综合久久久888| 国产精品影院在线观看| 久久久91精品| 精品美女久久久久久免费| 国产精品毛片a∨一区二区三区|国| 亚洲精品国产精品国自产观看浪潮| 亚洲精品免费在线视频| 亚洲人线精品午夜|