摘要:首先這是對的源碼分析,所以在看這篇文章之前你應該要有使用的基礎,如果沒有的強烈推薦看下官方文檔。在中統一由來替代。關于后續文章會詳細分析。在其內部的,是用來記錄事件的傳遞,方便的調試。這次主要是分析了中的基本組件與它的子類。
在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-梯度下降,有這一篇就足夠了
博客
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70713.html
摘要:最終的顯隱操作都會轉化為在方法中進行操作。主要是通過與數組來控制數組中各個的值,即顯隱它繼承于,顧名思義通過矩陣來改變狀態。 上篇文章我們分析了Fresco中的DraweeView,對其中的一些原理以及方法進行了解析。在這過程中我們了解到,DraweeView中是通過DraweeHolder來統一管理的。而DraweeHolder又是用來統一管理相關的Hierarchy與Control...
摘要:中設計有一個叫做模塊,它會在圖片加載完成前顯示占位圖,加載成功后自動替換為目標圖片。當圖片不再顯示在屏幕上時,它會及時地釋放內存和空間占用。大的內存占用勢必引發更加頻繁的。 Fresco圖片框架簡介及使用 Fresco是FaceBook退出了一個Android開源圖片管理框架,它提供了圖片下載、漸進式加載、內存管理等功能,很大程度上把程序員從繁瑣的圖片管理工作中解放了出來,官網地址,F...
閱讀 811·2023-04-25 20:18
閱讀 2092·2021-11-22 13:54
閱讀 2527·2021-09-26 09:55
閱讀 3857·2021-09-22 15:28
閱讀 2969·2021-09-03 10:34
閱讀 1710·2021-07-28 00:15
閱讀 1629·2019-08-30 14:25
閱讀 1281·2019-08-29 17:16