一键导入
bitmap-and-image-optimization
Downsample, cache, and choose the right image format (AVIF, HEIC, WebP) to cut memory, network, and decode time on iOS and Android.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Downsample, cache, and choose the right image format (AVIF, HEIC, WebP) to cut memory, network, and decode time on iOS and Android.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Schedule, batch, and constrain background work with WorkManager, BGTaskScheduler, and push-over-poll patterns to minimize battery and data cost.
Attribute battery drain to CPU, radio, GPS, display, and background work using Android Battery Historian and Xcode Energy Log / Instruments Energy gauge.
Shrink APK/AAB and IPA size with R8/ProGuard, dead-code elimination, asset hygiene, and Android App Bundle / App Thinning best practices.
Cut local and CI build times on Android (Gradle config cache, build cache, KSP over KAPT) and iOS (Xcode build cache, ccache, modules). Use when clean builds exceed ten minutes.
Find and fix common memory leaks caused by listeners, closures, singletons, and lifecycle mismatches on iOS, Android, Flutter, and React Native.
Capture and analyze heap dumps on Android (Android Studio Profiler, LeakCanary) and iOS (Instruments Allocations/Leaks). Use when memory-related crashes or growth are suspected.
| name | bitmap-and-image-optimization |
| description | Downsample, cache, and choose the right image format (AVIF, HEIC, WebP) to cut memory, network, and decode time on iOS and Android. |
Images dominate mobile memory and bandwidth. A single 4000×3000 JPEG decoded naively takes 48 MB in RAM (4000 × 3000 × 4 bytes). This skill covers sizing, caching, and format selection.
Decode to the on-screen pixel size, not the full resolution. The screen does not care about extra pixels.
Android (Glide):
Glide.with(view)
.load(url)
.override(view.width, view.height) // decode to target size
.format(DecodeFormat.PREFER_RGB_565) // for opaque images
.into(view)
Android (Coil):
imageView.load(url) {
size(ViewSizeResolver(imageView)) // matches the view
allowRgb565(true)
}
iOS (SDWebImage):
imageView.sd_setImage(
with: URL(string: url),
placeholderImage: placeholder,
context: [.imageThumbnailPixelSize: CGSize(width: 512, height: 512)]
)
iOS native UIImage downsample:
func downsample(url: URL, to size: CGSize, scale: CGFloat) -> UIImage? {
let opts: [CFString: Any] = [kCGImageSourceShouldCache: false]
guard let src = CGImageSourceCreateWithURL(url as CFURL, opts as CFDictionary) else { return nil }
let maxDim = max(size.width, size.height) * scale
let thumbOpts: [CFString: Any] = [
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceShouldCacheImmediately: true,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: maxDim,
]
return CGImageSourceCreateThumbnailAtIndex(src, 0, thumbOpts as CFDictionary).map { UIImage(cgImage: $0) }
}
Flutter:
CachedNetworkImage(
imageUrl: url,
memCacheWidth: 512, // decode at target size
memCacheHeight: 512,
placeholder: (c, _) => const ColoredBox(color: Color(0xffeeeeee)),
)
React Native — prefer FastImage or Expo Image; they downsample natively. Set resizeMode and explicit dimensions.
| Use case | Best format | Why |
|---|---|---|
| Photos over network | AVIF | 30–50% smaller than JPEG; wide OS support (Android 12+, iOS 16+). |
| Fallback for AVIF | WebP | Universal, better than JPEG/PNG, lossy or lossless. |
| Apple-first pipelines | HEIC | Native to iOS; smaller than JPEG. |
| Illustrations / icons | SVG or vector drawable | Scale for free; typically tiny. |
| Flat UI with transparency | WebP lossless or PNG-8 | Smaller than PNG-24. |
| Animations | AVIF sequence / AVIS or animated WebP | Replace GIFs. |
Content-Negotiation from your CDN:
Accept: image/avif,image/webp,image/apng,image/*,*/*;q=0.8
Return AVIF when Accept contains image/avif. Emit Vary: Accept.
RGB_565 (2 bytes/px) for opaque photos on low-end devices — halves RAM vs ARGB_8888.HARDWARE bitmaps on API 26+ — zero-copy to GPU, but cannot be read back on CPU.BitmapFactory.Options().apply {
inPreferredConfig = Bitmap.Config.HARDWARE // API 26+
inSampleSize = calculateInSampleSize(width, height, targetW, targetH)
}
Every serious loader has a memory cache + disk cache. Size them generously:
Runtime.getRuntime().maxMemory() (Android) or 50 MB cap (iOS).Coil example:
ImageLoader.Builder(context)
.memoryCache { MemoryCache.Builder(context).maxSizePercent(0.25).build() }
.diskCache { DiskCache.Builder().directory(context.cacheDir.resolve("img")).maxSizeBytes(256L * 1024 * 1024).build() }
.respectCacheHeaders(true)
.build()
For hero images on the next screen, preload before navigation:
imageLoader.enqueue(ImageRequest.Builder(ctx).data(heroUrl).build())
In SwiftUI use ImageRenderer or AsyncImage with .task { await prefetch() }. In Flutter use precacheImage.
StateFlow / @State. Store identifiers; let the loader return bitmaps per view.BackdropFilter / blur over large images; blur once into a smaller buffer, then upscale.Modifier.background or a single gradient.adb shell dumpsys meminfo --package <pkg> | findstr Graphics shows GPU texture memory.Image and ImageCache.Log per image: URL, source (cache/network), decoded width/height, decode time. A p95 > 40 ms or source=network > 50% on warm scrolls is a regression.
Accept content negotiation; JPEG/PNG only as fallback.