flutter圖片本地持久化

由于項(xiàng)目中的頭像圖片加載極慢,需要對(duì)此進(jìn)行優(yōu)化。主要針對(duì)以下兩點(diǎn)做優(yōu)化:

  1. 由于后端沒有對(duì)圖片進(jìn)行壓縮,前端對(duì)上傳的圖片提前進(jìn)行裁剪壓縮上傳。
  2. 對(duì)未改變的圖片進(jìn)行本地持久化,減少二次加載圖片的延遲。

圖片的裁剪壓縮直接采用了第三方插件,這里不再過多贅述,主要了解一下flutter的圖片加載及緩存模式。
在flutter中ImageProvider作為一個(gè)抽象類,他除了定義了圖片數(shù)據(jù)獲取和加載的相關(guān)接口,還提供了圖片數(shù)據(jù)源及緩存圖片的作用。渲染一張圖片:

  1. 先判斷圖片數(shù)據(jù)有沒有緩存,如果有,則直接返回ImageStream。
  2. 如果沒有緩存,則調(diào)用load(T key)方法從數(shù)據(jù)源加載圖片數(shù)據(jù),加載成功后先緩存,然后返回ImageStream。

查看圖片是否緩存及生成緩存數(shù)據(jù)都與緩存key相關(guān),現(xiàn)在我們看一下緩存的key,因?yàn)镸ap中相同key的值會(huì)被覆蓋,也就是說key是圖片緩存的一個(gè)唯一標(biāo)識(shí),只要是不同key,那么圖片數(shù)據(jù)就會(huì)分別緩存。圖片緩存key是ImageProvider.obtainKey()方法的返回值,而此方法需要ImageProvider子類去重寫。接下來看一下NetworkImage為的obtainKey()的具體實(shí)現(xiàn):

@override
Future<NetworkImage> obtainKey(image_provider.ImageConfiguration configuration) {
  return SynchronousFuture<NetworkImage>(this);
}

代碼創(chuàng)建了一個(gè)同步的future,然后直接將自身做為key返回。因?yàn)镸ap中在判斷key(此時(shí)是NetworkImage對(duì)象)是否相等時(shí)會(huì)使用“==”運(yùn)算符,那么定義key的邏輯就是NetworkImage的“==”運(yùn)算符:

@override
bool operator ==(dynamic other) {
  ... //省略無關(guān)代碼
  final NetworkImage typedOther = other;
  return url == typedOther.url
      && scale == typedOther.scale;
}

所以對(duì)于網(wǎng)絡(luò)圖片來說,會(huì)將其“url+縮放比例”作為緩存的key。也就是說如果兩張圖片的url或scale只要有一個(gè)不同,便會(huì)重新下載并分別緩存。

另外,圖片緩存是在內(nèi)存中,并沒有進(jìn)行本地文件持久化存儲(chǔ),所以網(wǎng)絡(luò)圖片在應(yīng)用重啟后需要重新聯(lián)網(wǎng)下載的原因。雖然Flutter中的ImageProvider本身是帶有內(nèi)存緩存機(jī)制的,但由于項(xiàng)目圖片存儲(chǔ)于云端,且每次圖片地址url都會(huì)有所變化,導(dǎo)致NetworkImage自帶的緩存機(jī)制也無效。

基于以上原因,最終決定將圖片以二進(jìn)制形式請(qǐng)求到本地,并以圖片主路徑(除其它附帶參數(shù))作為存儲(chǔ)key保存,若key不變,則采用Image.memory()讀取圖片數(shù)據(jù)流并渲染到頁面中。以下是部分關(guān)鍵代碼:

// Global.dart
import 'package:shared_preferences/shared_preferences.dart';

class Global {
  static SharedPreferences _prefs;
  static Profile profile = Profile();
  // 持久化Profile信息
  static saveProfile() =>
      _prefs.setString("profile", jsonEncode(profile.toJson()));
}
// userModel.dart
class UserModel extends ChangeNotifier {
  Profile get _profile => Global.profile; // 本地持久化
  String get _headImageKey => _profile.headImageKey;
  Uint8List get _headImageData {
    if (_profile.HeadImageData != null) {
      return new Uint8List.fromList(_profile.HeadImageData.codeUnits);
    } else {
      return null;
    }
  }

  get headImageKey => _headImageKey;
  get headImageData => _headImageData;

  set _headImageKey(String key) {
    Global.profile.headImageKey = key;
    Global.saveProfile();
  }

  set _headImageData(Uint8List bytes) {
    Global.profile.HeadImageData = new String.fromCharCodes(bytes);
    Global.saveProfile();
  }

 Future _initHeadImage(String url) async {
    String newImageKey = getImageKey(url);
    if (_headImageKey != newImageKey) { // 判斷key是否相同
      _headImageKey = newImageKey;
      Response res = await dio.get(url); // 二進(jìn)制形式請(qǐng)求圖片
      Uint8List bytes = res.data;
      _headImageData = bytes;
      notifyListeners();
    } else {
      print('利用緩存頭像!?。。?!');
    }
  }
}

調(diào)用Image.memory( Provider.of<UserModel>(context). headImageData, fit: BoxFit.cover, )

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容