2

I have this ImageAdapter for android's listView:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View view = null;
    RelativeLayout relativeLayout = null;
    Offer currentOffer = mOffersList.get(position);

    if (convertView == null) { // create a new view if no recycling
                                // available
        // Make up a new view
        LayoutInflater inflater = (LayoutInflater) mContext
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.offer_list_item, null);
        relativeLayout = (RelativeLayout) view
                .findViewById(R.id.offerImage);

    } else {
        view = (View) convertView;
        relativeLayout = (RelativeLayout) view
                .findViewById(R.id.offerImage);
        setBackgroundDrawable(relativeLayout, null);

    }

    String imageUrl = "";
    imageUrl = currentOffer.getImageUrl().toString();

    Bitmap bitmap = imageCache.get(imageUrl);

    if (bitmap != null) {
        Drawable dr = new BitmapDrawable(mContext.getResources(), bitmap);
        setBackgroundDrawable(relativeLayout, dr);
    } else {
        if (!downloadingImageUrls.contains(imageUrl)) {
            downloadingImageUrls.add(imageUrl);
            new DownloadImageAsyncTask().execute(imageUrl);
        }
    }

    return view;
}

and this:

class DownloadImageAsyncTask extends AsyncTask<String, Void, Void> {
        @Override
        protected Void doInBackground(String... params) {
            String imageUrl = params[0];
            try {
                Bitmap bitmap = BitmapFactory
                        .decodeStream((InputStream) new URL(imageUrl)
                                .getContent());
                imageCache.put(imageUrl, bitmap);

            } catch (IOException e) {
                Log.e("DownloadImageAsyncTask", "Error reading bitmap" + e);
            }
            downloadingImageUrls.remove(imageUrl);
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            notifyDataSetChanged();
        }
    }

why do all of the list items loaded together? it's don' asynchronously but yet not one by one. All together.

how can i load it lazily?

and why is this code more efficient?

// better
public class DownloadImageAsyncTask2 extends
        AsyncTask<String, Void, Bitmap> {
    private final ImageView imageView;

    public DownloadImageAsyncTask2(ImageView imageView) {
        this.imageView = imageView;
    }

    @Override
    protected void onPreExecute() {
        Log.i("DownloadImageAsyncTask", "Starting image download task...");
    }

    @Override
    protected Bitmap doInBackground(String... params) {
        try {
            return BitmapFactory.decodeStream((InputStream) new URL(
                    params[0]).getContent());
        } catch (IOException e) {
            Log.e("DownloadImageAsyncTask", "Error reading bitmap" + e);
        }
        return null;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
        }
    }
}
Thomas
  • 10,358
  • 4
  • 27
  • 35
Elad Benda
  • 35,076
  • 87
  • 265
  • 471
  • Make sure to use a WeakReference to the ImageView in your async task and check if the ImageView reference is still valid, otherwise you're holding onto a Context reference for longer than needed. Plus, there's no point in calling `setImageBitmap` if the ImageView has since been detached from the window and forgotten. – dcow Feb 25 '14 at 21:27
  • Also, why do you assert the second async task is better than the first, I wouldn't necessarily agree that it's more efficient especially if you're reloading images every time your ImageView loses a reference to one previously loaded (i.e. every config change). – dcow Feb 25 '14 at 21:31
  • Oh I think I see the nature of your assertion, the second method only invalidates the View in question and doesn't require the ListView to "reload" all the data by recreating all the views. However, you can override `notifyDataSetChanged()` to do only the operations necessary. The default just reloads the views, I believe. – dcow Feb 25 '14 at 21:33

4 Answers4

2

To answer your initial question, any adapter you write is exactly as it's called; it's an adapter. It lets you translate your data into Views displayed in an AdapterView. It would not make sense to force all views to be "loaded lazily" and, quite frankly, should not do that for a few reasons.

  1. Maybe you have prefetched all your images and don't need lazy loading. Maybe they even just come from resources and don't need to be handled in a special way at all.
  2. How would it know from where to lazily load your images (database, memory map, file, network resource, other android service, etc.)? That's your job when writing the adapter.

You might argue it would be nice for Adapter subclasses to have a loadLazily(Uri image, ImageView view) function. If so why not subclass AbsAdapter yourself and add it or submit a patch to the AOSP. I, however, doubt you will find agreement that even a function like that should be included as part of an Adapter. It's really your job to manage your data in an efficient manner, despite how normal it is to lean on the system for data management in Android.

To answer your other questions about the two methods of lazy loading you proposed, the difference is that the first method causes the AdapterView to reload all of its Views whereas in the second method you are simply invalidating the view into which you are loading the image.

I don't actually agree the second method is "better" on a whole because every time your configuration is changed or process dies and Views need to be reloaded you need to make new calls over the network or to the file system to load your images. If you cache them as you do in the first method then you can at least avoid needing to reload all the images through configuration changes. If you wrote a separate image loading service that ran in its own process (not something I'm recommending) you could also avoid the second case (of your :default process getting killed).

Point is, you're responsible for loading your images, not Android. If you want it done for you take a look at these great image loading or otherwise general resource acquisition libraries for android: Local image caching solution for Android: Square Picasso vs Universal Image Loader . I've personally used Universal-Image-Loader before and admit it works as advertised. However, it caches all your images on external storage which might not be an option for you.

Community
  • 1
  • 1
dcow
  • 7,765
  • 3
  • 45
  • 65
1

The listview can call getView for all items in its onMeasure (cf. measureHeightOfChildren in listView.java). So, with this getView, it loads all images.

To load lazily, you need to load images elsewhere: let the views ask the image when needed (onDraw) to a manager (which will load them asynchronously).

notifyDataSetChanged() with update all the listview (meaning recycling views, calling getView ...), imageView.setImageBitmap will update only the ImageView so it's a better idea.

0

use this imageloader it works IMAGELOADER

ROHIT PARMAR
  • 901
  • 15
  • 26
0

You can use this library https://github.com/thest1/LazyList. It's very easy to use.

Giancarlo
  • 745
  • 5
  • 13