Since Android introduced a new memory management of Bitmaps in Android 3.0 (Bitmap Data is now allocated inside the Dalvik Heap), I get this OutOfMemoryError Error which crashes my App.
Basically I have a ListView with approx. 1000 ImageViews holding Bitmaps from my Assets Folder(resolution: 300 x 200 pixel).
I detected this problem on an HTC Desire running Jelly Bean. If I scroll to fast, the Pro version of my App is crashing. (The Free version has only 150 Items in the list) I tried to reproduce the Error in the Emulator and found out that the Garbage Collection Messages of the DDMS in Android Versions 3.0 and above aren't GC_EXTERNAL_ALLOC like on my old 2.3.6 Smartphone (which runs the App without any problems). So the Memory Size for the big amount of Data isn't enough. I don't think that ListView really is holding all the Bitmaps in the memory, but there must really be a difference between 150 Items and 1000 Items.
I tried to AsyncTask the GetView() in my List Adapter, but then the OutOfMemory occurs in the AsyncTask Thread. So I'm getting nowhere with this ...
Someone has an Idea to fix this problem?
Google's development guide didn't help me
EDIT:
Ok, I MAT-analysed my APP, and it seems not to be the ListView that causes the problem, but my ViewFlipper.
My MainMenu, my SearchScreen, my SearchResults (ListView & GridView - toggleable) and my DetailedView are all single "pages" off my ViewFlipper.
MainMenu, SearchScreen and DetailedView take about 20 MB of my memory. I extracted the List- and Gridview from the code, and it seems, that they take up form 8MB up to 13MB (depending on how fast the scrolling is executed).
The Android SDK Emulator (emulating 4.2) has an VM Heap Size of 32MB. So initially it works, and if i scroll to fast i ran into the out of memory, with error message:
skia decoder->decode returned false
followed by diverse Bitmap-decode-OutOfMemory issues.
So I think I have to unload/cache the Pages of my ViewFlipper - right? How to do that???
Here is the extracted source of the ListView/GridView implementation.
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;
}
}
}
I'm not sure , but maybe I miss some details on my code?