現象:`ViewPager` 只需顯示幾張圖片,滑動幾下就拋 `OutOfMemoryError: Failed to allocate a 22118416-byte allocation`。核心原因:**`getCount()` 返回值過大或邏輯錯誤,導致 `instantiateItem` 被無限調用,瘋狂創建 View/位圖耗盡記憶體**。
@Override
public int getCount() {
if (null != imageList) {
return imageList.length; // 假設 length = 5
}
return 0;
}
// 問題不在這裡,關鍵看 instantiateItem 邏輯
@Override
public Object instantiateItem(View container, int position) {
View v = inflater.inflate(R.layout.activity_scene_page, null);
ImageView image = v.findViewById(R.id.scene_img);
image.setBackgroundResource(imageList[position]); // 若 position >= length 會崩潰,但 OOM 說明 position 在範圍內卻被重複調用
((ViewPager) container).addView(v);
return v;
}
有開發者為了實現「無限循環滑動」,將 `getCount()` 改為 `Integer.MAX_VALUE` 或 `length * 10000`,但 **`instantiateItem` 內部直接用 `position` 索引 `imageList` 且無取模**,導致:
public class LoopPagerAdapter extends PagerAdapter {
private final Context context;
private final int[] imageResIds;
private final LayoutInflater inflater;
public LoopPagerAdapter(Context context, int[] imageResIds) {
this.context = context;
this.imageResIds = imageResIds;
this.inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
// 無限循環:返回極大值,但實際頁面數 = imageResIds.length
return imageResIds.length == 0 ? 0 : Integer.MAX_VALUE;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
// ✅ 關鍵:取模映射到真實資源索引
int realPosition = position % imageResIds.length;
View v = inflater.inflate(R.layout.activity_scene_page, container, false);
ImageView image = v.findViewById(R.id.scene_img);
// ✅ 使用 Glide/Picasso 異步載入 + 縮放,避免 OOM
Glide.with(context)
.load(imageResIds[realPosition])
.override(800, 600) // 限制解碼尺寸
.into(image);
container.addView(v);
return v;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
// 可選:釋放圖片引用
View v = (View) object;
ImageView iv = v.findViewById(R.id.scene_img);
Glide.with(context).clear(iv);
}
}
| 優化點 | 做法 |
|---|---|
| 圖片解碼尺寸 | `BitmapFactory.Options.inSampleSize` 或 Glide `.override(w,h)` 限制最大尺寸 |
| 使用圖片加載庫 | Glide / Picasso / Coil 自動處理緩存、生命週期、OOM 保護 |
| 及時釋放 | `destroyItem` 中 `Glide.clear(imageView)`、置 null |
| 限制預載頁面 | `viewPager.setOffscreenPageLimit(1)` 預設 1,別設過大 |
| 避免在 instantiateItem 分配大對象 | 複用 View(`convertView` 模式)或使用 `RecyclerView` 替代 `ViewPager` |
結論:**`getCount()` 返回極大值實現無限循環時,必須在 `instantiateItem` 內 `position % realCount` 取模索引,並配合圖片加載庫限制解碼尺寸、及時釋放**,即可徹底解決 OOM。