App 圖片列表/詳情頁,網絡正常時加載網絡圖,斷網/弱網時自動展示本地緩存,網絡恢復自動重試加載高清圖,用戶無感知。
+------------------+
| View Layer | ImageView / RecyclerView
+------------------+
|
+------------------+
| Request Layer | RequestBuilder (Glide/Picasso/Coil/Fresco API)
+------------------+
|
+------------------+
| Engine Layer | Dispatcher → Cache → Network → Decode → Transform
+------------------+
|
+------------------+
| Cache Layer | MemoryCache (LruCache) + DiskCache (DiskLruCache)
+------------------+
|
+------------------+
| Network Layer | OkHttp / HttpURLConnection (Interceptor, Retry)
+------------------+
// 依賴
implementation 'com.github.bumptech.glide:glide:4.16.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
// 全局配置
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// 磁盤緩存 200MB
builder.setDiskCache(new InternalCacheDiskCacheFactory(context, "image_cache", 200 * 1024 * 1024));
// 內存緩存 50% 可用內存
builder.setMemoryCache(new LruResourceCacheCalculator(context));
}
}
// 使用:自動支持離線緩存
Glide.with(fragment)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.ALL) // 緩存原圖+轉換後
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.into(imageView);
✅ `DiskCacheStrategy.ALL`:下載後同時緩存**原始數據**和**轉換後圖片** (縮略、裁剪)。斷網時直接讀取磁盤緩存解碼顯示,無需重新下載轉換。
public class ImageLoader {
private LruCache memoryCache;
private DiskLruCache diskCache;
private ExecutorService executor = Executors.newFixedThreadPool(3);
private OkHttpClient client = new OkHttpClient.Builder()
.retryOnConnectionFailure(true)
.build();
public void loadImage(String url, ImageView view) {
// 1. 內存緩存
Bitmap bitmap = memoryCache.get(url);
if (bitmap != null) { view.setImageBitmap(bitmap); return; }
// 2. 磁盤緩存 (異步)
executor.execute(() -> {
Bitmap diskBitmap = getFromDiskCache(url);
if (diskBitmap != null) {
putToMemoryCache(url, diskBitmap);
runOnUiThread(() -> view.setImageBitmap(diskBitmap));
return;
}
// 3. 網絡下載
Request request = new Request.Builder().url(url).build();
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
byte[] data = response.body().bytes();
Bitmap netBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
// 寫入雙層緩存
putToMemoryCache(url, netBitmap);
putToDiskCache(url, data); // 存原始字節
runOnUiThread(() -> view.setImageBitmap(netBitmap));
}
} catch (IOException e) {
// 4. 彻底失敗:展示佔位/錯誤圖
runOnUiThread(() -> view.setImageResource(R.drawable.error));
}
});
}
}
| 策略 | DiskCacheStrategy.NONE | DATA | RESOURCE | ALL (推薦) |
|---|---|---|---|---|
| 原始數據 | ❌ | ✅ | ❌ | ✅ |
| 轉換後圖片 | ❌ | ❌ | ✅ | ✅ |
| 斷網展示原圖 | ❌ | ✅ (需重新解碼) | ❌ | ✅ (最快) |
| 斷網展示縮略圖 | ❌ | ❌ | ✅ | ✅ |
| 磁盤佔用 | 0 | 中 | 中 | 大 (2倍) |
// Glide 自動重試機制:連接失敗會自動重試 3 次 (默認)
// 自定義重試策略
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
OkHttpClient client = new OkHttpClient.Builder()
.retryOnConnectionFailure(true)
.addInterceptor(chain -> {
Request request = chain.request().newBuilder()
.header("Accept", "image/webp,image/*")
.build();
return chain.proceed(request);
})
.build();
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(client));
}
}
// 業務層監聽網絡變化,主動重載
ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkCallback callback = new NetworkCallback() {
@Override public void onAvailable(Network network) {
// 網絡恢復:重新加載列表可見項
adapter.notifyDataSetChanged(); // 或 recyclerView.getLayoutManager().findVisibleViews()
}
};
cm.registerDefaultNetworkCallback(callback);
離線圖片加載核心是 **`DiskCacheStrategy.ALL` + 適配器復用綁定生命周期**。生產項目**直接用 Glide/Coil/Fresco 成熟框架**,配置緩存大小、策略、重試攔截器,即可獲得生產級離線體驗。手寫框架僅供教學,邊界情況 (GIF、WebP、EXIF 旋轉、內存抖動) 極難處理完備。