0

I'm building a Pulse kind of UI. For this I'm using Gallery inside ListView. However, I'm facing a strange issue, the memory allocation in my app increases gradually from 9 MB to 64 MB as I scroll through listview until which it finally crashes.

What didn't work:

  • I doubted if this is because of bitmaps that I'm using. So, I tested it without images inside gallery. I also used Universal Image Loader library. However, nothing changed.

  • I read on similar question in SO that disabling scrollingCache of listView might help. However, still no effect.

What worked:

  • Designing efficient adapter using ViewHolder, getTag(), setTag() solved memory issues. However, it brought a strange issue that the listView sometimes shows empty or repeating rows, thereby showing wrong data in UI. How can I solve this? Is there any better approach to go about this?

ListViewAdapter.java

public class ListViewAdapter extends BaseAdapter {
    String user_locale;
    Context context;
    LayoutInflater inflater;
    AssetsAdapter assestsAdapter;
    List<Category> categoryList;
    List<BaseAssets> baseAssetList;
    AssetsTable assetTable;
    ViewHolder viewHolder;

public ListViewAdapter(Context context, List<Category> categoryList) {
    this.context = context;
    user_locale = Universal.getCurrentLanguage(context);
    inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    this.categoryList = categoryList;
    assetTable = new AssetsTable(context);
    viewHolder = new ViewHolder();
}

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

public Object getItem(int position) {
    return position;
}

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

public View getView(final int position, View convertView, ViewGroup parent) {
    String catID = categoryList.get(position).catID;

    if (convertView == null)
    {
        convertView = inflater.inflate(R.layout.category_list_item, parent, false);
        viewHolder.categoryTitle = (TextView) convertView.findViewById(R.id.category_title);
        viewHolder.gallery = (Gallery) convertView.findViewById(R.id.gallery);
        convertView.setTag(viewHolder);
    } else
        viewHolder = (ViewHolder) convertView.getTag();

    assetTable = new AssetsTable(context);
    int count = assetTable.getCountOfParticularCategory(catID);

    initializeGallery(catID);

    String text = categoryList.get(position).catName + "  (" + count + ")  ";
    viewHolder.categoryTitle.setText(text);
    return convertView;

}

public void initializeGallery(String catID) {
    assetTable = new AssetsTable(context);
    baseAssetList = assetTable.getAllBaseAssets(catID);
    assestsAdapter = new AssetsAdapter(context, baseAssetList);
    adjustGalleryHeight();
    viewHolder.gallery.setAdapter(assestsAdapter);
}

public void adjustGalleryHeight() {
    boolean isPortrait = false;
    final String TAG_PORTRAIT = "portrait";

    for (BaseAssets b : baseAssetList)
    {
        if (b.thumbOrientation.equals(TAG_PORTRAIT))
            isPortrait = true;
    }

    final float scale = context.getResources().getDisplayMetrics().density;
    if (isPortrait)
    {
        RelativeLayout.LayoutParams params1 = (RelativeLayout.LayoutParams) viewHolder.gallery.getLayoutParams();
        params1.height = (int) (350 * scale);
        viewHolder.gallery.setLayoutParams(params1);
    } else
    {
        RelativeLayout.LayoutParams params1 = (RelativeLayout.LayoutParams) viewHolder.gallery.getLayoutParams();
        params1.height = (int) (280 * scale);
        viewHolder.gallery.setLayoutParams(params1);
    }
}

class ViewHolder {
    TextView categoryTitle;
    Gallery gallery;

}

}

GalleryAdapter.java:

public class GalleryAdapter extends BaseAdapter {

final String TAG_PORTRAIT = "portrait";
LayoutInflater inflater;
ImageLoader imageLoader;
DisplayImageOptions options;
Context context;
List<BaseAssets> baseAssetsList;

Gallery.LayoutParams params;

public GalleryAdapter(Context context, List<BaseAssets> baseAssetsList) {
    imageLoader = ImageLoader.getInstance();
    options = new DisplayImageOptions.Builder().cacheOnDisc().imageScaleType(ImageScaleType.EXACTLY).bitmapConfig(Bitmap.Config.RGB_565).cacheInMemory().showStubImage(
            R.drawable.no_preview).build();
    this.context = context;
    this.baseAssetsList = baseAssetsList;
    inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

}

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

public Object getItem(int position) {
    return position;
}

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

public View getView(final int position, View convertView, ViewGroup parent) {

    ViewHolder viewHolder;
    if (convertView == null)
    {
        viewHolder = new ViewHolder();
        convertView = inflater.inflate(R.layout.gallery_list_item, parent, false);
        viewHolder.galleryWrapper = (RelativeLayout) convertView.findViewById(R.id.gallery_wrapper);
        viewHolder.downloadThumbnail = (ImageView) convertView.findViewById(R.id.thumbnailDownloadStatus);
        viewHolder.assetImage = (ImageView) convertView.findViewById(R.id.thumbnail_image);
        viewHolder.assetName = (TextView) convertView.findViewById(R.id.thumbnail_name);
        convertView.setTag(viewHolder);
    } else
        viewHolder = (ViewHolder) convertView.getTag();

    final float scale = context.getResources().getDisplayMetrics().density;

    if (baseAssetsList.get(position).thumbOrientation.equals(TAG_PORTRAIT))
    {

        params = new Gallery.LayoutParams((int) (166 * scale), (int) (245 * scale));
        viewHolder.galleryWrapper.setLayoutParams(params);

    } else
    {
        params = new Gallery.LayoutParams((int) (238 * scale), (int) (191 * scale));
        viewHolder.galleryWrapper.setLayoutParams(params);
    }

    viewHolder.assetName.setText(baseAssetsList.get(position).contentTitle);
    String raw_url = baseAssetsList.get(position).thumbnail;
    String correctUrl = correctUrl(raw_url);
    imageLoader.displayImage(correctUrl, viewHolder.assetImage, options);

    convertView.setOnClickListener(new View.OnClickListener() {

        public void onClick(View v) {
            Intent intent = new Intent(context, DownloadActivity.class);
            intent.putExtra(Universal.ASSET_TYPE, baseAssetsList.get(position).contentType);
            intent.putExtra(Universal.ASSET_TITLE, baseAssetsList.get(position).contentTitle);
            intent.putExtra(Universal.ASSET_URL, baseAssetsList.get(position).path);
            intent.putExtra(Universal.ASSET_LARGE_THUMBNAIL, baseAssetsList.get(position).thumbnail);

            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

            context.startActivity(intent);

        }
    });

    return convertView;
}

public String correctUrl(String raw_url) {
    String corrected_url = raw_url;
    if (!raw_url.contains("http:/"))
        corrected_url = ("http:/" + raw_url);
    return corrected_url;
}

class ViewHolder {
    RelativeLayout galleryWrapper;
    ImageView assetImage;
    ImageView downloadThumbnail;
    TextView assetName;

}

}

Gaurav Arora
  • 17,124
  • 5
  • 33
  • 44
  • It's because `initializeGallery(catID)` call. Inside this method you are creating objects and you are calling it from getView... It's very bad idea, it generates a lot of garbage when you are scrolling your list (when you scroll getView is called many times). – Michał Z. Apr 16 '13 at 05:47
  • 1
    https://github.com/nostra13/Android-Universal-Image-Loader/ check the 4th point under useful info – Raghunandan Apr 16 '13 at 05:49
  • @Raghunandan I've tried completely removing this library and all imageViews. I've also tried most of those points. However no help ! – Gaurav Arora Apr 16 '13 at 05:51
  • @MichałZ. Can you suggest a work around? – Gaurav Arora Apr 16 '13 at 05:51

2 Answers2

2

In your ListViewAdapter you are not calling

viewHolder = new ViewHolder();

in getView() when convertView is null.

Check it! That might be the problem.

Ameer Moaaviah
  • 1,530
  • 12
  • 27
1

Try changing the "viewHolder = new ViewHolder();" inside the getview of Adapter if the convertview is null;

Like :

public View getView(final int position, View convertView, ViewGroup parent) {
String catID = categoryList.get(position).catID;

if (convertView == null)
{
    viewHolder = new ViewHolder(); //Add this line. Otherwise u are sharing the same instance for other views.
    convertView = inflater.inflate(R.layout.category_list_item, parent, false);
    viewHolder.categoryTitle = (TextView) convertView.findViewById(R.id.category_title);
    viewHolder.gallery = (Gallery) convertView.findViewById(R.id.gallery);
    convertView.setTag(viewHolder);
} else
    viewHolder = (ViewHolder) convertView.getTag();

assetTable = new AssetsTable(context);
int count = assetTable.getCountOfParticularCategory(catID);

initializeGallery(catID);

String text = categoryList.get(position).catName + "  (" + count + ")  ";
viewHolder.categoryTitle.setText(text);
return convertView;

}

Eldhose M Babu
  • 14,382
  • 8
  • 39
  • 44
  • Hahaha..It worked..Never expected that problem would be that simple..Thanks ! Though I can't understand I was re-intializing all components inside viewHolder. However, they repeated. Do you have any idea why ? – Gaurav Arora Apr 16 '13 at 05:58
  • By re-initializing you are only changing the value assosiated with controls inside ViewHolder in the case where convertview is not null. So while changing the value of one view holder, since the same viewholder is associated with other views, the data will get repeated. – Eldhose M Babu Apr 16 '13 at 06:52