I have a ListView with a custom adapter. Every row has an ImageView that I render using a Bitmap, but my current code blocks the UI thread as I am using get()
after
You are having performance issues because you are calling the get()
method on your AsyncTask
. The get()
method basically causes the main thread to wait until the code in the AsyncTask
completes execution before the main thread continues executing other instructions. Why Google added this method is curious to say the least. So do this to fix your code.
Create a new Java class file. Name the file "DownloadImageTask" and add this code:
public interface DownloadImageListener {
void onCompletedImageDownload(Bitmap bm);
}
public class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
private static final String TAG = DownloadImageTask.class.getSimpleName();
private DownloadImageListener mListener;
private String imageUrl = "";
public DownloadImageTask(String imageUrl, DownloadImageListener listener){
this.imageUrl = imageUrl;
this.mListener = listener;
}
@Override
protected Bitmap doInBackground(String... urls) {
Bitmap bm = null;
try {
InputStream in = new java.net.URL(imageUrl).openStream();
bm = BitmapFactory.decodeStream(in);
} catch (Exception e) {
e.printStackTrace();
}
return bm;
}
protected void onPostExecute(Bitmap bm) {
mListener.onCompletedImageDownload(bm);
}
}
If you have any issues adding the public interface
to the "DownloadImageTask" Java file just create a separate Java file name "DownloadImageListener" and put the interface
code in there.
Set your code to query the AsyncTask
.
Change the Adapter
code inside your getView()
from this:
try {
Bitmap bm = new DownloadImageTask().execute(dataModel.getSpriteUrl()).get();
viewHolder.sprite.setImageBitmap(bm);
} catch (Exception e) {
e.printStackTrace();
}
to this:
try {
DownloadImageListener listener = new DownloadImageListener() {
@Override
public void onCompletedImageDownload(Bitmap bm) {
if(bm != null){
viewHolder.sprite.setImageBitmap(bm);
}
}
};
String imageUrl = dataModel.getSpriteUrl();
DownloadImageTask downloadImageTask = new DownloadImageTask(imageUrl, listener);
downloadImageTask.execute();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
This allows your AsyncTask
to execute and when the Bitmap
is returned the listener is triggered in the onPostExecute()
method sending the Bitmap
to your ListView
in the onCompletedImageDownload()
callback method.
Additional Info:
To improve performance even further you could create a caching model to save and retrieve images from the device if you have already downloaded them in the past. But that requires some really advanced techniques--and gets really tricky when images you wish to download might change from time to time.