James
James

Reputation: 5403

Android: async loaded bitmaps randomly don't appear in ImageView after ImageView.setBitmapDrawable(..)

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;
        }

Upvotes: 2

Views: 819

Answers (1)

Ernir Erlingsson
Ernir Erlingsson

Reputation: 2170

I think the .setImageBitmap(Bitmap) method is working fine, you are just setting it with null.

Check for the case of bitmap == null in the onPostExecute(Bitmap bitmap). This also fits to your observation that the image appears when you scroll, because Android calls getView(..) again for the visible rows at that time.

Upvotes: 1

Related Questions