Fresco是facebook出的图片加载库,功能强大,学习成本比较高。支持基本的图片加载、Gif图片加载、JPEG图片的渐进加载(和电脑浏览器一样)、显示图片加载进度。相对于Glide的主要优点是支持图片渐进加载,显示图片加载进度,在Api19以下通过匿名共享内存缓存图片。本文章主要分析Fresco不同的地方。
1.不同点分析 1.1 图片的加载。 1.1.1 NetworkFetchProducer.java
1 2 3 4 5 6 7 8 9 10 11 @Override public void produceResults (Consumer<EncodedImage> consumer, ProducerContext context) {... mNetworkFetcher.fetch( fetchState, new NetworkFetcher.Callback() { @Override public void onResponse (InputStream response, int responseLength) throws IOException { NetworkFetchProducer.this .onResponse(fetchState, response, responseLength); } }); }
这个是网络加载图片开始步骤,获取服务器图片流。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 private void onResponse ( FetchState fetchState, InputStream responseData, int responseContentLength) throws IOException { final PooledByteBufferOutputStream pooledOutputStream; if (responseContentLength > 0 ) { pooledOutputStream = mPooledByteBufferFactory.newOutputStream(responseContentLength); } else { pooledOutputStream = mPooledByteBufferFactory.newOutputStream(); } final byte [] ioArray = mByteArrayPool.get(READ_SIZE); try { int length; while ((length = responseData.read(ioArray)) >= 0 ) { if (length > 0 ) { pooledOutputStream.write(ioArray, 0 , length); maybeHandleIntermediateResult(pooledOutputStream, fetchState); float progress = calculateProgress(pooledOutputStream.size(), responseContentLength); fetchState.getConsumer().onProgressUpdate(progress); } } mNetworkFetcher.onFetchCompletion(fetchState, pooledOutputStream.size()); handleFinalResult(pooledOutputStream, fetchState); } finally { mByteArrayPool.release(ioArray); pooledOutputStream.close(); } }
(1)创建PooledByteBufferOutputStream缓冲流缓存数据。 (2)向PooledByteBufferOutputStream写入数据,回调加载进度,判断是否需要渐进显示图片。 (3)完成以后显示图片。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 private void maybeHandleIntermediateResult ( PooledByteBufferOutputStream pooledOutputStream, FetchState fetchState) { final long nowMs = SystemClock.uptimeMillis(); if (shouldPropagateIntermediateResults(fetchState) && nowMs - fetchState.getLastIntermediateResultTimeMs() >= TIME_BETWEEN_PARTIAL_RESULTS_MS) { fetchState.setLastIntermediateResultTimeMs(nowMs); fetchState.getListener() .onProducerEvent(fetchState.getId(), PRODUCER_NAME, INTERMEDIATE_RESULT_PRODUCER_EVENT); notifyConsumer(pooledOutputStream, false , fetchState.getConsumer()); } } private void notifyConsumer ( PooledByteBufferOutputStream pooledOutputStream, boolean isFinal, Consumer<EncodedImage> consumer) { CloseableReference<PooledByteBuffer> result = CloseableReference.of(pooledOutputStream.toByteBuffer()); EncodedImage encodedImage = null ; try { encodedImage = new EncodedImage(result); encodedImage.parseMetaData(); consumer.onNewResult(encodedImage, isFinal); } finally { EncodedImage.closeSafely(encodedImage); CloseableReference.closeSafely(result); } }
判断是否需要渐进加载显示图片。
1.2 图片的解析 1.2.1 ImagePipelineFactory.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static PlatformDecoder buildPlatformDecoder ( PoolFactory poolFactory, boolean decodeMemoryFileEnabled, boolean webpSupportEnabled) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { return new ArtDecoder( poolFactory.getBitmapPool(), poolFactory.getFlexByteArrayPoolMaxNumThreads()); } else { if (decodeMemoryFileEnabled && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { return new GingerbreadPurgeableDecoder(webpSupportEnabled); } else { return new KitKatPurgeableDecoder(poolFactory.getFlexByteArrayPool()); } } }
Fresco针对系统的版本做了不同的图片解析方案。
1.2.1 PlatformDecoder.java
1 2 3 4 5 6 7 8 CloseableReference<Bitmap> decodeFromEncodedImage ( final EncodedImage encodedImage, Bitmap.Config bitmapConfig) ; CloseableReference<Bitmap> decodeJPEGFromEncodedImage ( EncodedImage encodedImage, Bitmap.Config bitmapConfig, int length) ;
decodeFromEncodedImage是对完整图片的解析,decodeJPEGFromEncodedImage是解析JPEG渐进中的图片。
1.2.3 GingerbreadPurgeableDecoder.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 protected Bitmap decodeFileDescriptorAsPurgeable ( CloseableReference<PooledByteBuffer> bytesRef, int inputLength, byte [] suffix, BitmapFactory.Options options) { MemoryFile memoryFile = null ; try { memoryFile = copyToMemoryFile(bytesRef, inputLength, suffix); FileDescriptor fd = getMemoryFileDescriptor(memoryFile); Bitmap bitmap; if (mWebpSupportEnabled) { bitmap = sWebpBitmapFactory.decodeFileDescriptor(fd, null , options); } else { bitmap = BitmapFactory.decodeFileDescriptor(fd, null , options); } return Preconditions.checkNotNull(bitmap, "BitmapFactory returned null" ); } catch (IOException e) { throw Throwables.propagate(e); } finally { if (memoryFile != null ) { memoryFile.close(); } } }
针对API 19以下使用了匿名共享内存,减少Java Heap内存。匿名共享内存可查看Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划 。
1.2.4 KitKatPurgeableDecoder.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 protected Bitmap decodeByteArrayAsPurgeable ( CloseableReference<PooledByteBuffer> bytesRef, BitmapFactory.Options options) { final PooledByteBuffer pooledByteBuffer = bytesRef.get(); final int length = pooledByteBuffer.size(); final CloseableReference<byte []> encodedBytesArrayRef = mFlexByteArrayPool.get(length); try { final byte [] encodedBytesArray = encodedBytesArrayRef.get(); pooledByteBuffer.read(0 , encodedBytesArray, 0 , length); Bitmap bitmap = BitmapFactory.decodeByteArray( encodedBytesArray, 0 , length, options); return Preconditions.checkNotNull(bitmap, "BitmapFactory returned null" ); } finally { CloseableReference.closeSafely(encodedBytesArrayRef); } }
API19 这个版本不支持匿名共享内存,Bitmap将在java memory中。这个是注释:
The MemoryFile trick used in GingerbreadPurgeableDecoder does not work in KitKat. Here, weinstead use Java memory to store the encoded images, but make use of a pool to minimizeallocations. We cannot decode from a stream, as that does not support purgeable decodes.
1.2.5 ArtDecoder.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public CloseableReference<Bitmap> decodeFromEncodedImage ( EncodedImage encodedImage, Bitmap.Config bitmapConfig) { final BitmapFactory.Options options = getDecodeOptionsForStream(encodedImage, bitmapConfig); boolean retryOnFail=options.inPreferredConfig != Bitmap.Config.ARGB_8888; try { return decodeStaticImageFromStream(encodedImage.getInputStream(), options); } catch (RuntimeException re) { if (retryOnFail) { return decodeFromEncodedImage(encodedImage, Bitmap.Config.ARGB_8888); } throw re; } }
API21及以上,新增了Options inBitmap属性,这个属性支持Bitmap创建时复用之前无用的Bitmap。
2.缺点分析 1.1 图片处理问题 (1)不支持根据ImageView宽高处理图片 (2)图片压缩不够细腻。对应大图和长图的压缩有问题;没有Transformation的过程,意味不会根据ScaleType处理图片,只有更具simple size 压缩图片。所有的ScaleType都是通过定义Drawable和通过矩阵来实现最终效果,这种方式感觉浪费系统资源。 (3)扩展性不够好,代码复杂,重构和上手成本高。