Flutter加載圖片流程之ImageProvider源碼示例解析
加載網(wǎng)絡圖片
Image.network()
是Flutter提供的一種從網(wǎng)絡上加載圖片的方法,它可以從指定的URL加載圖片,并在加載完成后將其顯示在應用程序中。本節(jié)內(nèi)容,我們從源碼出發(fā),探討下圖片的加載流程。
ImageProvider
ImageProvider
是Flutter中一個抽象類,它定義了一種用于加載圖片的通用接口,可以用于加載本地圖片、網(wǎng)絡圖片等各種類型的圖片。
ImageProvider
類包含兩個核心方法:obtainKey
和loadBuffer
。
resolve
/// Resolves this image provider using the given `configuration`, returning /// an [ImageStream]. /// /// This is the public entry-point of the [ImageProvider] class hierarchy. /// /// Subclasses should implement [obtainKey] and [load], which are used by this /// method. If they need to change the implementation of [ImageStream] used, /// they should override [createStream]. If they need to manage the actual /// resolution of the image, they should override [resolveStreamForKey]. /// /// See the Lifecycle documentation on [ImageProvider] for more information. @nonVirtual ImageStream resolve(ImageConfiguration configuration) { assert(configuration != null); final ImageStream stream = createStream(configuration); // Load the key (potentially asynchronously), set up an error handling zone, // and call resolveStreamForKey. _createErrorHandlerAndKey( configuration, (T key, ImageErrorListener errorHandler) { resolveStreamForKey(configuration, stream, key, errorHandler); }, (T? key, Object exception, StackTrace? stack) async { await null; // wait an event turn in case a listener has been added to the image stream. InformationCollector? collector; assert(() { collector = () => <DiagnosticsNode>[ DiagnosticsProperty<ImageProvider>('Image provider', this), DiagnosticsProperty<ImageConfiguration>('Image configuration', configuration), DiagnosticsProperty<T>('Image key', key, defaultValue: null), ]; return true; }()); if (stream.completer == null) { stream.setCompleter(_ErrorImageCompleter()); } stream.completer!.reportError( exception: exception, stack: stack, context: ErrorDescription('while resolving an image'), silent: true, // could be a network error or whatnot informationCollector: collector, ); }, ); return stream; }
根據(jù)文檔解釋,我們可以了解到以下幾點:
1、使用給定的`configuration`解析該圖片提供器,返回一個 [ImageStream]。
2、這是 [ImageProvider] 類層次結構的公共入口點。
3、子類應該實現(xiàn) [obtainKey] 和 [load] 方法,這兩個方法將被該方法使用。
4、如果子類需要更改使用的 [ImageStream] 的實現(xiàn),則應該重寫 [createStream] 方法。
5、 如果子類需要管理實際的圖像分辨率,則應該重寫 [resolveStreamForKey] 方法。
閱讀resolve
方法的實現(xiàn)。我們可以知道:
1、它使用給定的configuration
參數(shù)創(chuàng)建一個ImageStream
對象(createStream
)。然后調用_createErrorHandlerAndKey
方法,該方法會異步獲取圖片的唯一標識符,并設置一個錯誤處理區(qū)域,以防圖片加載過程中發(fā)生錯誤。
2、如果獲取唯一標識符的過程中出現(xiàn)異常,則會將錯誤信息封裝成一個_ErrorImageCompleter
對象,并將其設置為ImageStream
的completer
屬性,表示圖片加載失敗。
3、如果唯一標識符獲取成功,則會調用resolveStreamForKey
方法來解析圖片,并將圖片數(shù)據(jù)存儲到ImageStream
對象中,供后續(xù)使用。
4、該方法是ImageProvider
類層次結構的公共入口點,因為它是所有圖片提供器的解析方法。子類只需要實現(xiàn)obtainKey
和load
方法來獲取圖片的唯一標識符和加載圖片的數(shù)據(jù),而不需要重寫resolve
方法。
5、如果子類需要更改使用的ImageStream
的實現(xiàn)方式,則可以重寫createStream
方法。如果子類需要管理實際的圖像分辨率,則可以重寫resolveStreamForKey
方法。例如,AssetImage
類中的createStream
方法返回一個AssetBundleImageStreamCompleter
對象,該對象用于從應用程序資源中加載圖片數(shù)據(jù)。而NetworkImage
類中的resolveStreamForKey
方法使用HTTP客戶端從網(wǎng)絡上加載圖片數(shù)據(jù)。
6、這段代碼中還有一些調試信息,例如將圖片提供器、圖片配置和圖片唯一標識符添加到調試信息中,以便在出現(xiàn)錯誤時進行調試。
obtainKey
/// Converts an ImageProvider's settings plus an ImageConfiguration to a key /// that describes the precise image to load. /// /// The type of the key is determined by the subclass. It is a value that /// unambiguously identifies the image (_including its scale_) that the [load] /// method will fetch. Different [ImageProvider]s given the same constructor /// arguments and [ImageConfiguration] objects should return keys that are /// '==' to each other (possibly by using a class for the key that itself /// implements [==]). Future<T> obtainKey(ImageConfiguration configuration);
這段注釋是關于obtainKey
方法的說明。該方法是ImageProvider
的子類應該實現(xiàn)的方法之一,用于將ImageProvider
的設置及ImageConfiguration
轉換為一個可以唯一標識圖片的key
。
不同的ImageProvider
根據(jù)相同的構造函數(shù)參數(shù)和ImageConfiguration
對象應該返回相等的key
,以便于后續(xù)加載和緩存圖片。key
的類型由子類確定,它應該是一個值,可以唯一地標識出要加載的圖片(包括其縮放比例)。
在實現(xiàn)obtainKey
方法時,子類可以考慮使用自定義的類來表示key
,并實現(xiàn)==
方法以保證唯一性。
resolveStreamForKey
@protected void resolveStreamForKey(ImageConfiguration configuration, ImageStream stream, T key, ImageErrorListener handleError) { // This is an unusual edge case where someone has told us that they found // the image we want before getting to this method. We should avoid calling // load again, but still update the image cache with LRU information. if (stream.completer != null) { final ImageStreamCompleter? completer = PaintingBinding.instance.imageCache.putIfAbsent( key, () => stream.completer!, onError: handleError, ); assert(identical(completer, stream.completer)); return; } final ImageStreamCompleter? completer = PaintingBinding.instance.imageCache.putIfAbsent( key, /// 加載 () => loadBuffer(key, PaintingBinding.instance.instantiateImageCodecFromBuffer), onError: handleError, ); if (completer != null) { /// 關鍵是解析并設置ImageStreamCompleter對象 stream.setCompleter(completer); } }
官方文檔解釋:
- 該方法是
ImageProvider
的子類應該實現(xiàn)的方法之一,用于根據(jù)key
來解析圖片。 resolveStreamForKey
方法是由resolve
方法調用的,其參數(shù)包括ImageConfiguration
、ImageStream
、key
和errorHandler
。子類可以通過實現(xiàn)resolveStreamForKey
方法來管理圖片的實際解析過程,同時也可以通過調用errorHandler
來處理解析過程中可能發(fā)生的錯誤。- 實現(xiàn)
resolveStreamForKey
方法時,子類可以考慮使用key
與ImageCache
交互,例如調用ImageCache.putIfAbsent
方法,并向stream
通知監(jiān)聽器。默認實現(xiàn)已經(jīng)使用key
與ImageCache
交互,子類可以選擇調用super.resolveStreamForKey
方法或不調用。
從上面的源碼,我們可以知道以下幾點:
- 1、如果
stream
對象已經(jīng)有了completer
(即已經(jīng)有了可以加載圖片的方式),則將completer
添加到ImageCache
中,實現(xiàn)緩存功能,并直接返回。 - 2、如果
stream
對象還沒有completer
,則調用loadBuffer
方法加載圖片,并將其返回的ImageStreamCompleter
對象添加到ImageCache
中,同時設置到stream
對象的completer
中。 - 3、如果
loadBuffer
方法出現(xiàn)了異常,則會將異常交給onError
回調處理,以便在異常處理時能夠提供詳細的錯誤信息。 - 4、關鍵是解析并設置
ImageStreamCompleter
對象 - 5、
PaintingBinding.instance.imageCache.putIfAbsent
方法在內(nèi)部將ImageStreamListener
對象添加到ImageStreamCompleter
對象的_listeners
數(shù)組中了。
PaintingBinding.instance.imageCache.putIfAbsent( key, () => loadBuffer(key, PaintingBinding.instance.instantiateImageCodecFromBuffer), onError: handleError, )
loadBuffer
/// Converts a key into an [ImageStreamCompleter], and begins fetching the /// image. /// /// For backwards-compatibility the default implementation of this method calls /// through to [ImageProvider.load]. However, implementors of this interface should /// only override this method and not [ImageProvider.load], which is deprecated. /// /// The [decode] callback provides the logic to obtain the codec for the /// image. /// /// See also: /// /// * [ResizeImage], for modifying the key to account for cache dimensions. @protected ImageStreamCompleter loadBuffer(T key, DecoderBufferCallback decode) { return load(key, PaintingBinding.instance.instantiateImageCodec); }
從源碼我們知道, [ImageProvider.load], which is deprecated
被廢棄了。子類只需要重寫loadBuffer
方法即可。
- 這個方法是ImageProvider的一個protected方法,用于從緩存中加載指定的圖片。
- 它接受兩個參數(shù):一個是唯一標識圖片的key,另一個是一個用于解碼圖片數(shù)據(jù)的回調函數(shù)decode。
- 這個方法調用了load方法,然后返回一個ImageStreamCompleter對象,它表示加載過程中的一個數(shù)據(jù)流。
- 在load方法中,使用傳入的decode回調函數(shù)從緩存或網(wǎng)絡中獲取圖片數(shù)據(jù)并解碼,然后將解碼后的圖片數(shù)據(jù)傳遞給ImageStreamCompleter對象,以便它可以生成一個帶有正確圖片數(shù)據(jù)的ImageInfo對象,這個ImageInfo對象可以被傳遞到Image widget中用于顯示圖片。
load(被廢棄)
/// Converts a key into an [ImageStreamCompleter], and begins fetching the /// image. /// /// This method is deprecated. Implement [loadBuffer] for faster image /// loading. Only one of [load] and [loadBuffer] must be implemented, and /// [loadBuffer] is preferred. /// /// The [decode] callback provides the logic to obtain the codec for the /// image. /// /// See also: /// /// * [ResizeImage], for modifying the key to account for cache dimensions. @protected @Deprecated( 'Implement loadBuffer for faster image loading. ' 'This feature was deprecated after v2.13.0-1.0.pre.', ) ImageStreamCompleter load(T key, DecoderCallback decode) { throw UnsupportedError('Implement loadBuffer for faster image loading'); }
從注釋可知:
這個方法被廢棄了,現(xiàn)在已經(jīng)不再建議使用了。如果需要更快的圖像加載,請實現(xiàn) [loadBuffer] 方法。在 [load] 和 [loadBuffer] 方法中只需要實現(xiàn)其中一個,而且 [loadBuffer] 更受推薦。
[decode] 回調提供了獲取圖像編解碼器的邏輯。
evict
/// Evicts an entry from the image cache. /// /// Returns a [Future] which indicates whether the value was successfully /// removed. /// /// The [ImageProvider] used does not need to be the same instance that was /// passed to an [Image] widget, but it does need to create a key which is /// equal to one. /// /// The [cache] is optional and defaults to the global image cache. /// /// The [configuration] is optional and defaults to /// [ImageConfiguration.empty]. /// /// {@tool snippet} /// /// The following sample code shows how an image loaded using the [Image] /// widget can be evicted using a [NetworkImage] with a matching URL. /// /// ```dart /// class MyWidget extends StatelessWidget { /// const MyWidget({ /// super.key, /// this.url = ' ... ', /// }); /// /// final String url; /// /// @override /// Widget build(BuildContext context) { /// return Image.network(url); /// } /// /// void evictImage() { /// final NetworkImage provider = NetworkImage(url); /// provider.evict().then<void>((bool success) { /// if (success) { /// debugPrint('removed image!'); /// } /// }); /// } /// } /// ``` /// {@end-tool} Future<bool> evict({ ImageCache? cache, ImageConfiguration configuration = ImageConfiguration.empty }) async { cache ??= imageCache; final T key = await obtainKey(configuration); return cache.evict(key); }
這是一個名為evict
的異步方法,它的作用是從圖像緩存中刪除給定配置下的圖片。它有兩個可選參數(shù):cache
和configuration
。如果cache
參數(shù)為null,則默認使用全局的imageCache
。configuration
參數(shù)是一個圖像配置,它用于獲取將要從緩存中刪除的圖片的鍵值。這個方法返回一個Future<bool>
對象,表示刪除是否成功。如果緩存中沒有找到要刪除的圖片,則返回false
。
列表快速滑動,內(nèi)存暴增時,可以用這個方法做些事情。
總結
ImageProvider
是Flutter中一個用于提供圖像數(shù)據(jù)的抽象類,它定義了如何從不同的數(shù)據(jù)源(如文件系統(tǒng)、網(wǎng)絡、內(nèi)存)中獲取圖像數(shù)據(jù),并將其轉換成ImageStreamCompleter
對象,以供Image
組件使用。
在Flutter中,使用Image
組件來加載和顯示圖像,需要先提供一個ImageProvider
對象作為其image
屬性的值。ImageProvider
類包含了兩個關鍵的方法:obtainKey
和load
。
obtainKey
方法用于獲取一個用于唯一標識圖像數(shù)據(jù)的ImageProvider
對象,這個對象可以用來緩存圖像數(shù)據(jù),以便在需要重新加載圖像時能夠快速獲取到它。例如,AssetImage
類使用圖片資源的路徑作為其ImageProvider
對象的標識符,以便在需要重新加載該資源時能夠快速地從內(nèi)存或磁盤緩存中獲取到它。
load
方法用于獲取一個ImageStreamCompleter
對象,該對象包含了用于繪制圖像的圖像數(shù)據(jù)。在Flutter 2.5之前,load
方法是一個抽象方法,必須由子類實現(xiàn)。但是從Flutter 2.5開始,load
方法已被廢棄,取而代之的是resolve
方法。resolve
方法接受一個ImageConfiguration
參數(shù),并返回一個Future<ImageStreamCompleter>
對象。它與load
方法的功能類似,都是用于獲取圖像數(shù)據(jù),并將其轉換成ImageStreamCompleter
對象,以供Image
組件使用。
使用ImageProvider
類加載和顯示圖像的流程如下:
- 創(chuàng)建一個
ImageProvider
對象,該對象提供了圖像數(shù)據(jù)的來源和標識符。 - 使用
ImageProvider
對象作為Image
組件的image
屬性的值。 Image
組件會調用obtainKey
方法獲取一個用于唯一標識圖像數(shù)據(jù)的ImageProvider
對象。Image
組件會調用resolve
方法獲取一個Future<ImageStreamCompleter>
對象。- 當圖像數(shù)據(jù)加載完成后,
ImageStreamCompleter
對象會將其通知給Image
組件,Image
組件會將其渲染到屏幕上。
總的來說,ImageProvider
類是Flutter中一個非常重要的類,它提供了一種方便的方式來加載和顯示圖像。雖然load
方法已被廢棄,但是resolve
方法提供了更好的替代方案,可以用于獲取圖像數(shù)據(jù)并將其轉換成ImageStreamCompleter
對象。
參考鏈接
Flutter系統(tǒng)網(wǎng)絡圖片加載流程解析_Android
Flutter | Image 源碼分析與優(yōu)化方式
困惑解答
第一次加載圖片時,stream
對象通常沒有completer
。在第一次調用resolveStreamForKey
時,會將stream
對象的completer
與對應的ImageCache
的ImageStreamCompleter
進行綁定,并且completer
會被設置為ImageStreamCompleter
。
以上就是Flutter加載圖片流程之ImageProvider源碼示例解析的詳細內(nèi)容,更多關于Flutter加載圖片ImageProvider的資料請關注腳本之家其它相關文章!
相關文章
Android開發(fā)手冊Button按鈕實現(xiàn)點擊音效
這篇文章主要為大家介紹了Android開發(fā)手冊Button按鈕實現(xiàn)點擊音效示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06Android App調用MediaRecorder實現(xiàn)錄音功能的實例
這篇文章主要介紹了Android App調用MediaRecorder實現(xiàn)錄音功能的實例,MediaRecorder非常強大,不僅能夠用來錄制音頻還可以錄制視頻,需要的朋友可以參考下2016-04-04Android中SwipeBack實現(xiàn)右滑返回效果
這篇文章主要介紹了Android中SwipeBack實現(xiàn)右滑返回效果的相關資料,需要的朋友可以參考下2016-02-02RecyclerView+PagerSnapHelper實現(xiàn)抖音首頁翻頁的Viewpager效果
這篇文章主要為大家詳細介紹了RecyclerView+PagerSnapHelper實現(xiàn)抖音首頁翻頁的Viewpager效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-10-10Android端權限隱私的合規(guī)化處理實戰(zhàn)記錄
大家應該都發(fā)現(xiàn)了,現(xiàn)在很多應用市場都要求應用上架需要用戶協(xié)議,這篇文章主要給大家介紹了關于Android端權限隱私合規(guī)化處理的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下2021-08-08Android開發(fā)Compose remember原理解析
這篇文章主要為大家介紹了Android開發(fā)Compose remember原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07