I have a ListView whose elements are photos I need to load from a remote API which I'm using loopj to access.
After loopj downloads the photo data, I use an AsyncTask to process the data into a scaled bitmap.
This usually works great, however..
The problem
In my AsyncTask's onPostExecute method, I call setImageBitmap on the ImageView. Occasionally, the bitmap simply doesn't appear in the ImageView after ImageView.setImageBitmap is called. I have verified through logs and also the debugger that ImageView.setImageBitmap is indeed called on the offending ImageView.
If I scroll the list a bit, the list element redraws, and the image is loaded from memory cache and correctly appears on the ImageView.
Here's my code:
public void setPersonImage(final ImageView personImageView, final Person p) {
personImageView.setImageResource(R.drawable.empty_image);
//kills unecessary requests for the imageview's previous occupant photo
if(personImageView.getTag() != null) {
AsyncHttpClient client = mPhotoRequestsMap.get(p.getEmployeeId());
if(client != null) {
client.cancelRequests(getActivity().getApplicationContext(), true);
Log.d(TAG, "Cancled photo request for " + String.valueOf(p.getEmployeeId()));
}
}
personImageView.setTag(p.getEmployeeId());
//first attempt to grab photo from cache
Bitmap cachedBitmap = mMemoryCache.get(p.getPhotoCacheKey());
if(cachedBitmap != null) {
personImageView.setImageBitmap(cachedBitmap);
Log.d(TAG, "bitmap for " + String.valueOf(p.getEmployeeId()) + " retrieved from memory cache");
} else {
AsyncHttpClient client = ApiWrapper.getPersonImage(180,mUserSession,
getActivity().getApplicationContext(), p.getEmployeeId(),
new MyAsyncHttpResponseHandler(this) {
@Override
public void onSuccess(String response) {
if(personImageView.getTag().equals(p.getEmployeeId())) {
// use async task to process the bitmap off the UI thread
AsyncTask<String,Integer,Bitmap> task = new AsyncTask<String,Integer,Bitmap>() {
@Override
protected Bitmap doInBackground(String... params) {
String response = params[0];
byte[] bitmapBytes = ApiWrapper.processImageResponse(response);
Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
Bitmap bmpScaled = ThumbnailUtils.extractThumbnail(bitmap, 180, 180);
return bmpScaled;
}
protected void onPostExecute(Bitmap bitmap) {
//set on imageview if tags agree
if(personImageView.getTag().equals(p.getEmployeeId())) {
Log.d(TAG, "setting image view for " + p.getEmployeeId());
personImageView.setImageBitmap(bitmap);
mMemoryCache.put(p.getPhotoCacheKey(), bitmap);
}
else {
Log.d(TAG, "tags don't match after decode bitmap, discarding...");
}
}
};
task.execute(response);
}
else {
Log.d(TAG, "tags don't match BEFORE decode bitmap employee id: " + p.getEmployeeId() + " tag: " + personImageView.getTag());
}
}
}
);
//create entry in PhotoRequestsMap table
mPhotoRequestsMap.put(p.getEmployeeId(), client);
} // end if not in cache
}
I've tried calling invalidate() on the ImageView after the bitmap is set on it, but sadly that doesn't solve it..
I'd greatly appreciate any help or pointers.
Update
The getView method from my Adapter:
public View getView(int position, View convertView, ViewGroup parent) {
// If we weren't given a view, inflate one
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(R.layout.list_item_person, null);
}
Person p = getItem(position);
TextView nameTextView = (TextView)convertView.findViewById(R.id.person_ListItemNameTextView);
nameTextView.setText(p.getFirstName() + " " + p.getLastName());
TextView idTextView = (TextView)convertView.findViewById(R.id.person_ListItemIdTextView);
idTextView.setText(String.valueOf(p.getEmployeeId()));
ImageView personImageView = (ImageView)convertView.findViewById(R.id.person_ListItemImageView);
setPersonImage(personImageView, p);
return convertView;
}