Performance issue downloading images for RecyclerView

前端 未结 1 1959
执笔经年
执笔经年 2021-01-25 03:55

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

相关标签:
1条回答
  • 2021-01-25 04:23

    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.

    0 讨论(0)
提交回复
热议问题