Reputation: 3761
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:
The way I see it ImageAdapter
is called several times. Is there any other way to reduce it.
How can I optimize the code of ImageAdapter.
Any other improvement?
Upvotes: 0
Views: 899
Reputation: 9070
(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.
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
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