您好,登錄后才能下訂單哦!
本篇內容主要講解“Flutter加載圖片流程之ImageProvider源碼分析”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Flutter加載圖片流程之ImageProvider源碼分析”吧!
Image.network()
是Flutter提供的一種從網絡上加載圖片的方法,它可以從指定的URL加載圖片,并在加載完成后將其顯示在應用程序中。
ImageProvider
是Flutter中一個抽象類,它定義了一種用于加載圖片的通用接口,可以用于加載本地圖片、網絡圖片等各種類型的圖片。
ImageProvider
類包含兩個核心方法:obtainKey
和loadBuffer
。
/// 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; }
根據文檔解釋,我們可以了解到以下幾點:
1、使用給定的`configuration`解析該圖片提供器,返回一個 [ImageStream]。
2、這是 [ImageProvider] 類層次結構的公共入口點。
3、子類應該實現 [obtainKey] 和 [load] 方法,這兩個方法將被該方法使用。
4、如果子類需要更改使用的 [ImageStream] 的實現,則應該重寫 [createStream] 方法。
5、 如果子類需要管理實際的圖像分辨率,則應該重寫 [resolveStreamForKey] 方法。
閱讀resolve
方法的實現。我們可以知道:
1、它使用給定的configuration
參數創建一個ImageStream
對象(createStream
)。然后調用_createErrorHandlerAndKey
方法,該方法會異步獲取圖片的唯一標識符,并設置一個錯誤處理區域,以防圖片加載過程中發生錯誤。
2、如果獲取唯一標識符的過程中出現異常,則會將錯誤信息封裝成一個_ErrorImageCompleter
對象,并將其設置為ImageStream
的completer
屬性,表示圖片加載失敗。
3、如果唯一標識符獲取成功,則會調用resolveStreamForKey
方法來解析圖片,并將圖片數據存儲到ImageStream
對象中,供后續使用。
4、該方法是ImageProvider
類層次結構的公共入口點,因為它是所有圖片提供器的解析方法。子類只需要實現obtainKey
和load
方法來獲取圖片的唯一標識符和加載圖片的數據,而不需要重寫resolve
方法。
5、如果子類需要更改使用的ImageStream
的實現方式,則可以重寫createStream
方法。如果子類需要管理實際的圖像分辨率,則可以重寫resolveStreamForKey
方法。例如,AssetImage
類中的createStream
方法返回一個AssetBundleImageStreamCompleter
對象,該對象用于從應用程序資源中加載圖片數據。而NetworkImage
類中的resolveStreamForKey
方法使用HTTP客戶端從網絡上加載圖片數據。
6、這段代碼中還有一些調試信息,例如將圖片提供器、圖片配置和圖片唯一標識符添加到調試信息中,以便在出現錯誤時進行調試。
/// 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
的子類應該實現的方法之一,用于將ImageProvider
的設置及ImageConfiguration
轉換為一個可以唯一標識圖片的key
。
不同的ImageProvider
根據相同的構造函數參數和ImageConfiguration
對象應該返回相等的key
,以便于后續加載和緩存圖片。key
的類型由子類確定,它應該是一個值,可以唯一地標識出要加載的圖片(包括其縮放比例)。
在實現obtainKey
方法時,子類可以考慮使用自定義的類來表示key
,并實現==
方法以保證唯一性。
@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
的子類應該實現的方法之一,用于根據key
來解析圖片。
resolveStreamForKey
方法是由resolve
方法調用的,其參數包括ImageConfiguration
、ImageStream
、key
和errorHandler
。子類可以通過實現resolveStreamForKey
方法來管理圖片的實際解析過程,同時也可以通過調用errorHandler
來處理解析過程中可能發生的錯誤。
實現resolveStreamForKey
方法時,子類可以考慮使用key
與ImageCache
交互,例如調用ImageCache.putIfAbsent
方法,并向stream
通知監聽器。默認實現已經使用key
與ImageCache
交互,子類可以選擇調用super.resolveStreamForKey
方法或不調用。
從上面的源碼,我們可以知道以下幾點:
1、如果 stream
對象已經有了 completer
(即已經有了可以加載圖片的方式),則將 completer
添加到 ImageCache
中,實現緩存功能,并直接返回。
2、如果 stream
對象還沒有 completer
,則調用 loadBuffer
方法加載圖片,并將其返回的 ImageStreamCompleter
對象添加到 ImageCache
中,同時設置到 stream
對象的 completer
中。
3、如果 loadBuffer
方法出現了異常,則會將異常交給 onError
回調處理,以便在異常處理時能夠提供詳細的錯誤信息。
4、關鍵是解析并設置ImageStreamCompleter
對象
5、PaintingBinding.instance.imageCache.putIfAbsent
方法在內部將ImageStreamListener
對象添加到ImageStreamCompleter
對象的_listeners
數組中了。
PaintingBinding.instance.imageCache.putIfAbsent( key, () => loadBuffer(key, PaintingBinding.instance.instantiateImageCodecFromBuffer), onError: handleError, )
/// 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方法,用于從緩存中加載指定的圖片。
它接受兩個參數:一個是唯一標識圖片的key,另一個是一個用于解碼圖片數據的回調函數decode。
這個方法調用了load方法,然后返回一個ImageStreamCompleter對象,它表示加載過程中的一個數據流。
在load方法中,使用傳入的decode回調函數從緩存或網絡中獲取圖片數據并解碼,然后將解碼后的圖片數據傳遞給ImageStreamCompleter對象,以便它可以生成一個帶有正確圖片數據的ImageInfo對象,這個ImageInfo對象可以被傳遞到Image widget中用于顯示圖片。
/// 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'); }
從注釋可知:
這個方法被廢棄了,現在已經不再建議使用了。如果需要更快的圖像加載,請實現 [loadBuffer] 方法。在 [load] 和 [loadBuffer] 方法中只需要實現其中一個,而且 [loadBuffer] 更受推薦。
[decode] 回調提供了獲取圖像編解碼器的邏輯。
/// 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
的異步方法,它的作用是從圖像緩存中刪除給定配置下的圖片。它有兩個可選參數:cache
和configuration
。如果cache
參數為null,則默認使用全局的imageCache
。configuration
參數是一個圖像配置,它用于獲取將要從緩存中刪除的圖片的鍵值。這個方法返回一個Future<bool>
對象,表示刪除是否成功。如果緩存中沒有找到要刪除的圖片,則返回false
。
列表快速滑動,內存暴增時,可以用這個方法做些事情。
第一次加載圖片時,stream
對象通常沒有completer
。在第一次調用resolveStreamForKey
時,會將stream
對象的completer
與對應的ImageCache
的ImageStreamCompleter
進行綁定,并且completer
會被設置為ImageStreamCompleter
。
到此,相信大家對“Flutter加載圖片流程之ImageProvider源碼分析”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。