如何避免 OOM 从 android 资源加载大图像
How to avoid OOM to load large images from android resource
我已经开发了 20 多个 android 应用程序,应用程序有一个教程 activity,它们由大图像幻灯片(例如;4 张大小为 750 x 1334 的图像)组成,并且会显示在首次发布时向用户展示。
由于图像质量问题,我不能再缩小尺寸了。
我的代码片段如下;
public class GalleryImageActivity extends BaseActivity implements OnGestureListener, OnTouchListener {
ViewPager imagePager;
GalleryImageAdapter galleryImageAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gallery_image);
imagePager = (ViewPager) findViewById(R.id.image_pager);
galleryImageAdapter = new GalleryImageAdapter(this);
imagePager.setAdapter(galleryImageAdapter);
imagePager.setOnTouchListener(this);
}
}
public class GalleryImageAdapter extends PagerAdapter {
public static int SCREEN_CNT = 4;
Bitmap[] bmp = new Bitmap[SCREEN_CNT];
@Override
public int getCount() {
return SCREEN_CNT;
}
@Override
public Object instantiateItem(ViewGroup view, int position) {
View view = inflater.inflate(R.layout.gallery_image_item, null);
ImageView imv = (ImageView) view.findViewById(R.id.imgView);
bmp[position] = readBitMap(context, R.drawable.tutorial_img_0 + position, position);
if (bmp[position] != null)
imv.setImageBitmap(bmp[position]);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (bmp[position] != null && !bmp[position].isRecycled())
{
bmp[position].recycle();
System.gc();
bmp[position] = null;
}
}
public Bitmap readBitMap(Context context, int resId, int position) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
InputStream is = context.getResources().openRawResource(resId);
BitmapFactory.decodeStream(is,null, opt);
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
opt.inJustDecodeBounds = false;
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
is = context.getResources().openRawResource(resId);
Bitmap bitmap = BitmapFactory.decodeStream(is,null, opt);
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
}
有时效果很好,但是当重复 5~6 次时,BitmapFactory.decodeStream 会抛出 "Out of memory"。
我如何决定比例大小或解决这个问题?
示例图像如下;
尝试使用一些图像加载库,如 Picasso 或 Glide。这会大大简化事情。
但在你的情况下,我认为问题不在于解码图像,而是你持有已经在内存中创建的位图。确保您始终只有一个位图在内存中。只需在工作室中打开 Profiler,并确认您是否没有存储对多个位图的引用。
您可以修改readBitMap
函数如下;
public Bitmap readBitMap(Context context, int resId, int position){
if (bmp[position] == null || bmp[position].isRecycled())
{
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
opt.inScaled = true;
opt.inDensity = DisplayMetrics.DENSITY_DEFAULT; // !important
InputStream is = context.getResources().openRawResource(resId);
try {
bmp[position] = BitmapFactory.decodeStream(is,null,opt);
} catch (Exception e){
bmp[position] = null;
}
}
return bmp[position];
}
某些类型的手机phone,尤其是三星,在内存泄漏方面设计不佳。
opt.inDensity = DisplayMetrics.DENSITY_DEFAULT
很重要
如果inDensity
是0
,BitmapFactory.decodeStream
将填写与资源关联的密度。 more...
当您的 activity(或页面)被破坏时,您应该将 imageview 的位图设置为 null
。
RGB_565
配置中大小为 750x1334 的位图需要大约 2MB 内存。其中 3 个可以存储在 6 MB 中,如果小心处理,应该不会创建 OutOfMemoryError
。
以下是您可以遵循的一些提示。
- 在清单的应用程序标签中使用
android:largeHeap="true"
。
- 为您的
ViewPager
使用 FragmentStatePagerAdapter
。
- 不要忘记回收不再需要的位图。
- 回收位图后使用
Runtime.getRuntime().gc();
。
- 如果您要将图像存储在
drawable
中,请将它们移动到 drawable-nodpi
。
- 使用 Glide 或 Picasso 库。
我已经开发了 20 多个 android 应用程序,应用程序有一个教程 activity,它们由大图像幻灯片(例如;4 张大小为 750 x 1334 的图像)组成,并且会显示在首次发布时向用户展示。
由于图像质量问题,我不能再缩小尺寸了。
我的代码片段如下;
public class GalleryImageActivity extends BaseActivity implements OnGestureListener, OnTouchListener {
ViewPager imagePager;
GalleryImageAdapter galleryImageAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gallery_image);
imagePager = (ViewPager) findViewById(R.id.image_pager);
galleryImageAdapter = new GalleryImageAdapter(this);
imagePager.setAdapter(galleryImageAdapter);
imagePager.setOnTouchListener(this);
}
}
public class GalleryImageAdapter extends PagerAdapter {
public static int SCREEN_CNT = 4;
Bitmap[] bmp = new Bitmap[SCREEN_CNT];
@Override
public int getCount() {
return SCREEN_CNT;
}
@Override
public Object instantiateItem(ViewGroup view, int position) {
View view = inflater.inflate(R.layout.gallery_image_item, null);
ImageView imv = (ImageView) view.findViewById(R.id.imgView);
bmp[position] = readBitMap(context, R.drawable.tutorial_img_0 + position, position);
if (bmp[position] != null)
imv.setImageBitmap(bmp[position]);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (bmp[position] != null && !bmp[position].isRecycled())
{
bmp[position].recycle();
System.gc();
bmp[position] = null;
}
}
public Bitmap readBitMap(Context context, int resId, int position) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
InputStream is = context.getResources().openRawResource(resId);
BitmapFactory.decodeStream(is,null, opt);
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
opt.inJustDecodeBounds = false;
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
is = context.getResources().openRawResource(resId);
Bitmap bitmap = BitmapFactory.decodeStream(is,null, opt);
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
}
有时效果很好,但是当重复 5~6 次时,BitmapFactory.decodeStream 会抛出 "Out of memory"。 我如何决定比例大小或解决这个问题?
示例图像如下;
尝试使用一些图像加载库,如 Picasso 或 Glide。这会大大简化事情。
但在你的情况下,我认为问题不在于解码图像,而是你持有已经在内存中创建的位图。确保您始终只有一个位图在内存中。只需在工作室中打开 Profiler,并确认您是否没有存储对多个位图的引用。
您可以修改readBitMap
函数如下;
public Bitmap readBitMap(Context context, int resId, int position){
if (bmp[position] == null || bmp[position].isRecycled())
{
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
opt.inScaled = true;
opt.inDensity = DisplayMetrics.DENSITY_DEFAULT; // !important
InputStream is = context.getResources().openRawResource(resId);
try {
bmp[position] = BitmapFactory.decodeStream(is,null,opt);
} catch (Exception e){
bmp[position] = null;
}
}
return bmp[position];
}
某些类型的手机phone,尤其是三星,在内存泄漏方面设计不佳。
opt.inDensity = DisplayMetrics.DENSITY_DEFAULT
很重要
如果inDensity
是0
,BitmapFactory.decodeStream
将填写与资源关联的密度。 more...
当您的 activity(或页面)被破坏时,您应该将 imageview 的位图设置为 null
。
RGB_565
配置中大小为 750x1334 的位图需要大约 2MB 内存。其中 3 个可以存储在 6 MB 中,如果小心处理,应该不会创建 OutOfMemoryError
。
以下是您可以遵循的一些提示。
- 在清单的应用程序标签中使用
android:largeHeap="true"
。 - 为您的
ViewPager
使用FragmentStatePagerAdapter
。 - 不要忘记回收不再需要的位图。
- 回收位图后使用
Runtime.getRuntime().gc();
。 - 如果您要将图像存储在
drawable
中,请将它们移动到drawable-nodpi
。 - 使用 Glide 或 Picasso 库。