由于 Android 在 Android 3.0 中引入了新的位图内存管理(位图数据现在在 Dalvik Heap中分配),我得到了这个 OutOfMemoryError 错误,它使我的应用程序崩溃。
基本上我有一个 ListView 大约。1000 个 ImageViews 包含我的资产文件夹中的位图(分辨率:300 x 200 像素)。
我在运行 Jelly Bean 的 HTC Desire 上检测到了这个问题。如果我滚动到快,我的应用程序的 Pro 版本会崩溃。(免费版列表中只有 150 项)我尝试在模拟器中重现错误,发现 Android 3.0 及更高版本中 DDMS 的垃圾收集消息不像我的旧 2.3.6 智能手机上的 GC_EXTERNAL_ALLOC (它运行应用程序没有任何问题)。因此,大量数据的内存大小是不够的。我不认为 ListView 真的将所有的 Bitmaps 都保存在内存中,但是 150 个 Items 和 1000 个 Items 之间肯定是有区别的。
我尝试在我的列表适配器中对 GetView() 执行 AsyncTask,但随后 OutOfMemory 发生在 AsyncTask 线程中。所以我对此无能为力......
有人有解决这个问题的想法吗?
谷歌的开发指南对我没有帮助
编辑:
好的,我对我的APP进行了MAT分析,似乎不是导致问题的ListView,而是我的ViewFlipper。
我的 MainMenu、我的 SearchScreen、我的 SearchResults(ListView 和 GridView - 可切换)和我的DetailedView 都是我的 ViewFlipper 的单个“页面”。
MainMenu、SearchScreen 和DetailedView 占用了我大约20 MB 的内存。我从代码中提取了 List- 和 Gridview,看起来它们占用了 8MB 到 13MB 的空间(取决于执行滚动的速度)。
Android SDK 模拟器(模拟 4.2)的 VM 堆大小为 32MB。所以最初它可以工作,如果我滚动到快速,我会遇到内存不足,并显示错误消息:
skia decoder->decode returned false
其次是各种 Bitmap-decode-OutOfMemory 问题。
所以我想我必须卸载/缓存我的 ViewFlipper 的页面 - 对吧?怎么做???
这是 ListView/GridView 实现的提取源代码。
public class GridTestActivity extends Activity {
GridView gv;
ListView lv;
int lvScreenWidth;
int lvScreenHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_grid_test);
Display display = getWindowManager().getDefaultDisplay();
this.lvScreenWidth = display.getWidth();
this.lvScreenHeight = display.getHeight();
MyAdapter adp = new MyAdapter(this);
gv = (GridView)findViewById(R.id.gridView1);
gv.setAdapter(adp);
gv.setFastScrollEnabled(true);
lv = (ListView)findViewById(R.id.listView1);
lv.setAdapter(adp);
lv.setFastScrollEnabled(true);
ActivityManager am = (ActivityManager)this.getSystemService(ACTIVITY_SERVICE);
Toast.makeText(this, "DALVIK HEAP SIZE: " + am.getMemoryClass() + "MB", 5).show();
}
static class ViewHolder{
TextView text;
ImageView icon;
}
public class MyAdapter extends BaseAdapter{
private LayoutInflater mInflater;
public MyAdapter(Context context) {
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
return 1000;
}
@Override
public Object getItem(int arg0) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_icon_text, parent, false);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.text.setText("pos:" + position);
// best way to load an asset-image???
try {
InputStream ims = getAssets().open("assetpic_" + position + ".jpg");
BitmapDrawable d = (BitmapDrawable) Drawable.createFromStream(ims, null);
ims.close();
holder.icon.setImageDrawable(d);
if (parent.equals(lv)){
int oldWidth = d.getIntrinsicWidth();
int oldHeight= d.getIntrinsicHeight();
holder.icon.getLayoutParams().height = lvScreenWidth * oldHeight/oldWidth;
}
}
catch(IOException ex) {
Log.i("MyAdapter" , "Could not load 'assetpic_" + position + ".jpg' from Assets-folder");
}
return convertView;
}
}
}
我不确定,但也许我错过了我的代码的一些细节?