日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

glide缩略图存储 android,Glide 显示视频缩略图及遇到的坑

發(fā)布時(shí)間:2023/12/19 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 glide缩略图存储 android,Glide 显示视频缩略图及遇到的坑 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Glide 顯示視頻縮略圖及遇到的坑

實(shí)現(xiàn)原理

Glide支持視頻格式的文件,但是在3.x里會(huì)有些欠缺。其底層是通過(guò) MediaMetadataRetriever實(shí)現(xiàn)的。

MediaMetadataRetriever擁有獲取視頻的第幾幀的能力,Glide獲取視頻里的第n幀的代碼如下:

VideoBitmapDecoder.java

@Override

public Bitmap decode(ParcelFileDescriptor resource, BitmapPool bitmapPool, int outWidth, int outHeight,

DecodeFormat decodeFormat)

throws IOException {

MediaMetadataRetriever mediaMetadataRetriever = factory.build();

mediaMetadataRetriever.setDataSource(resource.getFileDescriptor());

Bitmap result;

if (frame >= 0) {

result = mediaMetadataRetriever.getFrameAtTime(frame);

} else {

result = mediaMetadataRetriever.getFrameAtTime();

}

mediaMetadataRetriever.release();

resource.close();

return result;

}

提取核心代碼

mediaMetadataRetriever獲取Bitmap的代碼:

val file = FileInputStream(File(path))

val s = MediaMetadataRetriever()

s.setDataSource(file.fd)

file.close()

val bitmap = s.getFrameAtTime(-1)

imageView.setImageBitmap(bitmap)

s.release()

Glide3.x的一個(gè)bug

但3.x的Glide緩存策略不能是Source我們來(lái)分析各種緩存策略Glide的內(nèi)部的邏輯

Result緩存策略

EngineRunnable.java

private Resource> decode() throws Exception {

if (isDecodingFromCache()) {

return decodeFromCache();

} else {

return decodeFromSource();

}

}

EngineRunnable分走兩次,第一次走isDecodingFromCache

private Resource> decodeFromCache() throws Exception {

Resource> result = null;

//...ignore code

//先從result緩存里獲取

result = decodeJob.decodeResultFromCache();

if (result == null) {

//再?gòu)脑次募彺胬铽@取

result = decodeJob.decodeSourceFromCache();

}

return result;

}

第一次加載顯示decodeResultFromCache會(huì)為空,我們略過(guò),重點(diǎn)看decodeSourceFromCache

public ResourcedecodeSourceFromCache() throws Exception {

//如果不是cacheSource直接返回空

if (!diskCacheStrategy.cacheSource()) {

return null;

}

//...ignore code

}

decode失敗以后會(huì)把EngineRunnable 扔給另外一個(gè)線程

private void onLoadFailed(Exception e) {

if (isDecodingFromCache()) {

stage = Stage.SOURCE;

manager.submitForSource(this);

} else {

manager.onException(e);

}

}

因此它會(huì)再一次走到run()方法里的decode,而此次走的是decodeFromSource

DecodeJob.java

public ResourcedecodeFromSource() throws Exception {

Resourcedecoded = decodeSource();

return transformEncodeAndTranscode(decoded);

}

private ResourcedecodeSource() throws Exception {

Resourcedecoded = null;

try {

// 通過(guò)fetcher去加載Source

final A data = fetcher.loadData(priority);

//...ignore code

decoded = decodeFromSourceData(data);

} finally {

fetcher.cleanup();

}

return decoded;

}

DecodeJob.java

private ResourcedecodeFromSourceData(A data) {

final Resourcedecoded;

if (diskCacheStrategy.cacheSource()) {

decoded = cacheAndDecodeSourceData(data);

} else {

//因?yàn)榫彺娌呗允荝esult,所以走的是該處

decoded = loadProvider.getSourceDecoder().decode(data, width, height);

//...ignore code

}

return decoded;

}

SourceDecode其實(shí)就是ImageVideoBitmapDecoder

public Resourcedecode(ImageVideoWrapper source

, int width, int height){

Resourceresult = null;

InputStream is = source.getStream();

if (is != null) {

//通過(guò)streamDecoder去decode圖,它是StreamBitmapDecoder

result = streamDecoder.decode(is, width, height);

}

if (result == null) {

ParcelFileDescriptor fileDescriptor = source.getFileDescriptor();

if (fileDescriptor != null) {

//通過(guò)fileDescriptorDecoder去decode圖,它是FileDescriptorBitmapDecoder

result = fileDescriptorDecoder.decode(fileDescriptor, width, height);

}

}

return result;

}

ImageVideoBitmapDecoder里有兩種decoder

StreamBitmapDecoder直接將輸入流轉(zhuǎn)成Bitmap

FileDescriptorBitmapDecoder能將流通過(guò)VideoBitmapDecoder去轉(zhuǎn)Bitmap

而VideoBitmapDecoder底層就是通過(guò)MediaMetadataRetriever去獲取第一幀

如果源文件是視頻,它將先通過(guò)StreamBitmapDecoder去decode,結(jié)果decode失敗result=null

然后通過(guò)FileDescriptorBitmapDecoder去decode,可見(jiàn)這里Glide內(nèi)部并不知道源文件是圖片還是視頻,所以先用圖片的方式解

而圖片方式解的時(shí)候也是去讀它的頭文件:

@Override

public Resourcedecode(InputStream source, int width, int height) {

//通過(guò)downsampler去decode

Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);

return BitmapResource.obtain(bitmap, bitmapPool);

}

@Override

public Bitmap decode(InputStream is, BitmapPool pool, int outWidth,

int outHeight, DecodeFormat decodeFormat) {

final ByteArrayPool byteArrayPool = ByteArrayPool.get();

final byte[] bytesForOptions = byteArrayPool.getBytes();

final byte[] bytesForStream = byteArrayPool.getBytes();

//此處用了Option池,值得學(xué)習(xí)!

final BitmapFactory.Options options = getDefaultOptions();

//...ignore code

final Bitmap downsampled =

downsampleWithSize(invalidatingStream, bufferedStream,

options, pool, inWidth, inHeight, sampleSize,

decodeFormat);

private static Bitmap decodeStream(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,

BitmapFactory.Options options) {

if (options.inJustDecodeBounds) {

is.mark(MARK_POSITION);

} else {

bufferedStream.fixMarkLimit();

}

//如果是視頻文件此處無(wú)法decode出Bitmap

final Bitmap result = BitmapFactory.decodeStream(is, null, options);

//...ignore code

return result;

}

第一種decode失敗會(huì)用第二種,也就是最終會(huì)走到VideoBitmapDecoder去解析

Source緩存策略

如果緩存策略是Source

private ResourcedecodeSource() throws Exception {

Resourcedecoded = null;

try {

final A data = fetcher.loadData(priority);

//...ignore code

decoded = decodeFromSourceData(data);

} finally {

fetcher.cleanup();

}

return decoded;

}

private ResourcedecodeFromSourceData(A data) {

final Resourcedecoded;

//因?yàn)槭蔷彺鍿ource因?yàn)樽遚acheAndDecodeSourceData方法

if (diskCacheStrategy.cacheSource()) {

decoded = cacheAndDecodeSourceData(data);

} else {

long startTime = LogTime.getLogTime();

decoded = loadProvider.getSourceDecoder().decode(data, width, height);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey("Decoded from source", startTime);

}

}

return decoded;

}

而加載緩存的Decoder是CacheDecoder,非常不幸它不是ImageVideoBitmapDecoder

private ResourceloadFromCache(Key key) {

File cacheFile = diskCacheProvider.getDiskCache().get(key);

if (cacheFile == null) {

return null;

}

Resourceresult = null;

try {

result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);

} finally {

if (result == null) {

diskCacheProvider.getDiskCache().delete(key);

}

}

return result;

}

它是FileToStreamDecoder,以下是截圖

而FileToStreamDecoder的Decoder是StreamBitmapDecoder,最終它是把該文件當(dāng)成圖片解析

因此導(dǎo)致它失敗,如果result==null,它還會(huì)走

diskCacheProvider.getDiskCache().delete(key);

所以如果是視頻流,緩存策略是Source,它會(huì)很讓你失望,不停的copy文件,再解,解失敗再刪除,一直這樣下去

如果該源文件很大,會(huì)影響Glide的性能!

為了驗(yàn)證這一猜想,我們可以通過(guò)抓包工具看一下發(fā)生了什么:

結(jié)果它確實(shí)把這個(gè)視頻存下來(lái)了,不過(guò)發(fā)現(xiàn)decode失敗就會(huì)把它刪除

所以,顯示視頻圖的時(shí)候,一定切記不要用Source緩存策略,它不僅無(wú)法加載成功,而且會(huì)給你帶來(lái)很大隱患

結(jié)束!

總結(jié)

以上是生活随笔為你收集整理的glide缩略图存储 android,Glide 显示视频缩略图及遇到的坑的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。