Fragment中的Listview导致内存泄漏

时间:2012-06-27 14:14:27

标签: android

我有一个FragmentActivity,FragmentMediaOverview包含MediaItemView s列表(每个都有一个imageview和一些文本),然后点击其中一个项目打开一个细节片段。 现在当我从列表到细节片段多次返回(通过后退按钮)和向前(单击listitem)时,我最终遇到了OOM-Errors。我使用SoftReference s作为listitems中的位图以及详细信息片段中的位图。 根据MAT,有MediaItemViewFragmentMediaOverview个实例以及null实例,但我无法弄清楚原因。

我读了这个Android: AlertDialog causes a memory leak,但无法解决它MediaAdapter听众。

这是我的代码:

FragmentMediaOverview.java

(这不是ListFragment,因为对于平板电脑布局,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); } } }); } } 需要连接到gridview)

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;
    }
}

MediaAdapter.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");
                        }
                    });

        }
    }

}

MediaItemView.java

{{1}}

3 个答案:

答案 0 :(得分:2)

在MediaItemView中,位图的大小必须太大。如果位图为600x600并且您想要显示大小为50x50的图像,则可以使用Bitmap.createScaledBitmap。您还应该在加载位图时使用位图缓存。

答案 1 :(得分:1)

这是因为当您滚动浏览时,View中的rach子项的ListView会重新创建。这对资源非常重。为避免这种情况,请使用适配器getView()中的holder类来保存和重用视图。这称为Efficient Adapter。例如,请参阅Efficient List Adapter中的API demoshttp://developer.android.com/tools/samples/index.html

答案 2 :(得分:0)

您也可以使用:

android:hardwareAccelerated = true

Android 3.0 (API level 11)开始,Android 2D渲染管道旨在更好地支持硬件加速。硬件加速使用GPU执行在View画布上执行的所有绘制操作。

了解更多信息http://developer.android.com/guide/topics/graphics/hardware-accel.html