Problem
PowerImage's default HTTP path and native Surface lifecycle had several independent sources of latency and cross-request blocking:
- encoded-byte cache misses were not single-flight;
- network/decoding work was not visibility-prioritized at the real first
Codec.getNextFrame();
- cache writes, forced flushes, and fine-grained LRU touches competed with first paint;
- ordinary PNG/GIF/WebP could enter native/custom rendering paths instead of Flutter's codec/widget fast path;
- native rendering fenced every frame instead of only teardown;
- Surface release coordination could block unrelated engine/Surface generations;
- native resources depended on global binding/cache hooks instead of the completer's last listener.
Reproducible physical-device comparison
Huawei VOG-AL10, Android 10 / API 29, Flutter 3.38.7, full AOT compilation, identical 512x512 loopback HTTP fixture, 160x160 target, 10 cold iterations. ExtendedImage is pinned to 10.1.0 and CachedNetworkImage to 3.4.1. P95 is nearest-rank.
| Cold first displayed frame |
P50 |
P95 |
peak RSS median |
| PowerImage |
41.967 ms |
46.382 ms |
251,406 KB |
| ExtendedImage 10.1.0 |
42.390 ms |
63.427 ms |
258,870 KB |
| CachedNetworkImage 3.4.1 |
75.667 ms |
78.328 ms |
259,114 KB |
PowerImage is 1.0% faster than ExtendedImage at P50 and 26.9% faster at P95; versus CachedNetworkImage it is 44.5% / 40.8% faster.
Isolated A/B results for the proposed changes
| Change |
Before P50/P95 |
After P50/P95 |
Delta |
disk ImmutableBuffer.fromFilePath |
32.834 / 38.803 ms |
27.509 / 36.203 ms |
-16.2% / -6.7% |
| deferred/bounded cache maintenance |
241.172 / 249.526 ms |
229.213 / 232.482 ms |
-5.0% / -6.8% |
schedule the real first getNextFrame() |
45.145 / 77.059 ms |
41.967 / 46.382 ms |
-7.0% / -39.8% |
standard Flutter Image widget fast path |
44.361 / 60.090 ms |
42.646 / 59.193 ms |
-3.9% / -1.5% |
animated raster queueBuffer average |
0.560 / 0.582 ms |
0.543 / 0.559 ms |
-3.0% / -3.8% |
The final animated run submitted a median 826 frames versus 819 before the widget fast path, so the timing change is not caused by dropped frames. Private RSS stayed flat (126,916 KB before, 126,804 KB after).
Four final cache/lifecycle scenarios
| Scenario |
iterations |
P50 |
P95 |
| cold first frame |
10 |
41.967 ms |
46.382 ms |
| encoded-byte disk hit |
20 |
27.936 ms |
48.598 ms |
| Flutter ImageCache hit |
10 |
15.878 ms |
22.868 ms |
| release/rebuild 100 native Surfaces |
5 |
962.568 ms |
2,337.305 ms |
All iterations reached their required real-frame completion marker. The final disk-hit distribution contains three 45-49 ms scheduling outliers; the isolated fromFilePath A/B above is the evidence for that particular change.
Raw AndroidX Macrobenchmark JSON, fixture methodology, exact versions, and memory figures are checked in here:
https://github.com/q760440238/power_image/tree/feature/android-animated-webp/example/android/macrobenchmark/results/2026-08-03-VOG-AL10
A corresponding implementation and reproducible benchmark suite is available in PR #59.
Problem
PowerImage's default HTTP path and native Surface lifecycle had several independent sources of latency and cross-request blocking:
Codec.getNextFrame();Reproducible physical-device comparison
Huawei VOG-AL10, Android 10 / API 29, Flutter 3.38.7, full AOT compilation, identical 512x512 loopback HTTP fixture, 160x160 target, 10 cold iterations. ExtendedImage is pinned to 10.1.0 and CachedNetworkImage to 3.4.1. P95 is nearest-rank.
PowerImage is 1.0% faster than ExtendedImage at P50 and 26.9% faster at P95; versus CachedNetworkImage it is 44.5% / 40.8% faster.
Isolated A/B results for the proposed changes
ImmutableBuffer.fromFilePathgetNextFrame()Imagewidget fast pathqueueBufferaverageThe final animated run submitted a median 826 frames versus 819 before the widget fast path, so the timing change is not caused by dropped frames. Private RSS stayed flat (126,916 KB before, 126,804 KB after).
Four final cache/lifecycle scenarios
All iterations reached their required real-frame completion marker. The final disk-hit distribution contains three 45-49 ms scheduling outliers; the isolated
fromFilePathA/B above is the evidence for that particular change.Raw AndroidX Macrobenchmark JSON, fixture methodology, exact versions, and memory figures are checked in here:
https://github.com/q760440238/power_image/tree/feature/android-animated-webp/example/android/macrobenchmark/results/2026-08-03-VOG-AL10
A corresponding implementation and reproducible benchmark suite is available in PR #59.