Flutter Image Pre-Caching

고랭지참치·2025년 4월 9일
0

Flutter

목록 보기
13/24
post-thumbnail

배경상황

  • 사내에서 사용자에게 판매하는 보급형 디바이스는 성능이 매우 낮음.
  • 이미지 로딩하는데도 오래걸려서, 무조건 로딩화면을 봐야한다.
  • 이미지를 미리 캐싱해놔서 로딩 시간을 줄여 사용자 경험을 개선하자!

테스트 목적

  • Pre-caching이 실질적 효과를 가지는지를 확인한다.
  • Local Asset과 Network 이미지 각각의 Pre-caching 성능 차이를 비교한다.
    • 보통 network pre-cache가 유의미한 결과를 갖는다

테스트 환경

  • Web(Chrome)
    • Local
      • Cache 미적용
      • = Cache 적용
    • Network
      • Cache 미적용
      • Cache 적용
  • Samsung A8 테블릿
    • Local
      • Cache 미적용
      • Cache 적용
    • Network
      • Cache 미적용
      • Cache 적용

Web Build Test 결과

Web Local Asset cache 미적용

Web Local Asset cache 적용

Web Network Cache 미적용

Web Network Cache 적용

Android App Build Test 결과

App Asset cache 미적용

App Asset cache 적용

App Network cache 미적용

App Network cache 적용

코드 Repo

https://github.com/MinwooRowan/pre_cache_test

abstract class PreCacheUtil {
  PreCacheUtil._();

  static Future<void> startImageCache({
    bool isApplyForLocalAsset = true,
    List<String>? networkImagePathList,
  }) async {
    try {
      final binding = WidgetsFlutterBinding.ensureInitialized();
      binding.addPostFrameCallback((_) async {
        BuildContext? context = binding.rootElement;

        if (context != null) {
          if (isApplyForLocalAsset) {
            await _startImageCache(context);
          } else if (networkImagePathList != null) {
            await _startNetworkImageCache(context,
                networkImagePathList: networkImagePathList);
          }
        }
      });
    } catch (e) {
      rethrow;
    }
  }

  static Future<void> _startNetworkImageCache(BuildContext context,
      {required List<String> networkImagePathList}) async {
    try {
      for (String path in networkImagePathList) {
        await precacheImage(NetworkImage(path), context);
      }
    } catch (e) {
      rethrow;
    }
  }

  static Future<void> _startImageCache(BuildContext context) async {
    try {
      final manifestJson =
          await DefaultAssetBundle.of(context).loadString('AssetManifest.json');
      final Map<String, dynamic> manifestMap = json.decode(manifestJson);
      final List<String> assetList = manifestMap.keys.toList();

      List<String> extensionList = ['svg', 'png', 'jpg', 'jpeg'];
      assetList.removeWhere(
          (element) => !extensionList.contains(element.split('.').last));

      for (String path in assetList) {
        // ignore: use_build_context_synchronously
        _imageCache(path, context);
      }
    } catch (e) {
      rethrow;
    }
  }

  /// 이미지 전달받아서 캐싱함
  static Future<void> _imageCache(String path, BuildContext context) async {
    try {
      bool isSvg = path.contains('.svg');
      if (isSvg) {
        final loader = SvgAssetLoader(path);
        await svg.cache
            .putIfAbsent(loader.cacheKey(null), () => loader.loadBytes(null));
      } else {
        await precacheImage(AssetImage(path), context);
      }
    } catch (e) {
      rethrow;
    }
  }
}

결론

  • 성능이 안좋은(Cpu) 테블릿 기기에서 사용해보니 케싱 성능 체감이 된다.
  • 모든 이미지를 미리 캐싱해두는 것은 자원관리 측면에서 불필요하나, 중요도가 높거나 기획상 크리티컬 패스에 위치한 이미지들은 프리캐싱 해두는 것이 가능하다는 것을 기억하기.

래퍼런스

https://api.flutter.dev/flutter/widgets/precacheImage.html

https://medium.com/@rishad2002/image-loading-with-flutters-precacheimage-44431f7f9512

https://terry1213.com/flutter/flutter-precacheimage/

https://rlg1133.tistory.com/144

profile
소프트웨어 엔지니어 / Flutter 개발자

0개의 댓글