glide缩略图存储 android,Glide 显示视频缩略图及遇到的坑
Glide 顯示視頻縮略圖及遇到的坑
實現(xiàn)原理
Glide支持視頻格式的文件,但是在3.x里會有些欠缺。其底層是通過 MediaMetadataRetriever實現(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的一個bug
但3.x的Glide緩存策略不能是Source我們來分析各種緩存策略Glide的內部的邏輯
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) {
//再從源文件緩存里獲取
result = decodeJob.decodeSourceFromCache();
}
return result;
}
第一次加載顯示decodeResultFromCache會為空,我們略過,重點看decodeSourceFromCache
public ResourcedecodeSourceFromCache() throws Exception {
//如果不是cacheSource直接返回空
if (!diskCacheStrategy.cacheSource()) {
return null;
}
//...ignore code
}
decode失敗以后會把EngineRunnable 扔給另外一個線程
private void onLoadFailed(Exception e) {
if (isDecodingFromCache()) {
stage = Stage.SOURCE;
manager.submitForSource(this);
} else {
manager.onException(e);
}
}
因此它會再一次走到run()方法里的decode,而此次走的是decodeFromSource
DecodeJob.java
public ResourcedecodeFromSource() throws Exception {
Resourcedecoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
private ResourcedecodeSource() throws Exception {
Resourcedecoded = null;
try {
// 通過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 {
//因為緩存策略是Result,所以走的是該處
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
//...ignore code
}
return decoded;
}
SourceDecode其實就是ImageVideoBitmapDecoder
public Resourcedecode(ImageVideoWrapper source
, int width, int height){
Resourceresult = null;
InputStream is = source.getStream();
if (is != null) {
//通過streamDecoder去decode圖,它是StreamBitmapDecoder
result = streamDecoder.decode(is, width, height);
}
if (result == null) {
ParcelFileDescriptor fileDescriptor = source.getFileDescriptor();
if (fileDescriptor != null) {
//通過fileDescriptorDecoder去decode圖,它是FileDescriptorBitmapDecoder
result = fileDescriptorDecoder.decode(fileDescriptor, width, height);
}
}
return result;
}
ImageVideoBitmapDecoder里有兩種decoder
StreamBitmapDecoder直接將輸入流轉成Bitmap
FileDescriptorBitmapDecoder能將流通過VideoBitmapDecoder去轉Bitmap
而VideoBitmapDecoder底層就是通過MediaMetadataRetriever去獲取第一幀
如果源文件是視頻,它將先通過StreamBitmapDecoder去decode,結果decode失敗result=null
然后通過FileDescriptorBitmapDecoder去decode,可見這里Glide內部并不知道源文件是圖片還是視頻,所以先用圖片的方式解
而圖片方式解的時候也是去讀它的頭文件:
@Override
public Resourcedecode(InputStream source, int width, int height) {
//通過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池,值得學習!
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();
}
//如果是視頻文件此處無法decode出Bitmap
final Bitmap result = BitmapFactory.decodeStream(is, null, options);
//...ignore code
return result;
}
第一種decode失敗會用第二種,也就是最終會走到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;
//因為是緩存Source因為走cacheAndDecodeSourceData方法
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,最終它是把該文件當成圖片解析
因此導致它失敗,如果result==null,它還會走
diskCacheProvider.getDiskCache().delete(key);
所以如果是視頻流,緩存策略是Source,它會很讓你失望,不停的copy文件,再解,解失敗再刪除,一直這樣下去
如果該源文件很大,會影響Glide的性能!
為了驗證這一猜想,我們可以通過抓包工具看一下發(fā)生了什么:
結果它確實把這個視頻存下來了,不過發(fā)現(xiàn)decode失敗就會把它刪除
所以,顯示視頻圖的時候,一定切記不要用Source緩存策略,它不僅無法加載成功,而且會給你帶來很大隱患
結束!
總結
以上是生活随笔為你收集整理的glide缩略图存储 android,Glide 显示视频缩略图及遇到的坑的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android无需权限保存文件,即使使用
- 下一篇: Flutter学习笔记(15)--Mat