Fresco 其实是我司长期以来默认使用的图片加载框架,这个情况在 4 月份我在做新项目的技术选型的时候被打破了,那时我针对 Glide vs Fresco 的性能做了对比,在加载 GIF 的情况下,Glide + GifDrawable 三方库在内存和 CPU 上要优于 Fresco,所以领导就拍板决定在新版项目里使用 Glide, 但这个情况在后面发生了一些变化。
背景
我在最开始开发的时候其实是想过在Android 4.4
上使用 Fresco,Android 5.0
及以上使用 Glide,在代码层面也做了图片加载框架的封装,只不过领导认为太过麻烦给否决了。
后期在准对机顶盒和投影仪这些设备做版本开发时,发现内存使用没有很高就会出现内存波动,再加上我对 Fresco 持保留态度,知道 Fresco 在加载图片的时候会有效的利用匿名内存
,会在一定程度上提高内存的使用率,所以就决定继续完善之前的图片加载框架的封装。
代码封装
因为 Fresco 是使用SimpleDraweeView
这个自定义 View 来加载图片的,所以为了能够同时兼容 ImageView,我们需要先封装一个自定义 ViewGroup来同时承载ImageView
或者 Fresco 的SimpleDraweeView
。
首先使用一个接口来承担对外的共同操作,这样可以对模块外隐藏内部实现细节。
1 2 3 4
| public interface IMarketView {
... }
|
然后依据不同场景来实现不同的自定义ViewGroup
,这里拿其中一个来举例:
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public class MarketIconView extends FrameLayout implements IMarketView {
...
public MarketIconView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttrs(context, attrs); }
...
private void initAttrs(Context context, AttributeSet attrs) { ... if (ImageLoaderUtil.shouldUseGlideEngine()) { addView(buildShapeableImageView(), new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); } else { addView(buildFrescoImageView(), new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); } ... }
private ImageView buildShapeableImageView() { ShapeableImageView normalImageView = new ShapeableImageView(getContext()); normalImageView.setShapeAppearanceModel(normalImageView.getShapeAppearanceModel() .toBuilder().setAllCorners(CornerFamily.ROUNDED, mCornerSize) .build()); normalImageView.setStrokeColor(ColorStateList.valueOf(Color.TRANSPARENT)); normalImageView.setScaleType(ImageView.ScaleType.FIT_XY); return normalImageView; }
private SimpleDraweeView buildFrescoImageView() { SimpleDraweeView frescoImageView = new SimpleDraweeView(getContext()); frescoImageView.setScaleType(ImageView.ScaleType.FIT_XY); RoundingParams roundingParams = RoundingParams.fromCornersRadius(mCornerSize); frescoImageView.setHierarchy(new GenericDraweeHierarchyBuilder(getResources()) .setRoundingParams(roundingParams) .setActualImageScaleType(ScalingUtils.ScaleType.FIT_XY) .setFadeDuration(0) .build()); return frescoImageView; } }
|
然后就是修改之前定义的ImageLoader
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public interface ImageLoader {
void init(@NonNull Context context);
void loadImageWithOptions(@NonNull IMarketView viewParent, @NonNull String url, LoadOptions options);
void loadImageWithOptions(Fragment fragment, @NonNull IMarketView viewParent, @NonNull String url, LoadOptions options);
void loadImageWithOptions(Fragment fragment, @NonNull IMarketView viewParent, @NonNull String url, LoadOptions options, OnImageLoadListener loadListener);
void clearImageLoad(Fragment fragment, @NonNull IMarketView viewParent);
void resumeRequests(Fragment fragment);
void pauseRequests(Fragment fragment);
void onTrimMemory(Context context, int level);
void clearMemoryCache(Context context);
void preloadImage(@NonNull String url); }
|
这里使用了LoadOptions
来封装一些加载图片时的配置,这样在不同的场景下,可以对LoadOptions
进行配置,从而达到不同的效果,这里就不再赘述了。
后面就是对ImageLoader
的实现,我们基本上在GlideImageLoader
和FrescoImageLoader
里实现了这个接口。
最后再把 xml 里的<ImageView>
换成IMarketView
对应的实现即可。
总结
其实这样封装是带来了一些便利性,但是也为每一个要加载图片的 View 套上一层 FrameLayout,这样就会有一定的性能损耗,但是这个损耗可以被Glide
和Fresco
所抵消,所以这个是个值得的尝试。