Challenge : Preloading and Caching - preloading seize the network

Hi everyone, i complete the the first challenge, and i use LruCache to cache bitmap, downloaded by FlickrFetcher().getUrlBytes().Then I check LruCache in queueThumbnail() method, and I call onThumbnailDownloaded if bitmap is not null.

    public void queueThumbnail(T target, String url) {
        Log.i(TAG, "Got a URL: " + url);
        if (url == null) {
            mRequestMap.remove(target);
        } else {
            mRequestMap.put(target, url);
            Bitmap bitmap;
            if ((bitmap = mCaches.get(url)) != null) {
                if (mThumbnailDownloadListener != null) {
                    mThumbnailDownloadListener.onThumbnailDownloaded(target, bitmap);
                }
                Log.i(TAG, "Got a URL: " + url + " from cache");
                return;
            }
            mRequestHandler.obtainMessage(MESSAGE_DOWNLOAD, target).sendToTarget();
        }
    }

But I have some problem for the second challenge. I extract the part of download image to a method called getBitmap():

    private Bitmap getBitmap(String url) {
        try {
            byte[] bytes = new FlickrFetcher().getUrlBytes(url);
            Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
            mCaches.put(url, bitmap);
            Log.i(TAG, "Bitmap created");
            return bitmap;
        } catch (IOException e) {
            Log.e(TAG, "Error downloading image", e);
            e.printStackTrace();
        }
        return null;
    }

Then, I add a method called preLoad to send MESSAGE_PREDOWNLOAD Message to mRequestHandler, the request handler invoke getBitmap() and add downloaded bitmap to lrucache:

    public void preLoad(String url) {
        if (url == null) {
            return;
        }
        if (mCaches.get(url) == null) {
            mRequestHandler.obtainMessage(MESSAGE_PREDOWNLOAD, url).sendToTarget();
        }
    }

    @Override
    protected void onLooperPrepared() {
        mRequestHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                if (msg.what == MESSAGE_DOWNLOAD) {
                    T target = (T) msg.obj;
                    Log.i(TAG, "Got a request for URL: " + mRequestMap.get(target));
                    handRequest(target);
                } else if (msg.what == MESSAGE_PREDOWNLOAD) {
                    getBitmap((String) msg.obj);
                }
            }
        };
    }

Lastly, I add the content as follows in PhotoGalleryFragment:

    class FetchItemsTask extends AsyncTask<Void, Void, List<GalleryItem>> {
        @Override
        protected List<GalleryItem> doInBackground(Void... voids) {
            return new FlickrFetcher().fetchItems();
        }

        @Override
        protected void onPostExecute(List<GalleryItem> galleryItems) {
            mGalleryItems = galleryItems;
            preLoad(0, 10);
            setupAdapter();
        }
    }

    private void preLoad(int start, int count) {
        start = start < 0 ? 0 : start;
        count = count < mGalleryItems.size() ? count : mGalleryItems.size();
        for (int i = start; i < count; i++) {
            mThumbnailDownloader.preLoad(mGalleryItems.get(i).getUrl());
        }
    }


    class PhotoHolder extends RecyclerView.ViewHolder {
        ImageView mImageView;

        public PhotoHolder(View itemView) {
            super(itemView);
            mImageView = itemView.findViewById(R.id.item_image_view);
        }

        public void bind(Drawable drawable) {
            mImageView.setImageDrawable(drawable);
        }
    }

    class PhotoAdapter extends RecyclerView.Adapter<PhotoHolder> {

        private List<GalleryItem> mItems;

        public PhotoAdapter(@NonNull List<GalleryItem> items) {
            this.mItems = items;
        }

        @NonNull
        @Override
        public PhotoHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View itemView = LayoutInflater.from(getActivity()).inflate(R.layout.list_item_gallery, parent, false);
            return new PhotoHolder(itemView);
        }

        @Override
        public void onBindViewHolder(@NonNull PhotoHolder holder, int position) {
            GalleryItem item = mGalleryItems.get(position);
            Drawable placeHolder = getResources().getDrawable(R.drawable.img_placeholder);
            holder.bind(placeHolder);
            mThumbnailDownloader.queueThumbnail(holder, item.getUrl());
            preLoad(position - 10, position);
            preLoad(position, position + 10);
        }

        @Override
        public int getItemCount() {
            return mItems.size();
        }
    }

I add preLoad(int start, int count) method , and invoke it in two place: onPostExecute and onBindViewHolder.But I get the worse result than before, because the behavior of preload occupied network resource, resulting in the normal item that need display can’t download image quickly.
So how I can do to solve this problem.

There is no limit of preLoad method called in onBindViewHolder, that will bring about thread busy.So you can set a condition on GalleryItem, that limit the process of preload to every GalleryItem only once.

like this:

private void preLoad(int start, int count) {
start = start > 0 ? start : 0;
count = count < mItems.size() ? count : mItems.size();
for (int i = start; i < count; i++) {
if (!mItems.get(i).isPreDownload()) {
mItems.get(i).setIsPreDownload(true);
mThumbnailDownloader.preLoad(mItems.get(i).getUrl());
}
}
}