摘要:首先这是对的源码分析,所以在看这篇文章之前你应该要有使用的基础,如果没有的强烈推荐看下官方文档。在中统一由来替代。关于后续文章会详细分析。在其内部的,是用来记录事件的传递,方便的调试。这次主要是分析了中的基本组件与它的子类。
在Android中图片加载的框架很多,例如:Fresco、Picasso、Glide与Imageloader。它们都有各自的优点,但总的来说,使用起来方便简单、可配置性高与提供良好的缓存机制。由于平常主要用的还是Fresco,所以这里有必要对Fresco的原理进行深入研究。这样对于以后的使用与理解将会得到巨大的帮助。
Fresco是专注于对图片加载而设计的框架,所以对于以图片为主的App强烈推荐使用。Fresco对于图片的展示支持多种情况:backgroud image(背景图)、placeholder image(占位图)、actual image(加载的图片)、progress bar image(进度条)、retry image(重新加载的图片)、failure image(失败图片)与overlay image(叠加图)。Fresco既然支持这么多图片展示情况,那么它对这次图层的管理模式又是怎么样的呢?Fresco对于这些图层的管理都交给了Hierarchy,而这些图层的数据都通过Controller来设置。先不分析这些,这些后续文章会详细分析,今天先从Fresco的基本组件开始。
SimpleDraweeView首先这是对Fresco的源码分析,所以在看这篇文章之前你应该要有使用Fresco的基础,如果没有的强烈推荐看下Fresco官方文档。
我们使用Fresco进行图片加载,使用最多的还是已经封装好的SimpleDraweeView,而在SimpleDraweeView的构造方法中会调用init()方法,它的源码如下:
private void init(Context context, @Nullable AttributeSet attrs) { if (isInEditMode()) { return; } Preconditions.checkNotNull( sDraweeControllerBuilderSupplier, "SimpleDraweeView was not initialized!"); mSimpleDraweeControllerBuilder = sDraweeControllerBuilderSupplier.get(); if (attrs != null) { TypedArray gdhAttrs = context.obtainStyledAttributes( attrs, R.styleable.SimpleDraweeView); try { if (gdhAttrs.hasValue(R.styleable.SimpleDraweeView_actualImageUri)) { setImageURI( Uri.parse(gdhAttrs.getString(R.styleable.SimpleDraweeView_actualImageUri)), null); } else if (gdhAttrs.hasValue((R.styleable.SimpleDraweeView_actualImageResource))) { int resId = gdhAttrs.getResourceId( R.styleable.SimpleDraweeView_actualImageResource, NO_ID); if (resId != NO_ID) { setActualImageResource(resId); } } } finally { gdhAttrs.recycle(); } } }
这个方法做的事情很简单,但我们要注意的是它会对sDraweeControllerBuilderSupplier进行null判断,如果为null将会抛出异常。sDraweeControllerBuilderSupplier 是供应类,通过它的get方法来获取DraweeControllerBuilder,这个是controller构造器,这个以后的章节会详细说明。空判断的目的就是在使用SimpleDraweeView之前必须初始化sDraweeControllerBuilderSupplier。在SimpleDraweeView中我们能找到它的初始化方法
public static void initialize( Supplier extends SimpleDraweeControllerBuilder> draweeControllerBuilderSupplier) { sDraweeControllerBuilderSupplier = draweeControllerBuilderSupplier; }
它是一个static方法,在SimpleDraweeView初始化之前加载一次即可。这是SimpleDraweeView的关键。它还有一个关键方法
public void setImageURI(Uri uri, @Nullable Object callerContext) { //通过controller 来保存Uri 等相关信息 DraweeController controller = mSimpleDraweeControllerBuilder .setCallerContext(callerContext) .setUri(uri) .setOldController(getController()) .build(); setController(controller); }
使用mSimpleDraweeControllerBuilder来构建一个Controller,而Controller是由build模式所创建,这里我们能看到uri也交由Controller管理。其实最终uri会封装成一个ImageRequest,Controller真正持有的是uri的封装体ImageRequest。在SimpleDraweeView中它会重写setImageURI方法,最终也就是将ImageView中的原生方法给覆盖掉。还有其它的类似的setImageResource与setImageBitmap等,Fresco都在这些方法上加了@Deprecated,意思就是说不推荐使用,如果使用的话就跟直接使用ImageView没什么区别,这就无法体验到Fresco的强大的特性。在Fresco中统一由setController来替代。
关于Controller后续文章会详细分析。Fresco
如果我们根据Fresco官方文档的正常步骤来使用的话就无需担心这一步,因为在我们在使用Fresco之前都要先调用Fresco.initialize(context)
public static void initialize( Context context, @Nullable ImagePipelineConfig imagePipelineConfig, @Nullable DraweeConfig draweeConfig) { if (sIsInitialized) { FLog.w( TAG, "Fresco has already been initialized! `Fresco.initialize(...)` should only be called " + "1 single time to avoid memory leaks!"); } else { sIsInitialized = true; } // we should always use the application context to avoid memory leaks context = context.getApplicationContext(); if (imagePipelineConfig == null) { //初始化ImagePipeline工厂,包含ImagePipelineConfig 相关初始化配置信息 // (三级缓存、图片解码/编码、转化、渐变、bitmap配置、四种executor 分别为 io、decode、background、lightweight background)等 ImagePipelineFactory.initialize(context); } else { ImagePipelineFactory.initialize(imagePipelineConfig); } //初始化Drawee相关配置信息 initializeDrawee(context, draweeConfig); }
除了初始化ImagePipeline之外,最后还会调用initializeDrawee (context, draweeConfig),我们来看下initializeDrawee做了什么
private static void initializeDrawee( Context context, @Nullable DraweeConfig draweeConfig) { //构建PipelineDraweeControllerBuilderSupplier, //其中ImagePipeline、PipelineDraweeControllerFactory、ControllerListener set集合 sDraweeControllerBuilderSupplier = new PipelineDraweeControllerBuilderSupplier(context, draweeConfig); //初始化SimpleDrawee //初始化时通过调用PipelineDraweeControllerBuilderSupplier实现的Supplier的get()方法 // 返回配置信息的封装体PipelineDraweeControllerBuilder implements SimpleDraweeControllerBuilder SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier); }
在这里我们会看到之前所提到的sDraweeControllerBuilderSupplier与SimpleDraweeView中必须优先调用的initialize方法。相信现在应该明白的为什么在使用Fresco之前必须调用它的initialize方法了。因为它必须要初始化一些必要的配置信息,其中就包括使用的控件SimpleDraweeView的配置信息。
GenericDraweeView上面所说的SimpleDraweeView的父类是GenericDraweeView,它做的事情很简单,处理xml相关的属性。它会通过inflateHierarchy方法进行初始化。
protected void inflateHierarchy(Context context, @Nullable AttributeSet attrs) { GenericDraweeHierarchyBuilder builder = GenericDraweeHierarchyInflater.inflateBuilder(context, attrs); setAspectRatio(builder.getDesiredAspectRatio()); setHierarchy(builder.build()); }
它是由GenericDraweeHierarchyBuilder来统一封装这些属性。最终通过build方法来构建GenericDraweeHierarchy,这就是Fresco的图层。然后通过setHierarchy将图层传递给DraweeHolder。DraweeHolder是用来管理Hierarchy与Controller的。而DraweeHolder是在最底层的DraweeView中,这也是GenericDraweeView的父类。下面我们进入DraweeView ,来看看它到底做了什么。
DraweeViewDraweeView是Fresco最底层的控件,也是我们使用它展示图片的基础,它继承于ImageView,所以它能做的事也无非于在原生ImageView上做扩展或者方法重写,从而来实现自己的一套逻辑。先看下它的构造方法
public DraweeView(Context context) { super(context); init(context); }
没什么特别的逻辑,就一个init方法,那么就进入init看看
/** This method is idempotent so it only has effect the first time it"s called */ private void init(Context context) { if (mInitialised) { return; } mInitialised = true; mDraweeHolder = DraweeHolder.create(null, context); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { ColorStateList imageTintList = getImageTintList(); if (imageTintList == null) { return; } setColorFilter(imageTintList.getDefaultColor()); } // In Android N and above, visibility handling for Drawables has been changed, which breaks // activity transitions with DraweeViews. mLegacyVisibilityHandlingEnabled = sGlobalLegacyVisibilityHandlingEnabled && context.getApplicationInfo().targetSdkVersion >= 24; //Build.VERSION_CODES.N }
我们可以看到它会通过mInitialised来判断是否需要初始化,源码注释也说明的该方法只会调用一次。这是因为创建Hierarchy的代价太大,所以只会创建一次,以后都会使用同一个mDraweeHolder中的Hierarchy,所以会看到这里就必须初始化一个mDraweeHolder。在DraweeView中还有以下几个主要方法:
void setHierarchy(DH hierarchy)设置Hierarchy,同时会将Hierarchy交由mDraweeHolder管理,最后还会调用super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());来将Hierarchy中的图层树显示出来。
Drawable getTopLevelDrawable()会通过mDraweeHolder中的Hierarchy来获取图层树。
void setController(@Nullable DraweeController draweeController)设置Controller,同时也会将Hierarchy中的图层树显示出来。
void onAttachedToWindow()、void onDetachedFromWindow()、void onStartTemporaryDetach()与void onFinishTemporaryDetach()是来控制图层的显示与隐藏、绑定与解绑的回调函数,它们都分别会调用mDraweeHolder的onAttach()与onDetach()。其实最终调用的还是Controller中的onAttach()与onDetach()。
boolean onTouchEvent(MotionEvent event)控制控件的触摸,如果Controller有效的话会调用Controller中的onTouchEvent。
void setAspectRatio(float aspectRatio)用来设置DraweeView显示的宽高比例。
setImageDrawable、setImageBitmap、setImageResource与setImageURI这些方法都是原生ImageView的方法,但在DraweeView中这些方法都被加上了@Deprecated标记。标明不推荐使用,如果一定使用的话,那么DraweeView将会退化成一个普通的ImageView。因为在DraweeView中都是通过Controller来体现它的缓存、加载机制等特性。
上面这些就是DraweeView的主要涉及到的方法与特性,不过在DraweeView中基本上每一个方法都涉及到了DraweeHolder,那它到底是干什么的呢?别急下面就轮到它了。
DraweeHolderA holder class for Drawee controller and hierarchy.
上面的是官方注释,说明DraweeHolder是用来管理Hierarchy与Controller的,同时也是它们之间的联系的桥梁。DraweeView以及它的子类都是通过它来间接操作Controller与Hierarchy。
public staticDraweeHolder create( @Nullable DH hierarchy, Context context) { DraweeHolder holder = new DraweeHolder (hierarchy); holder.registerWithContext(context); return holder; }
它是通过公有的静态方法来创建自身实例的。在上面的DraweeView的init方法中会调用。在其内部的DraweeEventTracker,是用来记录事件的传递,方便dubug的调试。如果不需要的话,可以在Fresco.initialize()之前调用DraweeEventTracker.disable()。那么剩下的方法其实基本上在DraweeView中都说过。
onAttach()与onDetach(),都会调用attachOrDetachController(),根据情况分别调用attachController()与detachController(),最终调用的就是Controller的onAttach()与onDetach()
Drawable getTopLevelDrawable()调用mHierarchy.getTopLevelDrawable()获取图层树。
void setController(@Nullable DraweeController draweeController)设置Controller,在设置之前会先判断是否已经wasAttached,如果是的话就先调用detachController(),然后清除老的Controller,再将Hierarchy设置到新的Controller中。最后再attachController()进行绑定显示图层。
void setHierarchy(DH hierarchy)设置Hierarchy,如果Controller有效的话就与Hierarchy建立链接,将Hierarchy设置到Controller中。
以上就是DraweeHolder的主要方法,都跟Controller与Hierarchy相关。而DraweeHolder又与DraweeView相连,所以最终还是要回到Controller与Hierarchy中。
End这次主要是分析了Fresco中的基本组件DraweeView与它的子类。如果你还想进一步了解Hierarchy与Controller的原理,下篇文章将会详细分析相关的原理,敬请期待!
Fresco源码分析系列Github地址
关注 RecommendFresco源码分析之Hierarchy
Android共享动画兼容实现
Kotlin最佳实践
RecyclerView下拉刷新与上拉更多
Android高仿微信之mvp实现(四)
tensorflow-梯度下降,有这一篇就足够了
博客
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/70713.html
摘要:最终的显隐操作都会转化为在方法中进行操作。主要是通过与数组来控制数组中各个的值,即显隐它继承于,顾名思义通过矩阵来改变状态。 上篇文章我们分析了Fresco中的DraweeView,对其中的一些原理以及方法进行了解析。在这过程中我们了解到,DraweeView中是通过DraweeHolder来统一管理的。而DraweeHolder又是用来统一管理相关的Hierarchy与Control...
摘要:中设计有一个叫做模块,它会在图片加载完成前显示占位图,加载成功后自动替换为目标图片。当图片不再显示在屏幕上时,它会及时地释放内存和空间占用。大的内存占用势必引发更加频繁的。 Fresco图片框架简介及使用 Fresco是FaceBook退出了一个Android开源图片管理框架,它提供了图片下载、渐进式加载、内存管理等功能,很大程度上把程序员从繁琐的图片管理工作中解放了出来,官网地址,F...
阅读 793·2023-04-25 20:18
阅读 2072·2021-11-22 13:54
阅读 2497·2021-09-26 09:55
阅读 3797·2021-09-22 15:28
阅读 2934·2021-09-03 10:34
阅读 1680·2021-07-28 00:15
阅读 1527·2019-08-30 14:25
阅读 1255·2019-08-29 17:16