reiley
reiley

Reputation: 3761

Android:More efficient solution to fetch images from internet at runtime and show in listview

Scenario: A bit tricky but interesting. I have sent a request that will give me a JSON response. This response will further contain, say 10, JSON request URLs.

I will then send request to these 10 Urls and each URL will then contain 2 image URLs. That means 20 images in total.

My intentions are to show these images in a ListView as fast as possible to the user. This is what I've done till now:

Step 1: As soon as the activity starts, pop a ProgressDialog showing loading circle as in youtube.

Step 2: IN AN ASYNC TASK, Send the first request that give URLs of those 10 JSON requests. Send this request then parse & store these 10 URLs present is response.

Step 3: Starting from first(out of 10), send the request that will give the JSON response.

Step 4: Parse this response to fetch the 2 image URLs and store in LinkedList.

Step 5: As we have the actual link of the image now, dismiss the progress dialog and fetch the image.

Step 6: This image will be stored in drawable. I've made a custom BaseAdapter called ImageAdapter that has ArrayList<Drawable> mImages;

Step 7: Store the received drawable in this ArrayList, using: adapter.mImages.add(drawable);

Step 8: Then call adapter.notifyDataSetChanged(); This will update the listview as soon the image is ready to be displayed.

Step 9: Back to step 3 to process further URLs.

Below is the code (striped all the unnecessary code):

ActivityClass

public class ImageViewer extends Activity {
    GridView gvImage;
    private ProgressDialog progressDialog;
    private final static Integer IMAGE_LIMIT = 10;
    private ImageAdapter adapter;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        try {
            setContentView(R.layout.ImageViewer);
            gvImage = (GridView) findViewById(R.id.gvImage);
            adapter = new ImageAdapter(this);
            gvImage.setAdapter(adapter);

            progressDialog = ProgressDialog.show(ImageViewer.this, "In Progress",
                    "Loading...", true, true, new DialogInterface.OnCancelListener() {

                        public void onCancel(DialogInterface dialog) {
                            finish();
                        }
                    });
            new SendRequest().execute(null);
        } catch (Exception e) {
        }
    }

    class SendRequest extends AsyncTask {
        protected Object doInBackground(Object... params) {
            try {
                HttpClient client = new DefaultHttpClient(httpParameters);
                HttpContext localContext = new BasicHttpContext();
                HttpGet httpGet = new HttpGet(BaseRequest_URL);

                JSONObject jsonResponse = null;

                HttpResponse httpResponse = client.execute(httpGet, localContext);
                if (httpResponse.getStatusLine().getStatusCode() == 200) {
                    HttpEntity entity = httpResponse.getEntity();
                    jsonResponse = getJSONObjectFromEntity(entity); //getJSONObjectFromEntity() will return JSONObject
                    processJSON(jsonResponse);
                }
                progressDialog.dismiss();
            } catch (Exception e) {
            }
            return null;
        }

        private void processJSON(JSONObject jsonResponse) throws JSONException {
            for (int i = 0; i < IMAGE_LIMIT; i++) {
                // Some JSON parsing. Output will be 'imageURL' which will be image URL
                displayImage(imageURL);
            }
        }

        public void displayImage(String uri150) {
            try {
                InputStream is = (InputStream) new URL(uri150).getContent();
                if(progressDialog.isShowing()){
                    progressDialog.dismiss();
                }
                Drawable d = Drawable.createFromStream(is, "image 150");
                adapter.mImages.add(d);
                handler.sendEmptyMessage(0); // Handler Below
            } catch (Exception e) {
            }
        }

        protected void onPostExecute(Object result) {
            super.onPostExecute(result);
        }
    }

    private Handler handler = new Handler(){
        public void handleMessage(android.os.Message msg) {
            adapter.notifyDataSetChanged();
        };
    };
}

ImageAdapter Class:

public class ImageAdapter extends BaseAdapter {
    Context context;
    public ArrayList<Drawable> mImages = new ArrayList<Drawable>();

    public ImageAdapter(Context c) {
        this.context = c;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView imageView = new ImageView(context);
        try {
            imageView.setLayoutParams(new GridView.LayoutParams(150, 150));
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imageView.setImageDrawable(mImages.get(position));
            imageView.setTag("Image" + position);
        } catch (Exception e) {
        }
        return imageView;
    }
}

Questions:

  1. The way I see it ImageAdapter is called several times. Is there any other way to reduce it.

  2. How can I optimize the code of ImageAdapter.

  3. Any other improvement?

Upvotes: 0

Views: 899

Answers (4)

Mustafa Ibrahim
Mustafa Ibrahim

Reputation: 1120

just search for Android LazyImageLoading

Good Luck

Upvotes: 0

Barış &#199;ırıka
Barış &#199;ırıka

Reputation: 1570

Try this library. It is really good for layz loading.

Upvotes: 0

inmyth
inmyth

Reputation: 9070

  1. (the first 2 questions) Adapter doesn't only control which row (view in the list) appears, it also recycles it. In your Adapter, getView creates a new view every time it is called, when it should only create a new one if the old one is not available. Check out Mark Murphy (commonsware)'s adapter here.

  2. In your code, all the images are downloaded during progress dialog. Your user will probably not see them all to the last one and it will definitely burden your memory. I suggest you lazy load both JSON and images (request your JSON, parse it, load the image one-by-one asynchronously depending on how much of the grid your users see/scroll). There have been a lot of discussions regarding image lazy loading and you should definitely start from Fedor's answer.

Upvotes: 1

Dmytro Danylyk
Dmytro Danylyk

Reputation: 19796

What I can tell you for sure it is WRONG to store your images in memory, as you can get OutOfMemoryException.

To learn how to work with images and cache visit Displaying Bitmaps Efficiently

Upvotes: 1

Related Questions