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

首頁 > 學院 > 開發設計 > 正文

探索Glide對Gif圖片資源的獲取、解析過程

2019-11-09 14:02:49
字體:
來源:轉載
供稿:網友

先預祝大家湯圓節快樂!很久沒寫博客了。今天我們來探索一下Glide是如何支持Gif圖片加載的。

本篇博客的目的

了解代碼分析的基本思路與方法了解Glide是如何對Gif圖片進行支持的

探索背景

為什么會有這么一個想法呢,一來一直對Glide是知其名而不知其所以然,二來還主要是工作中需要對它研究研究,以便更好的支持工作內容。

我想很多同學都希望自己可以對某種著名的開源框架了解貫通,但是很多時候研究一款框架實在是費神費力,很容易就會放棄。

造成這樣的困局主要有三點:

一來因為我們在探究源碼時沒有明確的目標。二來是因為我們沒有合適順手的工具。三來是因為找不到重點,容易被其它不相干代碼迷惑。

接下來我們就對上面這些問題一一帶入。

探索準備工作

1,首先我的目標很明確,我需要了解Glide是否支持Gif圖片,以及它是如何支持Gif圖片的。這樣我才可以在應用層對其做良好的支持。

因為我們的工作要求是:所有的ImageView都必須支持Gif圖片

我的解決辦法有三種:

1.如果Glide支持Gif圖片,那么我只需要在圖片調用層全部加上Gif支持開關。(事實上Glide默認就支持Gif,不需要我單獨添加控制。)2.如果Glide支持Gif圖片,但是它的檢測開銷成本很大,那我就必須手動對資源進行解析,判斷是否是Gif,如果是,則調用Gif圖片的加載邏輯。如果不是,則走一般的圖片加載邏輯。3.如果Glide不支持Gif圖片,那么我必須對ImageView進行擴展,然后更改應用內所有的ImageView的繼承關系。這個工作量是巨大的。

因為有以上判斷條件,所以我決定先從Glide的Gif支持入手。

2,因為我們需要對Glide研究、分析,那么手上必須有Glide的最新代碼。我們在Glide的主頁上找到源代碼的下載地址,下載即可。

Glide首頁: https://github.com/bumptech/glide/releases Glide源碼地址: https://github.com/bumptech/glide/releases/download/v3.7.0/glide-3.7.0-sources.jar

3.準備工作已經做的差不多了,最后還剩代碼分析利器Android Studio以及Source Insight,當然放在手邊為我們做輔助記錄的筆和紙是少不了的。

Source Insight的主頁為:https://www.sourceinsight.com/ Source Insight的功能很強大,我也只是懂一點點基本用法而已,不過足夠用了。下載好的代碼需要使用Source Insight打開,我們需要實時檢索文件使用。這里不再說明Source Insight的用法,請自行學習了解。它在這里的作用是幫我們做一些引用關系檢查。

除了Source Insight之外,我們主要使用Android Studio進行代碼分析調試。需要將剛剛下載好的源代碼解壓,然后作為我們工程的一部分: 這里寫圖片描述

然后按照Glide的使用說明開始我們的分析入口編寫:

// For a simple view:@Override public void onCreate(Bundle savedInstanceState) { ... ImageView imageView = (ImageView) findViewById(R.id.my_image_view); Glide.with(this).load("http://QQ.yh31.com/tp/zjbq/201612231514480890.gif").into(imageView);}

為了輔助我們一次次分析Glide的網絡訪問,我們在onDestroy方法中加入以下代碼:

PRotected void onDestroy() { super.onDestroy(); Glide.get(this).clearMemory(); Glide.get(this).clearDiskCache();}

開始探索之旅

我們如果需要了解Glide是否默認支持Gif圖片,那么只需要在load方法內替換成gif圖片的地址即可。

我們發現,它支持。

那么它是如何完成網絡資源獲取、Gif類型識別、Gif資源解析這些工作的呢?下面讓我們一起來一探究竟。

Glide的網絡資源獲取

Glide對Gif資源的獲取也是Glide網絡請求的核心,我想大家對這些框架一般都看中的是這部分。讓我們從這里究其所以然。

在這里聲明一下,我們剛開始拿到代碼時,就算會使用,也不知道真正的分析入口在哪里。但是不要灰心,就算是對代碼再熟悉的人,也會迷失在這結構復雜的代碼海洋里。請記住,分析的過程是總是需要來回反復查看、嘗試的。所以手邊的紙和筆對我們的幫助就體現出來了,我們需要通過紙和筆來記錄我們走過的重要流程。

PS: 以后的分析過程會將沒有歧義的過程自動略過,并且會將無關代碼自動省略。

PS: 我們的分析手段主要有兩種,一是通過斷點調試來分析,二是通過上下文來分析。其中第一種比較方便,文章中主要采用第一種方法。

我們先來分析這段代碼:

Glide.with(this)

由于我們是在Activity中使用的,所以這里的this應當是Activity,我們進入這個方法查看:

public static RequestManager with(FragmentActivity activity) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(activity); }

好,從上面得知,這個方法返回了一個RequestManager對象,接下來分析

.load("http://qq.yh31.com/tp/zjbq/201612231514480890.gif")

這里的load方法則調用的是RequestManager的load方法,我們看一下:

public DrawableTypeRequest<String> load(String string) { return (DrawableTypeRequest<String>) fromString().load(string); }

我們看到,load方法返回了一個DrawableTypeRequest對象,我們先記住它。接下來我們需要分析

.into(new ImageView(this));

我們跟著這個into方法一路追蹤,最后來到了GenericRequestBuilder的into方法:

public <Y extends Target<TranscodeType>> Y into(Y target) { ... Request request = buildRequest(target); target.setRequest(request); lifecycle.addListener(target); requestTracker.runRequest(request); return target; }

這里我們看到構建了一個Request對象,我們進去看一下是如何構建這個對象的,最后我們在GenericRequestBuilder類中定位到了這個方法:

private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority, RequestCoordinator requestCoordinator) { return GenericRequest.obtain( loadProvider, model, signature, context, priority, target, sizeMultiplier, placeholderDrawable, placeholderId, errorPlaceholder, errorId, fallbackDrawable, fallbackResource, requestListener, requestCoordinator, glide.getEngine(), transformation, transcodeClass, isCacheable, animationFactory, overrideWidth, overrideHeight, diskCacheStrategy); }

看來上面提到的Request對象實則為GenericRequest的實例,我們先記下。

然后返回進入requestTracker.runRequest(request)中查看,看起來像是運行這個請求的意思。

runRequest的內部實現是這樣的:

public void runRequest(Request request) { requests.add(request); if (!isPaused) { request.begin(); } else { pendingRequests.add(request); } }

它內部調用了request對象的begin方法,也就是說這里調用了GenericRequest的begin()方法。我們找到這個方法:

public void begin() { ... if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight); } else { target.getSize(this); } ... }

在這里走的else條件,我們可能已經不太記得target到底是誰實現的,它只是個接口,幸好有AS,我們通過調試知道這個target其實為:GlideDrawableImageViewTarget,具體它是什么時候被設置到這里的,我們先不去深究它,肯定能找到地方,但找它不是我們的目的。

我們找到它對應的getSize()方法:

public void getSize(SizeReadyCallback cb) { sizeDeterminer.getSize(cb); }

我們不要在這里停留,繼續往下走,最后我們會走到com.bumptech.glide.request.GenericRequest的onSizeReady方法中,我們在這里注意重點部分:

public void onSizeReady(int width, int height) { ... loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder, priority, isMemoryCacheable, diskCacheStrategy, this); ... }

從Engine的load方法我們進去看,這里是我們繼續執行的重點,我們進入到com.bumptech.glide.load.engine.Engine的load方法:

public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher, DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder, Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) { ... EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable); DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation, transcoder, diskCacheProvider, diskCacheStrategy, priority); EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority); jobs.put(key, engineJob); engineJob.addCallback(cb); engineJob.start(runnable); ... return new LoadStatus(cb, engineJob); }

在這路上一定不能被其它代碼迷惑,要感知哪部分是重點,嘗試自己分析一下這部分。有沒有很像任務及線程池?沒錯,你如果看各個類之間的繼承關系的話,它們確實是,我們就不再看它們之間的關系,我們只用看EngineRunnable的run()方法。

public void run() { ... Exception exception = null; Resource<?> resource = null; try { resource = decode(); } catch (Exception e) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Exception decoding", e); } exception = e; } ... if (resource == null) { onLoadFailed(exception); } else { onLoadComplete(resource); } }

這段代碼主要由兩部分組成,這先簡單描述一下它們的工作流程,首先進入decode方法嘗試從緩存中獲取資源,第一次當然是null,然后進入onLoadFailed方法。onLoadFailed會將這個任務再次提交,再次重新執行,這次會進入decodeFromSource方法:

private Resource<?> decodeFromSource() throws Exception { return decodeJob.decodeFromSource(); }

我們一路向下,最后來到com.bumptech.glide.load.engine.DecodeJob的decodeSource方法,這個過程千萬別掉隊了,這里馬上就要見到如何訪問網絡了:

private Resource<T> decodeSource() throws Exception { ... final A data = fetcher.loadData(priority); ... decoded = decodeFromSourceData(data); ... return decoded; }

這里有兩部分重點,一個是獲取資源,一個是對資源進行解析。這里的fetcher也是一個接口,它的實現類中有HttpUrlFetcher,很明顯的網絡資源獲取類,我們通過調試也發現這里的對象是ImageVideoFetcher,而它的內部正是調用了HttpUrlFetcher的loadData方法,我們再繼續往下,我們很快就發現了Glide的網絡訪問核心方法:

private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException { ... urlConnection = connectionFactory.build(url); for (Map.Entry<String, String> headerEntry : headers.entrySet()) { urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue()); } urlConnection.setConnectTimeout(2500); urlConnection.setReadTimeout(2500); urlConnection.setUseCaches(false); urlConnection.setDoInput(true); ... final int statusCode = urlConnection.getResponseCode(); if (statusCode / 100 == 2) { return getStreamForSuccessfulRequest(urlConnection); } ... }

好,是不是很熟悉呢?原來Glide內部使用了Android的HttpURLConnection來進行網絡訪問,而且這里的訪問訪問超時時間是固定的:2500毫秒。

到目前為止,我們所處的位置為HttpUrlFetcher的loadDataWithRedirects方法,當然,我們并不在主線程:

at com.bumptech.glide.load.data.HttpUrlFetcher.loadDataWithRedirects(HttpUrlFetcher.java:49) at com.bumptech.glide.load.data.HttpUrlFetcher.loadData(HttpUrlFetcher.java:44) at com.bumptech.glide.load.data.HttpUrlFetcher.loadData(HttpUrlFetcher.java:20) at com.bumptech.glide.load.model.ImageVideoModelLoader$ImageVideoFetcher.loadData(ImageVideoModelLoader.java:70) at com.bumptech.glide.load.model.ImageVideoModelLoader$ImageVideoFetcher.loadData(ImageVideoModelLoader.java:53) at com.bumptech.glide.load.engine.DecodeJob.decodeSource(DecodeJob.java:170) at com.bumptech.glide.load.engine.DecodeJob.decodeFromSource(DecodeJob.java:128) at com.bumptech.glide.load.engine.EngineRunnable.decodeFromSource(EngineRunnable.java:122) at com.bumptech.glide.load.engine.EngineRunnable.decode(EngineRunnable.java:101) at com.bumptech.glide.load.engine.EngineRunnable.run(EngineRunnable.java:58)

所以,到目前為止,我們已經知道了Glide是如何訪問網絡的。

Glide對Gif資源的識別方式

接著上面的部分繼續,因為我們已經得到了從網絡傳回的數據流,那么接下來就需要對這些數據進行解析,我們回到com.bumptech.glide.load.engine.DecodeJo的decodeSource方法,也就是回到這里:

private Resource<T> decodeSource() throws Exception { Resource<T> decoded = null; try { long startTime = LogTime.getLogTime(); final A data = fetcher.loadData(priority); ... decoded = decodeFromSourceData(data); } finally { fetcher.cleanup(); } return decoded; }

因為我們是從fetcher.loadData中返回的,所以接下來我們需要進入decodeFromSourceData方法內,然后再一路向下追蹤,最后來到com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecode的decodeStream方法內:

private GifBitmapWrapper decodeStream(ImageVideoWrapper source, int width, int height, byte[] bytes) throws IOException { InputStream bis = streamFactory.build(source.getStream(), bytes); bis.mark(MARK_LIMIT_BYTES); ImageHeaderParser.ImageType type = parser.parse(bis); bis.reset(); ... return result; }

我們會注意到有段代碼,將InputStream解析為了ImageHeaderParser.ImageType類型的對象,我們可以猜測,這極有可能是對各種網絡流進行分類的地方,我們進去繼續向下追蹤一探究竟,最后來到com.bumptech.glide.load.resource.bitmap.ImageHeaderParser的getType方法:

public ImageType getType() throws IOException { int firstTwoBytes = streamReader.getUInt16(); // JPEG. if (firstTwoBytes == EXIF_MAGIC_NUMBER) { return JPEG; } final int firstFourBytes = firstTwoBytes << 16 & 0xFFFF0000 | streamReader.getUInt16() & 0xFFFF; // PNG. if (firstFourBytes == PNG_HEADER) { // See: http://stackoverflow.com/questions/2057923/how-to-check-a-png-for-grayscale-alpha-color-type streamReader.skip(25 - 4); int alpha = streamReader.getByte(); // A RGB indexed PNG can also have transparency. Better safe than sorry! return alpha >= 3 ? PNG_A : PNG; } // GIF from first 3 bytes. if (firstFourBytes >> 8 == GIF_HEADER) { return GIF; } return UNKNOWN; }

果不其然,在這個方法內部對所有的數據進行識別,我們在最后面看到了gif數據的識別原理:firstFourBytes >> 8 == GIF_HEADER。

Glide對Gif資源的解析方式

好,既然知道了現在的數據流是gif了,那么接下來就是解析過程了,我們回到com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder的decodeStream方法處,繼續往下走,我們很快就在該方法內看到有這么一行代碼:

if (type == ImageHeaderParser.ImageType.GIF) { result = decodeGifWrapper(bis, width, height); }

原來這個方法對GIF類型的圖片做了專門的處理,我們進入這個方法并一路向下,最后我們會來到com.bumptech.glide.load.resource.gif.GifResourceDecoder的decode(byte[] data, int width, int height, GifHeaderParser parser, GifDecoder decoder)方法:

private GifDrawableResource decode(byte[] data, int width, int height, GifHeaderParser parser, GifDecoder decoder) { ... Bitmap firstFrame = decodeFirstFrame(decoder, header, data); ... GifDrawable gifDrawable = new GifDrawable(context, provider, bitmapPool, unitTransformation, width, height, header, data, firstFrame); return new GifDrawableResource(gifDrawable); }

我們注意到在這個方法內解析了Gif資源的第一幀。我們進到decodeFirstFrame方法看一下它是如何解析的:

private Bitmap decodeFirstFrame(GifDecoder decoder, GifHeader header, byte[] data) { decoder.setData(header, data); decoder.advance(); return decoder.getNextFrame(); }

這里最后調用了decoder.getNextFrame()方法,這里的decoder為GifDecoder,也就是專門用于解析Gif資源的解碼器,我們進入getNextFrame()方法一探究竟:

public synchronized Bitmap getNextFrame() { ... status = STATUS_OK; GifFrame currentFrame = header.frames.get(framePointer); GifFrame previousFrame = null; int previousIndex = framePointer - 1; if (previousIndex >= 0) { previousFrame = header.frames.get(previousIndex); } ... // Transfer pixel data to image. Bitmap result = setPixels(currentFrame, previousFrame); ... return result; }

這里的代碼還挺長的,我們只挑最主要的看,它最后調用了setPixels()方法:

private Bitmap setPixels(GifFrame currentFrame, GifFrame previousFrame) { ... // Decode pixels for this frame into the global pixels[] scratch. decodeBitmapData(currentFrame); // Copy each source line to the appropriate place in the destination. int pass = 1; int inc = 8; int iline = 0; for (int i = 0; i < currentFrame.ih; i++) { int line = i; if (currentFrame.interlace) { if (iline >= currentFrame.ih) { pass++; switch (pass) { case 2: iline = 4; break; case 3: iline = 2; inc = 4; break; case 4: iline = 1; inc = 2; break; default: break; } } line = iline; iline += inc; } line += currentFrame.iy; if (line < header.height) { int k = line * header.width; // Start of line in dest. int dx = k + currentFrame.ix; // End of dest line. int dlim = dx + currentFrame.iw; if ((k + header.width) < dlim) { // Past dest edge. dlim = k + header.width; } // Start of line in source. int sx = i * currentFrame.iw; while (dx < dlim) { // Map color and insert in destination. int index = ((int) mainPixels[sx++]) & 0xff; int c = act[index]; if (c != 0) { dest[dx] = c; } dx++; } } } ... // Set pixels for current image. Bitmap result = getNextBitmap(); result.setPixels(dest, 0, width, 0, 0, width, height); return result; }

這段代碼還是很長,我們將不主要的代碼隱去,中間很長一部分推測應該是進行數據轉換。最終是調用了Bitmap的setPixels方法完成位圖的創建。

好,到此為止,我們知道了Gif圖是如何解析成位圖的了,然后我們返回,回到com.bumptech.glide.load.resource.gif.GifResourceDecoder的decode方法繼續向下走:

private GifDrawableResource decode(byte[] data, int width, int height, GifHeaderParser parser, GifDecoder decoder) { ... Bitmap firstFrame = decodeFirstFrame(decoder, header, data);//這里是剛剛出來的地方,從這里繼續向下 if (firstFrame == null) { return null; } Transformation<Bitmap> unitTransformation = UnitTransformation.get(); GifDrawable gifDrawable = new GifDrawable(context, provider, bitmapPool, unitTransformation, width, height, header, data, firstFrame); return new GifDrawableResource(gifDrawable); }

我們很快就發現,剛才解析好的位圖被用作創建了GifDrawable對象,然后GifDrawable對象又用來創建了GifDrawableResource對象,然后返回,回到最開始的com.bumptech.glide.load.engine.EngineRunnable的run方法:

public void run() { if (isCancelled) { return; } Exception exception = null; Resource<?> resource = null; try { resource = decode();//我們剛剛從這里返回 } catch (Exception e) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Exception decoding", e); } exception = e; } ... if (resource == null) { onLoadFailed(exception); } else { onLoadComplete(resource);//然后代碼繼續向下執行會從這里走 } }

我們回到最開始的EngineRunnable的run方法。然后我們知道這里的resource不是null,所以進入onLoadComplete方法。到這里為止,我們就完成了Gif資源的解析過程分析。

從onLoadComplete方法開始就是Gif資源的輪播流程了,由于篇幅有限,在這里就不再涉及,有興趣的同學可以自行分析鍛煉一下。

最后希望同學們可以嘗試使用本方法舉一反三,分析一下其它框架,反復學習,加深印象。


我建了一個QQ群,歡迎對學習有興趣的同學加入。我們可以一起探討、深究、掌握那些我們會用到的技術,讓自己不至于太落伍。 這里寫圖片描述


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
中文字幕亚洲无线码a| 久久久久国产精品一区| 亚洲人成电影网站色| 亚洲精品免费av| 日韩免费电影在线观看| 国产精品91一区| 中文字幕精品一区二区精品| 热99久久精品| 亚洲欧美国产制服动漫| 欧美黄色片免费观看| 91久久久在线| 欧美日韩一区二区三区| 在线精品91av| 久久久精品国产一区二区| 国产欧美在线视频| 精品视频久久久久久| 热re99久久精品国产66热| 久久艹在线视频| 九色精品免费永久在线| 98精品在线视频| 91精品视频大全| 午夜精品在线视频| 97国产精品久久| 国产极品精品在线观看| 欧美精品aaa| 日韩电影在线观看免费| 欧美成人黑人xx视频免费观看| 国产一区二区三区网站| 日韩精品电影网| 日韩精品免费综合视频在线播放| 久久久91精品国产| 欧美精品日韩三级| 亚洲成人xxx| 国产一区二区日韩精品欧美精品| 亚洲精品在线观看www| 亚洲人成网在线播放| 亚洲国产91色在线| 精品国产一区二区三区久久| 午夜欧美大片免费观看| 欧美精品在线观看| 日韩三级成人av网| 午夜剧场成人观在线视频免费观看| 亚洲v日韩v综合v精品v| 欧美日韩在线视频首页| 亚洲国产一区二区三区四区| 国产亚洲a∨片在线观看| 国产免费一区视频观看免费| 91av视频在线观看| 91在线视频一区| 91精品国产自产在线| 亚洲free性xxxx护士白浆| 在线视频欧美日韩| 热久久美女精品天天吊色| 欧美不卡视频一区发布| 九九九久久国产免费| 91精品国产色综合| 66m—66摸成人免费视频| 在线丨暗呦小u女国产精品| 日韩av在线直播| 久久99精品久久久久久青青91| 久久精品免费电影| 欧美丝袜一区二区| 亚洲欧美综合v| 精品国产乱码久久久久久婷婷| 亚洲人永久免费| 国产精品精品国产| 热re91久久精品国99热蜜臀| 夜夜狂射影院欧美极品| 久久精品影视伊人网| 亚洲国产婷婷香蕉久久久久久| 久久中文字幕在线| 亚洲激情免费观看| 国产欧美久久一区二区| 亚洲色图激情小说| 91国产精品91| 欧美亚洲另类视频| 日韩精品丝袜在线| 欧美有码在线视频| 狠狠色香婷婷久久亚洲精品| 青青草99啪国产免费| 亚洲国产成人一区| 久久99国产综合精品女同| 8090成年在线看片午夜| 久久亚洲精品中文字幕冲田杏梨| 中文字幕免费国产精品| 久久6免费高清热精品| 91欧美日韩一区| 亚洲一级黄色片| 欧美一级大片在线免费观看| 久久国产精品久久久久| 亚洲国产另类久久精品| 久久久视频免费观看| 国产欧美一区二区三区在线| 成人天堂噜噜噜| 精品久久久久久中文字幕一区奶水| 欧美激情va永久在线播放| 成人做爰www免费看视频网站| 国产精品麻豆va在线播放| 中文字幕亚洲一区二区三区| 国产精品久久久久久搜索| 国产视频精品xxxx| 国产精品激情自拍| 亚洲精品视频网上网址在线观看| 久久香蕉精品香蕉| 欧美国产日本高清在线| 国产成人一区二区| 成人写真福利网| 亚洲成年人在线| 国产精品福利网站| 国产99久久久欧美黑人| 欧美日韩亚洲系列| 久久青草福利网站| 久久6免费高清热精品| 国产亚洲欧洲高清一区| 国产精品免费一区二区三区都可以| 日本精品免费观看| 国产国产精品人在线视| 日韩视频亚洲视频| 国产精品成人av在线| 日韩精品中文字幕在线播放| 最近更新的2019中文字幕| 岛国av一区二区三区| 欧美激情免费视频| 欧美富婆性猛交| 国产精品直播网红| 一区二区国产精品视频| 精品福利视频导航| 日韩中文字幕国产精品| 欧洲s码亚洲m码精品一区| 欧美亚洲日本黄色| 最近免费中文字幕视频2019| 国产精品高清在线| 国内精品久久久久久中文字幕| 欧美激情2020午夜免费观看| 欧美日韩在线观看视频小说| 国产精品久久久久久久久借妻| 国产成人鲁鲁免费视频a| 国产成人精品在线视频| 精品久久久久久久久久久久| 亚洲美女av在线播放| 欧美日韩色婷婷| 一区二区三区国产在线观看| 午夜精品一区二区三区在线| 91久久精品国产91性色| 欧美性猛交xxxx乱大交极品| 日韩h在线观看| 色综合久久久久久中文网| 57pao成人永久免费视频| 亚洲精品福利资源站| 美女少妇精品视频| 亚洲一区二区三区四区在线播放| 精品性高朝久久久久久久| 中文字幕免费国产精品| 欧美精品福利在线| 国产精品第七十二页| 国产精品 欧美在线| 国产成+人+综合+亚洲欧洲| 久久夜色精品国产欧美乱| 精品日本高清在线播放| 欧美午夜丰满在线18影院| 成人午夜两性视频| 亚洲视频电影图片偷拍一区| 亚洲www视频| 亚洲人成绝费网站色www|