我正在为Android 3.0 Honeycomb构建一个书籍查看器。它在三星Galaxy Tab上运行良好,但在3.0.1上的Motorola Xoom上获得了很多OutOfMemoryErrors。两个设备都有48MB的VM堆空间。
我有两项活动:
BookActivity
- 有一个SlowGallery
可以加载1280x640张图片,还有一个小图库可以加载160x80张图片。
SlowGallery
是一个次要覆盖,可以让它一次投放一个图库项目,而不是快速滚动。
BitmapActivity
- 只有一个ImageView
可以加载4488x2244图像,还有一个小图库可以加载160x80图像。缩小图像尺寸不是一种选择,因为用户打算将图像放大到100%。
我把它放在Bitmap加载方法中,将位图缩减为16位:
BitmapFactory.Options o2 = new BitmapFactory.Options();
if (compressColor) o2.inPreferredConfig = Bitmap.Config.RGB_565;
o2.inPurgeable = true;
o2.inInputShareable = true;
o2.inSampleSize = (int) scale;
b = BitmapFactory.decodeStream(fis, null, o2);
当我双击BookActivity的图库时,它会调用BitmapActivity,传递要打开的相应图像编号。我可以这样做1-3次,输入BitmapActivity,然后在它遇到OutOfMemoryError之前单击Back。
在双击图库时,它会调用PageAdapter.purge(),它可以直观地卸载图库的图像。
这是BookActivity.PageAdapter,它是SlowGallery的适配器:
private class PageAdapter extends BaseAdapter {
private final LayoutInflater mInflater;
private Page currentPage;
private boolean purge = false;
private WeakReference<Bitmap> weakReferenceBitmap;
public PageAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
public void unpurge() {
purge = false;
notifyDataSetChanged();
}
public void purge() {
purge = true;
holder = null;
notifyDataSetChanged();
}
@Override
public int getCount() {
if (listPages==null || purge) return 0;
return listPages.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.gallery_pages, null);
holder = new ViewHolderIssues();
holder.image = (ImageView) convertView.findViewById(R.id.ImageViewPage);
convertView.setTag(holder);
} else {
holder = (ViewHolderIssues) convertView.getTag();
}
if (position < listPages.size()) {
currentPage = listPages.get(position);
File f = new File(Engine.PATH + currentPage.getImageMedium());
if (!purge) {
if (!f.exists()) {
holder.image.setImageResource(R.drawable.loading);
Engine.triggerTrickle(currentPage.getImageMedium(), WeightedAsset.IMAGE_MEDIUM, getApplicationContext());
} else {
weakReferenceBitmap = new WeakReference<Bitmap>(Engine.loadImageFromBitmap(currentPage.getImageMedium(), screenWidth, 1, true));
if (weakReferenceBitmap.get()!=null) {
holder.image.setImageBitmap(weakReferenceBitmap.get());
} else {
holder.image.setImageDrawable(null);
}
}
} else {
weakReferenceBitmap.clear();
holder.image.setImageDrawable(null);
holder = null;
System.gc();
}
f = null;
}
return convertView;
}
}
查看HPROF文件,在Dominator Tree下,即使我在BookActivity.SlowGallery
,BitmapActivity
仍然存在。在BookActivity
时,SlowGallery
占用3.2-6.5MB(每张图片1.6MB)但在BitmapActivity
中占用1.6MB(SlowGallery.LinearLayout.ImageView.BitmapDrawable
= 1.6MB)。我该如何摆脱SlowGallery
?
答案 0 :(得分:0)
啊,有点复杂的代码块。弱引用可能有所帮助,但一般来说,如果在适配器中使用ImageView,则在设置新位图之前,应该获取旧的位图,然后调用Bitmap.recycle()
。您应该在destroy
编辑时执行BitmapActivity。这应该是您需要的大部分内容。
答案 1 :(得分:0)
谢谢!我设法重构它来回收位图。但是,由于BitmapActivity.onCreate
在BookActivity.onDestroy
之前被调用,因此画廊仍然存在。如果我确实调用了purge()函数,除了点击/选中的视图(由Gallery
双击监听器保持活动之外)我将设法清除其中的大部分内容!)
然后我消灭了这个已经存在的画廊并放置了我自己的ImageView imageviewCurrentPage
(和一个ImageView imageviewFlipperPage
)并将它们放入和取出。完成动画后,imageviewFlipperPage会立即销毁,所以我保证最多只能加载2个位图。使用所选的,点击的视图等,Gallery
可以使用五个视图。
我还添加了这个以进一步隔离内存使用情况:
<activity android:name=".BitmapActivity" android:process=":BitmapActivity" ...>
但是,它带来了复杂性,即静态类不会跨进程共享。因此,如果在BookActivity中我做了Engine.SESSION_ID = 5,其中SESSION_ID是静态的,我将无法在BitmapActivity的Engine.SESSION_ID中读取它。