0

i am writing a flickr client as part of a project from a book that i am reading. in it i create a subclass of HandlerThread that downloads images from flickr and then sets them in an ImageView. i set a placeholder .png earlier in the program and this binds (but with delay) however when i try to replace it nothing happens. the placeholder stays there and is not replaced. it seems like something is happening between that is preventing my HandlerThread from running properly. i have tried stepping through this with the debugger and also looking through the logcat however the logcat has been particularly unhelpful ever since the last update to android studio. it works partially if at all. so right i am getting no error message. earlier i got one stating that i had a null pointer exception but i wasnt able to see where it had come from and upon running the application again it was gone. i am posting samples of my code including the main Fragment which serves as the UI Thread, and the ThumbnailDownloader class which is my HandlerThread subclass.

public class PhotoGalleryFragment extends Fragment {
private static final String TAG = "PhotoGalleryFragment";

private RecyclerView mRecyclerView;
private List<GalleryItem> mItems = new ArrayList<>();
private ThumbnailDownloader<PhotoHolder> mThumbnailDownloader;

public static PhotoGalleryFragment newInstance(){
    return new PhotoGalleryFragment();
}

@Override
public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setRetainInstance(true);
    new FetchItemsTask().execute();

    Handler responseHandler = new Handler();
    mThumbnailDownloader = new ThumbnailDownloader<>(responseHandler);
    mThumbnailDownloader.setThumbnailDownloadListener( new ThumbnailDownloader.ThumbnailDownloadListener<PhotoHolder>() {
        @Override
        public void onThumbnailDownloaded(PhotoHolder target, Bitmap bitmap) {
            Log.i(TAG, "setThumbnailDownloadListener() called!");
            Drawable drawable = new BitmapDrawable(getResources(), bitmap);
            target.bindDrawable(drawable);

        }
    });
    mThumbnailDownloader.start();
    mThumbnailDownloader.getLooper();
    Log.i(TAG, "background thread started!");
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
    View v = inflater.inflate(R.layout.fragment_photo_gallery, container, false);

    mRecyclerView = (RecyclerView) v.findViewById(R.id.photo_recycler_view);
    mRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));

    setupAdapter();

    return v;
}

public void onDestroyView(){
    super.onDestroyView();
    mThumbnailDownloader.clearQueue();
}

@Override
public void onDestroy(){
    super.onDestroy();
    mThumbnailDownloader.quit();
    Log.i(TAG, "background thread destroyed!");
}

private void setupAdapter(){
    if (isAdded()){
        mRecyclerView.setAdapter(new PhotoAdapter(mItems));
    }
}

private class PhotoHolder extends RecyclerView.ViewHolder{
    private ImageView mItemImageView;

    public PhotoHolder(View itemView){
        super(itemView);

        mItemImageView = (ImageView) itemView.findViewById(R.id.item_image_view);
    }

    public void bindDrawable(Drawable drawable){
        mItemImageView.setImageDrawable(drawable);
    }
}//end PhotoHolder inner class

private class PhotoAdapter extends RecyclerView.Adapter<PhotoHolder>{

    private List<GalleryItem> mGalleryItems;

    public PhotoAdapter(List<GalleryItem> items){
        mGalleryItems = items;
    }

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

    @Override
    public void onBindViewHolder(PhotoHolder photoHolder, int position) {
        GalleryItem galleryItem = mGalleryItems.get(position);
        Drawable placeholder = getResources().getDrawable(R.drawable.wait); <--this is the placeholder image
        photoHolder.bindDrawable(placeholder);//this <--this sets the placeholder but seems to wait for the thumbnail download to complete for some reason

        mThumbnailDownloader.queueThumbnail(photoHolder, galleryItem.getUrl());
    }

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

private class FetchItemsTask extends AsyncTask<Void, Void, List<GalleryItem>>{

    @Override
protected List<GalleryItem> doInBackground(Void... params) {
   return new FlickrFetchr().fetchItems();
}

@Override
protected void onPostExecute(List<GalleryItem> items){
    mItems = items;
    setupAdapter();
}
}//end FetchItemsTask inner class

}//end class

and here is my HandlerThread implementation:

public class ThumbnailDownloader<T> extends HandlerThread {
private static final String TAG = "ThumbnailDownloader";
private static final int MESSAGE_DOWNLOAD = 0;

private boolean mHasQuit = false;
private Handler mRequestHandler = new Handler();
private Handler mResponseHandler = new Handler();
private ThumbnailDownloadListener<T> mThumbnailDownloadListener;
private ConcurrentMap<T, String> mRequestMap = new ConcurrentHashMap<>();

public interface ThumbnailDownloadListener<T>{
    void onThumbnailDownloaded(T target, Bitmap bitmap);
}

public void setThumbnailDownloadListener(ThumbnailDownloadListener<T> listener){
    mThumbnailDownloadListener = listener;
}

public ThumbnailDownloader(Handler responseHandler){
    super(TAG);
    mResponseHandler = responseHandler;
    Log.i(TAG, "ThumbnailDownloader created!");
}

@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 a url:" + mRequestMap.get(target));
                handleRequest(target);
            }
        }
    };
}

@Override
public boolean quit(){
    mHasQuit = true;
    return super.quit();
}

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);
        mRequestHandler.obtainMessage(MESSAGE_DOWNLOAD, target).sendToTarget();
    }

}

public void clearQueue(){
    mRequestHandler.removeMessages(MESSAGE_DOWNLOAD);
    mRequestMap.clear();
}

private void handleRequest(final T target){
    try{
        final String url = mRequestMap.get(target);

        if (url == null){
            return;
        }
        byte[] bitmapBytes = new FlickrFetchr().getUrlBytes(url);
        final Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
        Log.i(TAG, "Bitmap created!"); //<---this is output in the logcat at the terminal but not the android monitor
        mResponseHandler.post(new Runnable(){

            @Override
            public void run() {
                if (mRequestMap.get(target) != null || mHasQuit){
                    return;
                }
                mRequestMap.remove(target);
                mThumbnailDownloadListener.onThumbnailDownloaded(target, bitmap);
            }
        });
    }catch (IOException ioe){
        Log.e(TAG, "error downloading image: ", ioe);
    }
}

}

honestly at this point i am not even sure where the problem is. it doesn't seem like queueThumbnail ever runs. but just setting the placeholder wait.png takes so long that it almost seems like there is some mix up between the placeholder and the downloaded image when it comes to binding to the ViewHolder. i am just not sure where it could be. i added comments to point this out.

Mox_z
  • 501
  • 7
  • 30
  • why are you extending `HandlerThread`? why dont you simply do something like [this](https://stackoverflow.com/a/25096981/2252830) (see UPDATE part of it for two way `Message` passing) – pskink Jun 04 '17 at 06:17
  • @pskink I guess the only reason I'm extending handlerthread is because this is how the book says to do it. Also I was working on this before and was able to get it working successfully but then experienced data loss and had to start over. I guess what I'm saying is that I the way you suggested makes sense but I know that the way in the book also works and want to do it that way. Just can not find out where I am making the mistake. Do you see anything that stands out as a mistake to you – Mox_z Jun 04 '17 at 13:53

0 Answers0