Emre Alparslan
Emre Alparslan

Reputation: 1042

How can I solve memory leak gridview into listview? - Android

I trying to make a listview which contains a view in every row. This view contains 2 textviews and 1 gridview which is 2 columns. In every column I use a basic layout which is consist of 2 textviews.

This is preview of basic layout which is used in every block of gridview.

enter image description here

Here is its xml; -First view-

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listview_item">

<TextView
    android:layout_width="220dp"
    android:layout_height="wrap_content"
    android:textSize="18dp"
    android:text="Item Name"
    android:id="@+id/list_item"
    android:maxLines="1"
    android:layout_marginLeft="5dp"
    />

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Price"
    android:textSize="18dp"
    android:id="@+id/item_price"
    android:layout_alignParentRight="true"
    android:layout_alignParentEnd="true"
    android:layout_marginRight="5dp"/>

</RelativeLayout>

Here is my second view which contains 2 textview and 1 gridview.

enter image description here

Here its xml; -Second view-

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listview_item"
android:orientation="vertical">

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="40dp"
    android:text="Kategori"
    android:id="@+id/categories_title_list_layout"/>

<GridView
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:id="@+id/gridView_list_layout"
    android:numColumns="2"
    android:layout_weight="1"/>

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Not: Lorem ipsum dolor sit amet.."
    android:textSize="18dp"
    android:id="@+id/categories_note"/>

</LinearLayout>

Here is my last view; -Third view-

enter image description here

This listview's every row takes shape of my second view which takes shape of first view.

Here is my adapter which are create this views. For first view I use this adapter;

public class ItemListAdapter extends BaseAdapter {

private Context context;
private ArrayList<Item> items;

public ItemListAdapter(Context context, ArrayList<Item> items) {
    super();
    this.context = context;
    this.items = items;
}

public int getCount() {
    return items.size();
}

public Object getItem(int i) {
    return items.get(i);
}

public long getItemId(int i) {
    return i;
}

public View getView(final int i, View view, ViewGroup viewGroup) {
    ViewHolder holder;

    if(view == null) {
        holder = new ViewHolder();
        LayoutInflater inflater = (LayoutInflater)     context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.listview_item_content, null);

        holder.title = (TextView) view.findViewById(R.id.list_item);
        holder.price = (TextView) view.findViewById(R.id.item_price);

        view.setTag(holder);
    }

    holder = (ViewHolder) view.getTag();
    Typeface face=Typeface.createFromAsset(context.getAssets(), "fonts/roboto.ttf");
    holder.title.setTypeface(face, Typeface.BOLD);
    holder.title.setText(items.get(i).getName());
    holder.title.setTextColor(Color.parseColor(Shop.getInstance().getItemGridTextColor()));
    holder.price.setText(Global.getLocalizedPriceStringByLocale(Shop.getLocale(), items.get(i).getPrice()));
    holder.price.setTextColor(Color.parseColor(Shop.getInstance().getItemGridTextColor()));

    return view;
}

public class ViewHolder {
    public TextView title;
    public TextView price;
}
}

(This adapter takes item's title and price and puts them in first view.)

My second adapter which create second view is here;

public class CategoryListAdapter extends BaseAdapter {

private Context context;
//private ArrayList<Item> items;
private  ArrayList<Category> currentCategory;

public CategoryListAdapter(Context context,  ArrayList<Category> category) {
    super();
    this.context = context;
    currentCategory = category;

}

public int getCount() {
    return currentCategory.size();
}

public Object getItem(int i) {
    return currentCategory.get(i);
}

public long getItemId(int i) {
    return i;
}

public View getView(final int i, View view, ViewGroup viewGroup) {

    ViewHolder holder;

    if (view == null) {
        holder = new ViewHolder();
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.grid_list_layout, null);

        holder.title = (TextView) view.findViewById(R.id.categories_title_list_layout);
        holder.gridView = (GridView) view.findViewById(R.id.gridView_list_layout);
        holder.note = (TextView) view.findViewById(R.id.categories_note);

        view.setTag(holder);
    }

    holder = (ViewHolder) view.getTag();
    Typeface face = Typeface.createFromAsset(context.getAssets(), "fonts/roboto.ttf");
    holder.title.setTypeface(face, Typeface.BOLD);

    if(currentCategory.get(i).getName().equals("")){
        holder.title.setText("Diğer");
    }else{
        holder.title.setText(currentCategory.get(i).getName());
    }

    holder.title.setTextColor(Color.parseColor(Shop.getInstance().getItemGridTextColor()));
    holder.note.setText(currentCategory.get(i).getDeepNote());
    holder.note.setTextColor(Color.parseColor(Shop.getInstance().getItemGridTextColor()));
    holder.gridView.setAdapter(new ItemListAdapter(context, currentCategory.get(i).getItems()));
    holder.gridView.setBackgroundColor(Color.parseColor(Shop.getInstance().getItemGridBackgroundColor()));
    holder.gridView.getBackground().setAlpha(180);

    return view;
}

public class ViewHolder {
    public TextView title;
    public GridView gridView;
    public TextView note;
}
}

And I use this adapter to create ListView.

Here is my problem. This works really slow. I mean ListView freezes for a moment then slide down when I try to move down. And there is another problem about GridView's height. My GridView's height is wrap_content but it doesn't behave like wrap_content. It shows bigger or smaller GridView.

enter image description here

For example; under "Diğer" title there should be a GridView which contains only 1 item as you can see, but it can not show the complete text. And under "Adet Ürünler" there should be 190 items but it only views 20 of them.

These are my problems. Sorry for my coding. If you can not understand my code, please ask me. Thanks for your helps.

Upvotes: 0

Views: 696

Answers (2)

Christian R.
Christian R.

Reputation: 1538

as far as i understand your problem, it's low performance during scrolling the list you've shown in the screenshot?

a solution to your problem would need you to rewrite your adapter and layouts. Your performance problem is due to the rather large list-items you use (e.g. a grid inside a single list item with 190 items), that have to be loaded during scrolling the list, together with a rather low re-usability of list-items.

in addition, i prefer not to use view-holders.

To get rid of the grids, you could use a list of objects (or wrappers like shown below), that contains the 'title', 'note' and the single grid rows in between. you would have to overwrite some of the adapters methods, to use multiple viewtypes inside one listview (like shown below).

perhaps you'll also need some more code, to map your model into the new list, but after all, your performance should be back to normal.

only disadvantage i know of (and have no fast solution for) is due to the different height in single list items, the scrollbar of the whole list shows sometimes a strange behaviour (like: height of scrollbar indicator changes during scroll)

wrapper

public class ListItemWrapper {
    ListItemType type;
    Object content;

    public ListItemWrapper(ListItemType type, Object content) {
        this.type = type;
        this.content = content;
    }

    public enum ListItemType { Title,Note, Content;}
}

ListAdapter

public class ListAdapter extends ArrayAdapter<ListItemWrapper> {

    private LayoutInflater inflater;

    public ListAdapter(Context context, int resource) {
        super(context, resource);
        inflater = LayoutInflater.from(context);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ListItemWrapper item = getItem(position);
        switch (item.type) {
            case Content: return getViewForGridRow((GridRowContent)item.content, convertView);
            case Note: return getViewForNote((String)item.content, convertView);
            case Title: return getViewForTitle((String) item.content, convertView);

        }
        return convertView; //this case should never happen
    }

    private View getViewForTitle(String content, View convertView) {
        if (convertView == null) {
            //TODO inflate a new view for the title
            convertView =  inflater.inflate(R.layout.titleRowLayout, null);
        }
        //TODO set textview value for the title
        return convertView;
    }

    private View getViewForNote(String content, View convertView) {
        if (convertView == null) {
            //TODO inflate a new view for the note
            convertView =  inflater.inflate(R.layout.noteRowLayout, null);
        }
        //TODO set textview value for the note
        return convertView;
    }

    private View getViewForGridRow(GridRowContent item, View convertView) {
        if (convertView == null) {
            //TODO inflate a new view for a single grid row
            convertView =  inflater.inflate(R.layout.gridRowLayout, null);
        }
        //TODO set values of the grid row (e.g. textview items)
        return convertView;
    }

    @Override
    public int getItemViewType(int position) {
        return getItem(position).type.ordinal();
    }

    @Override
    public int getViewTypeCount() {
        return ListItemType.values().length;
    }
}

I simplified parts of the model, but i hope you'll get the point.

as you can see, i don't use a custom BaseAdapter, since the arrayadapter already manages my collection. i only need to tell the list, that there are different item view types and which item in the collection uses which view type.

Additionally, there's no need to use any holders, since all holding and managing the different views is already done by the adapter - e.g. we only need to inflate views if the given convertView is not already cached within the adapter.

using this way should be much more memory efficient and performance should increase, since much less views have to be inflated in a single step.

I hope this helps,

Christian

Edit

can't explain the disapearing gridview in the center, but that shouldn't happen anymore

Upvotes: 0

nKn
nKn

Reputation: 13761

This answer won't give you a definitive solution, not because I'm not willing, but because it's impossible (and even harder without not just viewing your code, but knowing it very well). But from my experience I can tell you that those kind of memory leaks doesn't occur just due to directly referenced objects - objects you declare (and keep referencing another classes/objects) in turn depends on many other classes and so on, and probably you're seeing a memory leak due to an incorrect handling of any of your instances which at the same time reference other instances.

Debugging memory leaks is a often a very hard work, not just because as I said above it sometimes doesn't depend directly on what you've declared, but also because finding a solution might not be trivial. The best thing you can do is what you already seem to be doing: DDMS + HPROF. I don't know how much knowledge you have, but although it's not a universal method, this link helped me so much to find memory leaks in my code.

Although it seems trivial, the best way to debug those kind of things is progresively remove portions of your code (overall, those which implies working with instances of other classes) and see how the HPROF report change.

Upvotes: 1

Related Questions