Reputation: 6095
I have a FragmentActivity with a FragmentMediaOverview
containing a list of MediaItemView
s (each with a imageview and some text) and a click on one of the items opening a detail-Fragment.
Now when I go back (via back button) and forth (click on listitem) several times from list to detail fragment I eventually run into OOM-Errors. I use SoftReference
s for the bitmaps in the listitems as well as in the detail fragment.
According to MAT there is an incresing number of MediaItemView
s as well as FragmentMediaOverview
instances, but I just cannot figure out why.
I read this Android: AlertDialog causes a memory leak , but couldn't solve it null
ing out listeners.
Here is my code:
FragmentMediaOverview.java
(This is not a ListFragment because for a tablet-layout the MediaAdapter
needs to connect to a gridview)
public class FragmentMediaOverview extends Fragment {
private static String TAG = FragmentMediaOverview.class.getSimpleName();
private MediaAdapter adapter;
private OnMediaSelectedListener selListener;
private ArrayList<BOObject> mediaItems;
private ViewGroup layoutContainer;
private AdapterView itemContainer; // list or gridview
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, "onCreateView");
layoutContainer = (ViewGroup) inflater.inflate(R.layout.fragment_media_overview, null);
return layoutContainer;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
selListener = (OnMediaSelectedListener) activity;
}
@Override
public void onDestroy() {
super.onDestroy();
itemContainer.setOnItemClickListener(null);
selListener = null;
adapter = null;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initUi(layoutContainer);
displayMedia();
}
private void initUi(ViewGroup layoutContainer) {
itemContainer = (AdapterView) layoutContainer.findViewById(android.R.id.list);
itemContainer.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
BOMedia mediaItem = ((BOMedia) mediaItems.get(position));
//the FragmentActivity is coordinating the FragmentTransactions
selListener.onMediaSelected(mediaItem);
}
});
}
private void displayMedia() {
Log.d(TAG, "Displaying List");
if (mediaItems == null) {
loadMedia();
return;
}
Log.d(TAG, "List: " + mediaItems.size() + ", adapter: " + itemContainer.getAdapter());
if (adapter == null) {
Log.d(TAG, "Create Adapter with " + mediaItems.size());
adapter = new MediaAdapter(getActivity(), mediaItems);
}
if (itemContainer.getAdapter() == null) {
itemContainer.setAdapter(adapter);
} else {
adapter.setItems(mediaItems);
adapter.notifyDataSetChanged();
}
}
private void loadMedia() {
FragmentHelper.showProgressSpinner(layoutContainer, android.R.id.list);
DbHelper.getInstance().getMedia(mediaType, new DbQueryFinishListener() {
@Override
public void onDbCallFinish(ArrayList<BOObject> objects) {
if (!getActivity().isFinishing()) {
mediaItems = objects;
Collections.sort(mediaItems, new Comparator<BOObject>() {
final Collator c = Collator.getInstance(Locale.GERMAN);
@Override
public int compare(BOObject s1, BOObject s2) {
if (s2 != null && ((BOMedia) s2).getTitle() != null && s1 != null
&& ((BOMedia) s1).getTitle() != null) {
return c.compare(((BOMedia) s1).getTitle(),((BOMedia) s2).getTitle());
} else {
return 0;
}
}
});
displayMedia();
FragmentHelper.hideProgressSpinner(layoutContainer, android.R.id.list);
}
}
@Override
public void onDbCallException(Exception exception) {
if (!getActivity().isFinishing()) {
FragmentHelper.hideProgressSpinner(layoutContainer, android.R.id.list);
}
}
});
}
}
MediaAdapter.java
public class MediaAdapter extends BaseAdapter {
private static final String TAG = MediaAdapter.class.getSimpleName();
private Context context;
private ArrayList<BOObject> mediaItems;
public MediaAdapter(Context c, ArrayList<BOObject> mediaItems) {
super();
context = c;
this.mediaItems = mediaItems;
}
@Override
public int getCount() {
return mediaItems.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = new MediaItemView(context);
}
((MediaItemView)convertView).initialize((BOMedia) mediaItems.get(position));
return convertView;
}
public void setItems(ArrayList<BOObject> mediaItems) {
this.mediaItems = mediaItems;
}
}
MediaItemView.java
public class MediaItemView extends LinearLayout {
private static final String TAG = MediaItemView.class.getSimpleName();
private BOMedia item;
private SoftReference<Bitmap> bm;
private ImageView iv;
private Context ctx;
public MediaItemView(Context context) {
super(context);
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layoutInflater.inflate(R.layout.view_media_item, this);
this.ctx = context;
}
/** Init the view with a new BOMedia object
* @param mediaItem
*/
public void initialize(BOMedia mediaItem) {
this.item = mediaItem;
initUI();
}
private void initUI() {
TextView title = (TextView) findViewById(R.id.itemText);
iv = (ImageView) findViewById(R.id.itemImage);
title.setText(Html.fromHtml(item.getTitle()));
iv.setImageBitmap(null);
bm = null;
System.gc();
iv.invalidate();
if (item.getFilepathThumb() != null && !item.getFilepathThumb().equals("")) {
ExpansionPackManager.getInstance().getBitmapResource(item.getFilepathThumb(), false,
new BitmapReadListener() {
@Override
public void onFileRead(BitmapResponseMessage message) {
Log.d(TAG, "Bitmap read: " + message.getFilepath());
Bitmap image = message.getBitmap();
if (image != null && message.getFilepath().equals(item.getFilepathThumb())) {
bm = new SoftReference<Bitmap>(image);
iv.setImageBitmap(bm.get());
Log.d(TAG, "image set");
} else {
Log.d(TAG, "image too late: " + image);
}
}
@Override
public void onFileException(Throwable exception) {
Log.d(TAG, "image exception");
}
});
}
}
}
Upvotes: 5
Views: 5593
Reputation: 1166
In MediaItemView the size of your bitmap must be too big. If the bitmap is 600x600 and you want to display a image with a size of 50x50 you can use Bitmap.createScaledBitmap
. You should also use bitmap cache while loading your bitmap.
Upvotes: 2
Reputation: 7087
You can also use:
android:hardwareAccelerated = true
Beginning in Android 3.0 (API level 11)
, the Android 2D
rendering pipeline is designed to better support hardware acceleration. Hardware acceleration carries out all drawing operations that are performed on a View's canvas using the GPU
.
For more info http://developer.android.com/guide/topics/graphics/hardware-accel.html
Upvotes: 0
Reputation: 17139
This is because the View
for rach child in the ListView
is recreated as you scroll through. This is very heavy on resources. To avoid this use a holder class in adapters getView()
to hold and reuse the views. This is called an Efficient Adapter
. For example see Efficient List Adapter
in API demos
. http://developer.android.com/tools/samples/index.html
Upvotes: 1