日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

Android教你如何發(fā)現(xiàn)APP卡頓的實現(xiàn)

瀏覽:99日期:2022-09-21 18:26:29

最近部門打算優(yōu)化下 APP 在低端機上的卡頓情況,既然想優(yōu)化,就必須獲取卡頓情況,那么如何獲取卡頓情況就是本文目的。

一般主線程過多的 UI 繪制、大量的 IO 操作或是大量的計算操作占用 CPU,導(dǎo)致 App 界面卡頓。只要我們能在發(fā)生卡頓的時候,捕捉到主線程的堆棧信息和系統(tǒng)的資源使用信息,即可準(zhǔn)確分析卡頓發(fā)生在什么函數(shù),資源占用情況如何。那么問題就是如何有效檢測 Android 主線程的卡頓發(fā)生?

用 adb 系統(tǒng)工具觀察 App 的卡頓數(shù)據(jù)情況,試圖重現(xiàn)場景來定位問題。

常用的方式是使用 adb SurfaceFlinger 服務(wù)和 adb gfxinfo 功能,在自動化操作 app 的過程中,使用 adb 獲取數(shù)據(jù)來監(jiān)控 app 的流暢情況,發(fā)現(xiàn)出現(xiàn)出現(xiàn)卡頓的時間段,尋找出現(xiàn)卡頓的場景和操作。

方式1:adb shell dumpsysSurfaceFlinger

使用 ‘a(chǎn)db shell dumpsysSurfaceFlinger’ 命令即可獲取最近 127 幀的數(shù)據(jù),通過定期執(zhí)行 adb 命令,獲取幀數(shù)來計算出幀率 FPS。

方式2:adb shell dumpsys gfxinfo

使用 ‘a(chǎn)db shell dumpsys gfxinfo’ 命令即可獲取最新 128 幀的繪制信息,詳細包括每一幀繪制的 Draw,Process,Execute 三個過程的耗時,如果這三個時間總和超過 16.6ms 即認為是發(fā)生了卡頓。

已有的兩種方案比較適合衡量回歸卡頓問題的修復(fù)效果和判斷某些特定場景下是否有卡頓情況,然而,這樣的方式有幾個明顯的不足:

一般很難構(gòu)造實際用戶卡頓的環(huán)境來重現(xiàn); 這種方式操作起來比較麻煩,需編寫自動化用例,無法覆蓋大量的可疑場景,測試重現(xiàn)耗時耗人力; 無法衡量靜態(tài)頁面的卡頓情況; 出現(xiàn)卡頓的時候app無法及時獲取運行狀態(tài)和信息,開發(fā)定位困難。

隨著對Android 源碼的深入研究,也有了其他兩種比較方便的方式,并且這兩種方式侵入性小,占用內(nèi)存低,能夠更好的用在實際場景中:

利用UI線程的Looper打印的日志匹配; 使用Choreographer.FrameCallback

利用 UI 線程的 Looper 打印的日志匹配

Android 主線程更新 UI。如果界面1秒鐘刷新少于 60 次,即 FPS 小于 60,用戶就會產(chǎn)生卡頓感覺。簡單來說,Android 使用消息機制進行 UI 更新,UI 線程有個 Looper,在其 loop方法中會不斷取出 message,調(diào)用其綁定的 Handler 在 UI 線程執(zhí)行。如果在 handler 的 dispatchMesaage 方法里有耗時操作,就會發(fā)生卡頓。

下面來看下 Looper.loop( ) 的源碼

public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException('No Looper; Looper.prepare() wasn’t called on this thread.'); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); // Allow overriding a threshold with a system prop. e.g. // adb shell ’setprop log.looper.1000.main.slow 1 && stop && start’ final int thresholdOverride =SystemProperties.getInt('log.looper.' + Process.myUid() + '.' + Thread.currentThread().getName() + '.slow', 0); boolean slowDeliveryDetected = false; for (;;) { Message msg = queue.next(); // might block if (msg == null) {// No message indicates that the message queue is quitting.return; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) {logging.println('>>>>> Dispatching to ' + msg.target + ' ' + msg.callback + ': ' + msg.what); } // Make sure the observer won’t change while processing a transaction. final Observer observer = sObserver; final long traceTag = me.mTraceTag; long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs; if (thresholdOverride > 0) {slowDispatchThresholdMs = thresholdOverride;slowDeliveryThresholdMs = thresholdOverride; } final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0); final boolean logSlowDispatch = (slowDispatchThresholdMs > 0); final boolean needStartTime = logSlowDelivery || logSlowDispatch; final boolean needEndTime = logSlowDispatch; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0; final long dispatchEnd; Object token = null; if (observer != null) {token = observer.messageDispatchStarting(); } long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid); try {msg.target.dispatchMessage(msg);if (observer != null) { observer.messageDispatched(token, msg);}dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } catch (Exception exception) {if (observer != null) { observer.dispatchingThrewException(token, msg, exception);}throw exception; } finally {ThreadLocalWorkSource.restore(origWorkSource);if (traceTag != 0) { Trace.traceEnd(traceTag);} } if (logSlowDelivery) {if (slowDeliveryDetected) { if ((dispatchStart - msg.when) <= 10) { Slog.w(TAG, 'Drained'); slowDeliveryDetected = false; }} else { if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, 'delivery', msg)) { // Once we write a slow delivery log, suppress until the queue drains. slowDeliveryDetected = true; }} } if (logSlowDispatch) {showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, 'dispatch', msg); } if (logging != null) {logging.println('<<<<< Finished to ' + msg.target + ' ' + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn’t corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) {Log.wtf(TAG, 'Thread identity changed from 0x' + Long.toHexString(ident) + ' to 0x' + Long.toHexString(newIdent) + ' while dispatching to ' + msg.target.getClass().getName() + ' ' + msg.callback + ' what=' + msg.what); } msg.recycleUnchecked(); } }

代碼中兩處標(biāo)紅的地方,就是 msg.target.dispatchMessage(msg) 的執(zhí)行前后索打印的 log。通過測量處理時間就能檢測到部分UI線程是否有耗時的操作。注意到這行執(zhí)行代碼的前后,有兩個 logging.println 函數(shù),如果設(shè)置了logging,會分別打印出 ”>>>>> Dispatching to “ 和 ”<<<<< Finished to “ 這樣的日志,這樣我們就可以通過兩次log的時間差值,來計算 dispatchMessage 的執(zhí)行時間,從而設(shè)置閾值判斷是否發(fā)生了卡頓。

那么如何設(shè)置 logging 呢?

我們看下面的代碼:

/** * Control logging of messages as they are processed by this Looper. If * enabled, a log message will be written to <var>printer</var> * at the beginning and ending of each message dispatch, identifying the * target Handler and message contents. * * @param printer A Printer object that will receive log messages, or * null to disable message logging. */public final class Looper { private Printer mLogging; public void setMessageLogging(@Nullable Printer printer) { mLogging = printer; } } public interface Printer { void println(String x); }

Looper 的 mLogging 是私有的,并且提供了 setMessageLogging(@Nullable Printer printer) 方法,所以我們可以自己實現(xiàn)一個 Printer,在通過 setMessageLogging() 方法傳入即可,代碼如下:

public class BlockDetectByPrinter { public static void start() { Looper.getMainLooper().setMessageLogging(new Printer() { private static final String START = '>>>>> Dispatching'; private static final String END = '<<<<< Finished'; @Override public void println(String x) {if (x.startsWith(START)) { LogMonitor.getInstance().startMonitor();}if (x.startsWith(END)) { LogMonitor.getInstance().removeMonitor();} } }); }}

設(shè)置了logging后,loop方法會回調(diào) logging.println 打印出每次消息執(zhí)行的時間日志:”>>>>> Dispatching to “和”<<<<< Finished to “。BlockDetectByPrinter 的使用則在Application 的 onCreate 方法中調(diào)用 BlockDetectByPrinter.start() 即可。

我們可以簡單實現(xiàn)一個 LogMonitor 來記錄卡頓時候主線程的堆棧信息。當(dāng)匹配到 >>>>> Dispatching 時,執(zhí)行 startMonitor,會在 200ms(設(shè)定的卡頓閾值)后執(zhí)行任務(wù),這個任務(wù)負責(zé)在子線程(非UI線程)打印UI線程的堆棧信息。如果消息低于 200ms 內(nèi)執(zhí)行完成,就可以匹配到 <<<<< Finished 日志,那么在打印堆棧任務(wù)啟動前執(zhí)行 removeMonitor 取消了這個任務(wù),則認為沒有卡頓的發(fā)生;如果消息超過 200ms 才執(zhí)行完畢,此時認為發(fā)生了卡頓,并打印 UI 線程的堆棧信息。

LogMonitor如何實現(xiàn)?

public class LogMonitor { private static final String TAG = 'LogMonitor'; private static LogMonitor sInstance = new LogMonitor(); private HandlerThread mLogThread = new HandlerThread('log'); private Handler mIoHandler; private static final long TIME_BLOCK = 200L; private LogMonitor() { mLogThread.start(); mIoHandler = new Handler(mLogThread.getLooper()); } private static Runnable mLogRunnable = new Runnable() { @Override public void run() { StringBuilder sb = new StringBuilder(); StackTraceElement[] stackTrace = Looper.getMainLooper().getThread().getStackTrace(); for (StackTraceElement s : stackTrace) {sb.append(s.toString() + 'n'); } Log.e(TAG, sb.toString()); } }; public static LogMonitor getInstance() { return sInstance; } public boolean isMonitor() { return mIoHandler.hasCallbacks(mLogRunnable); } public void startMonitor() { mIoHandler.postDelayed(mLogRunnable, TIME_BLOCK); } public void removeMonitor() { mIoHandler.removeCallbacks(mLogRunnable); }}

這里我們使用 HandlerThread 來構(gòu)造一個 Handler,HandlerThread 繼承自 Thread,實際上就一個 Thread,只不過比普通的 Thread 多了一個 Looper,對外提供自己這個 Looper 對象的 getLooper 方法,然后創(chuàng)建 Handler 時將 HandlerThread 中的 looper 對象傳入。這樣我們的 mIoHandler 對象就是與 HandlerThread 這個非 UI 線程綁定的了,它處理耗時操作將不會阻塞UI。如果UI線程阻塞超過 200ms,就會在子線程中執(zhí)行 mLogRunnable,打印出 UI 線程當(dāng)前的堆棧信息,如果處理消息沒有超過 1000ms,則會實時的 remove 掉這個mLogRunnable 任務(wù)。

發(fā)生卡頓時打印出堆棧信息的大致內(nèi)容如下,開發(fā)可以通過 log 定位耗時的地方。

2020-10-30 14:26:13.823 30359-30415/com.example.myproxyplugin E/LogMonitor: java.lang.Thread.sleep(Native Method) java.lang.Thread.sleep(Thread.java:443) java.lang.Thread.sleep(Thread.java:359) com.example.myproxyplugin.MainActivity$1.run(MainActivity.java:22) android.os.Handler.handleCallback(Handler.java:900) android.os.Handler.dispatchMessage(Handler.java:103) android.os.Looper.loop(Looper.java:219) android.app.ActivityThread.main(ActivityThread.java:8347) java.lang.reflect.Method.invoke(Native Method) com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513) com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1055)

優(yōu)點:用戶使用 app 或者測試過程中都能從app層面來監(jiān)控卡頓情況,一旦出現(xiàn)卡頓能記錄 app 狀態(tài)和信息, 只要dispatchMesaage執(zhí)行耗時過大都會記錄下來,不再有前面兩種adb方式面臨的問題與不足。

缺點:需另開子線程獲取堆棧信息,會消耗少量系統(tǒng)資源。

在實際實現(xiàn)中,不同手機不同 Android 系統(tǒng)甚至是不同的 ROM 版本,Loop 函數(shù)不一定都能打印出 ”>>>>> Dispatching to “ 和 ”<<<<< Finished to “ 這樣的日志,導(dǎo)致該方式無法進行。

優(yōu)化的策略:我們知道 Loop 函數(shù)開始和結(jié)束必會執(zhí)行 println 打印日志,所以優(yōu)化版本將卡頓的判斷改為,Loop輸出第一句 log 時當(dāng)作 startMonitor,輸出下一句log時當(dāng)作end時刻來解決這個問題。

其實 Looper 中有個 Observer 接口可以很好的完成這個任務(wù),只是因為被標(biāo)記為 hide 了,所以我們不能使用,不過可以知道下。

Observer 接口提供了三個方法,分別是監(jiān)聽任務(wù)開始,結(jié)束,發(fā)生錯誤的回調(diào)。

/** {@hide} */ public interface Observer { /** * Called right before a message is dispatched. * * <p> The token type is not specified to allow the implementation to specify its own type. * * @return a token used for collecting telemetry when dispatching a single message. * The token token must be passed back exactly once to either * {@link Observer#messageDispatched} or {@link Observer#dispatchingThrewException} * and must not be reused again. * */ Object messageDispatchStarting(); /** * Called when a message was processed by a Handler. * * @param token Token obtained by previously calling * {@link Observer#messageDispatchStarting} on the same Observer instance. * @param msg The message that was dispatched. */ void messageDispatched(Object token, Message msg); /** * Called when an exception was thrown while processing a message. * * @param token Token obtained by previously calling * {@link Observer#messageDispatchStarting} on the same Observer instance. * @param msg The message that was dispatched and caused an exception. * @param exception The exception that was thrown. */ void dispatchingThrewException(Object token, Message msg, Exception exception); }

利用Choreographer.FrameCallback監(jiān)控卡頓

Choreographer.FrameCallback 官方文檔鏈接(https://developer.android.com/reference/android/view/Choreographer.FrameCallback.html)

我們知道, Android 系統(tǒng)每隔 16ms 發(fā)出 VSYNC 信號,來通知界面進行重繪、渲染,每一次同步的周期為16.6ms,代表一幀的刷新頻率。SDK 中包含了一個相關(guān)類,以及相關(guān)回調(diào)。理論上來說兩次回調(diào)的時間周期應(yīng)該在 16ms,如果超過了 16ms 我們則認為發(fā)生了卡頓,利用兩次回調(diào)間的時間周期來判斷是否發(fā)生卡頓(這個方案是 Android 4.1 API 16 以上才支持)。

這個方案的原理主要是通過 Choreographer 類設(shè)置它的 FrameCallback 函數(shù),當(dāng)每一幀被渲染時會觸發(fā)回調(diào) FrameCallback, FrameCallback 回調(diào) void doFrame (long frameTimeNanos) 函數(shù)。一次界面渲染會回調(diào) doFrame 方法,如果兩次 doFrame 之間的間隔大于 16.6ms 說明發(fā)生了卡頓。

public class FPSFrameCallback implements Choreographer.FrameCallback { private static final String TAG = 'FPS_TEST'; private long mLastFrameTimeNanos = 0; private long mFrameIntervalNanos; public FPSFrameCallback(long lastFrameTimeNanos) { mLastFrameTimeNanos = lastFrameTimeNanos; // 1s 60 幀 mFrameIntervalNanos = (long) (1000000000 / 60.0); } @Override public void doFrame(long frameTimeNanos) { //初始化時間 if (mLastFrameTimeNanos == 0) { mLastFrameTimeNanos = frameTimeNanos; } final long jitterNanos = frameTimeNanos - mLastFrameTimeNanos; if (jitterNanos >= mFrameIntervalNanos) { final long skippedFrames = jitterNanos / mFrameIntervalNanos; if (skippedFrames > 30) {Log.i(TAG, 'Skipped ' + skippedFrames + ' frames! ' + 'The application may be doing too much work on its main thread.'); } } mLastFrameTimeNanos = frameTimeNanos; //注冊下一幀回調(diào) Choreographer.getInstance().postFrameCallback(this); }}

本質(zhì)和 log 沒太多區(qū)別,但是這個更加通用些,不會因為機型系統(tǒng)原因出現(xiàn)不可用的問題。

示例

下面進入實戰(zhàn),看看代碼層面是如何實現(xiàn)的。

MainActivity 代碼如下:

public class MainActivity extends AppCompatActivity { Handler handler = new Handler(Looper.getMainLooper()); private final Runnable runnable = new Runnable() { @Override public void run() { try {Thread.sleep(600);handler.postDelayed(runnable, 500); } catch (InterruptedException e) {e.printStackTrace(); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Choreographer.getInstance().postFrameCallback(new FPSFrameCallback(System.nanoTime())); BlockDetectByPrinter.start(); } @Override protected void onResume() { super.onResume(); handler.postDelayed(runnable, 500); }}

收集到的堆棧信息如下:

2020-10-30 14:26:13.823 30359-30415/com.example.myproxyplugin E/LogMonitor: java.lang.Thread.sleep(Native Method) java.lang.Thread.sleep(Thread.java:443) java.lang.Thread.sleep(Thread.java:359) com.example.myproxyplugin.MainActivity$1.run(MainActivity.java:22) android.os.Handler.handleCallback(Handler.java:900) android.os.Handler.dispatchMessage(Handler.java:103) android.os.Looper.loop(Looper.java:219) android.app.ActivityThread.main(ActivityThread.java:8347) java.lang.reflect.Method.invoke(Native Method) com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513) com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1055)

對于 FPS log 可以看到如下信息:

I/Choreographer: Skipped 64 frames! The application may be doing too much work on its main thread. I/FPS_TEST: Skipped 65 frames! The application may be doing too much work on its main thread.

如果你要把上面的方法用到自己的APP 中,那么還需要很多操作,具體可以閱讀參考文獻的內(nèi)容。

參考文章

廣研Android卡頓監(jiān)控系統(tǒng)

到此這篇關(guān)于Android教你如何發(fā)現(xiàn)APP卡頓的實現(xiàn)的文章就介紹到這了,更多相關(guān)Android APP卡頓內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Android
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
日本中文字幕视频一区| 国产模特精品视频久久久久| 国产色播av在线| 国产欧美日韩免费观看| 综合视频一区| 日本精品国产| 国产精品高潮呻吟久久久久| 国产伦一区二区三区| 国产精选一区| 欧美成人a交片免费看| 欧美粗暴jizz性欧美20| 香蕉精品999视频一区二区| 视频一区在线视频| 欧美精品三级在线| 黑森林国产精品av| 欧美日韩精品一区二区视频| 一区免费在线| 国产日韩一区| 毛片不卡一区二区| 99精品视频在线| 伊人久久一区| 国产+成+人+亚洲欧洲在线| 久久亚洲专区| 亚洲一级淫片| 精品久久久中文字幕| 久久国产中文字幕| 日韩激情av在线| 国产精品久久久久av电视剧| 蜜臀久久99精品久久久久久9| 欧美一区久久| 久久天堂av| 日本a口亚洲| japanese国产精品| 亚洲精品国产精品粉嫩| 欧美男人天堂| 国产精品视频一区二区三区四蜜臂| 中文另类视频| 国产精品久久久久久妇女| 91精品成人| 日韩一区二区三区在线免费观看| 亚洲三级网站| 黄色成人在线网址| 在线精品亚洲欧美日韩国产| 欧美一级网站| 亚洲午夜久久| 夜久久久久久| 欧美日韩中文一区二区| 久久精品九色| 妖精视频成人观看www| 中文在线资源| 国产一区调教| 久久精品亚洲| 欧美激情在线精品一区二区三区| 男女性色大片免费观看一区二区| 国产精品二区不卡| 里番精品3d一二三区| 日韩精品免费观看视频| 亚洲小说春色综合另类电影| 亚洲天堂黄色| 国产综合精品| 亚洲大全视频| 黄色成人在线网址| 日韩精品一二三区| 国产精品97| 亚洲综合国产| 日本不卡不码高清免费观看 | 成人av二区| 红桃视频国产精品| 最新日韩欧美| 免费看黄色91| 国产三级精品三级在线观看国产| 国产美女撒尿一区二区| 国产精品极品国产中出| 欧美精品影院| 一区二区三区四区日本视频| 99久久亚洲精品| 亚洲欧洲一区| 日韩精品免费一区二区夜夜嗨| 国产视频一区二| 99精品网站| 日韩欧美中文字幕电影| 国产精品香蕉| 色爱av综合网| 首页国产欧美日韩丝袜| 久久99视频| 婷婷六月综合| 久久国际精品| 日韩三区免费| 一区二区精品| 福利一区和二区| 美女国产一区| 成人片免费看| 欧美一区91| 九一精品国产| 国产福利资源一区| 妖精视频成人观看www| 麻豆久久久久久| 免费观看久久久4p| 日本蜜桃在线观看视频| 亚洲精品精选| 欧美日韩色图| 国产精品多人| 中文字幕av一区二区三区人| 久久uomeier| 青青草国产成人99久久| 欧美日韩中文一区二区| 精品久久免费| 久久国产日韩欧美精品| 亚洲一区日本| 亚洲成人va| 国产精品亚洲欧美一级在线| 亚洲少妇自拍| 日韩国产欧美| 国产精品久久久久久久免费观看 | 91精品一区二区三区综合| 日韩1区2区3区| 热久久久久久久| 欧美肉体xxxx裸体137大胆| 国际精品欧美精品| 国产一区二区三区四区| 欧美偷窥清纯综合图区| 亚洲精品在线a| 日韩免费精品| 日韩高清二区| 日本伊人久久| 欧美一区网站| 国产精品久一| 你懂的网址国产 欧美| 日韩中出av| 日韩福利视频导航| 国产亚洲高清一区| 国产精品色婷婷在线观看| 69堂免费精品视频在线播放| 欧美私人啪啪vps| 国产精品17p| 成人在线观看免费视频| 国产videos久久| 亚洲高清av| 丝瓜av网站精品一区二区| 中文字幕av一区二区三区人| 91麻豆精品| 日韩a一区二区| 亚洲先锋成人| 亚洲黄色在线| 国产日韩欧美在线播放不卡| 久久99久久人婷婷精品综合| 日韩成人精品一区| 国产精品7m凸凹视频分类| 日韩在线卡一卡二| 欧美91在线|欧美| 久久精品国产68国产精品亚洲| 夜久久久久久| 久久中文字幕导航| 红桃视频亚洲| 欧美激情一区| 亚洲一区二区成人| 国产一区一一区高清不卡| 午夜欧美精品| 清纯唯美亚洲综合一区| xxxxx性欧美特大| 欧美日韩一区二区高清| 欧美日韩中文一区二区| 国产视频一区二区在线播放| 欧美日韩尤物久久| 国产精品久久久亚洲一区| 999国产精品| 精品99在线| 日韩精品久久久久久| 激情欧美亚洲| 国产资源在线观看入口av| 91精品国产自产在线丝袜啪| 亚洲精品国产偷自在线观看| 久久不见久久见国语| 午夜国产精品视频| 视频在线不卡免费观看| 国产精品嫩草影院在线看| 久久av一区| 久久视频国产| 亚洲免费福利| 麻豆精品新av中文字幕| 日韩一区二区三区精品| 亚洲欧洲一区二区天堂久久| 韩日一区二区| 欧美激情aⅴ一区二区三区| 91精品国产自产观看在线| 亚洲精品第一| 视频一区中文字幕精品| 首页国产欧美日韩丝袜| 人人精品亚洲| 亚洲黄色中文字幕| 国产精品国产三级国产在线观看| 免费亚洲一区| 日本精品黄色| 中文字幕系列一区| 国产精品毛片一区二区在线看| 国产成人久久精品麻豆二区 | 久久久久久免费视频| 四虎4545www国产精品| 欧美日韩水蜜桃| 国产精品日韩久久久|