javaandroidandroid-fragmentsgridviewandroid-handlerthread

Loss of messages in HandlerThread


I have a class that extends a HandlerThread class for creating a bitmap from photo on the device in GridView when it's needed but from time to time instead of creating a bitmap of photo it fils a GridView item(ImageView) with nothing and i cant understand why this is happening. Help please =)

HandlerThread class:

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

private Handler mHandler;
private Map<Token, String> requestMap =
        Collections.synchronizedMap(new HashMap<Token, String>());
private Handler mResponseHandler;
private Listener<Token> mListener;

public interface Listener<Token>{
    void onThumbnailDownloaded(Token token, Bitmap thumbnail);
}

public void setListener(Listener<Token> listener){
    mListener = listener;
}

public ThumbnailDownloader(Handler responseHandler){
    super(TAG);
    mResponseHandler = responseHandler;
}

public void queueThumbnail(Token token, String path){
    Log.i(TAG, "Got an PATH: " + path);
    requestMap.put(token, path);

    mHandler
            .obtainMessage(MESSAGE_CREATE_BITMAP, token)
            .sendToTarget();
}

private void handleRequest(final Token token){
    final String path = requestMap.get(token);
    final Bitmap bitmap = PictureUtils.getScaledBitmap(path,
            GalleryFragment.getImageViewWidth(),
            GalleryFragment.getImageViewHeight());

    mResponseHandler.post(new Runnable() {
        @Override
        public void run() {
            if (requestMap.get(token) != path){
                return;
            }
            requestMap.remove(token);
            mListener.onThumbnailDownloaded(token, bitmap);
        }
    });
}

@Override
protected void onLooperPrepared() {
    mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MESSAGE_CREATE_BITMAP){
                Token token = (Token) msg.obj;
                Log.i(TAG, "Got a request for drawable: " + requestMap.get(token));
                handleRequest(token);
            }
        }
    };
}

public void clearQueue() {
    mHandler.removeMessages(MESSAGE_CREATE_BITMAP);
    requestMap.clear();
}

}

GridView class:

public class GalleryFragment extends Fragment {
private ArrayList<String> mPathList;
private GalleryHolderAdapter mAdapter;
private GridView mGridView;
private PhotoList mList;
private static int imageViewHeight;
private static int imageViewWidth;

private ThumbnailDownloader<ImageView> mThumbnailThread;
private static final  String TAG = "GalleryFragment";

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mThumbnailThread = new ThumbnailDownloader<ImageView>(new Handler());

    mThumbnailThread.setListener(new ThumbnailDownloader.Listener<ImageView>() {
        @Override
        public void onThumbnailDownloaded(ImageView imageView, Bitmap thumbnail) {
            if (isVisible()) {
                imageView.setImageBitmap(thumbnail);
            }
        }
    });

    mThumbnailThread.start();
    mThumbnailThread.getLooper();
}

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

    mPathList = new PhotoList().getPathList();
    mList = new PhotoList();

    mGridView = (GridView) view.findViewById(R.id.gridView);

    mAdapter = new GalleryHolderAdapter(getActivity(),mPathList);

    mGridView.setAdapter(mAdapter);

    FloatingActionButton fab = (FloatingActionButton) view.findViewById(R.id.button_add_photo);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            FragmentManager manager = getFragmentManager();
            manager.beginTransaction()
                    .replace(R.id.main_content_frame, new CameraFragment())
                    .commit();
        }
    });

    mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            Intent intent = new Intent(getActivity(), PhotoPagerActivity.class);
            intent.putExtra("PATH_PAGER", mPathList.get(position));
            startActivity(intent);
        }
    });

    return view;
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    imageViewHeight = mAdapter.imageViewMaxHeight;
    imageViewWidth = mAdapter.imageViewMaxWidth;

    System.gc();
}

public static int getImageViewHeight(){
    return imageViewHeight;
}

public static int getImageViewWidth() {
    return imageViewWidth;
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    mThumbnailThread.clearQueue();
}

@Override
public void onDestroy() {
    super.onDestroy();
    mThumbnailThread.quit();
}

public class GalleryHolderAdapter extends BaseAdapter {
    private Context mContext;
    private ArrayList<String> mPathList;
    private ViewHolder holder;
    public int imageViewMaxHeight;
    public int imageViewMaxWidth;

    public GalleryHolderAdapter(Context context, ArrayList<String> items) {
        mContext = context;
        mPathList = items;
    }

    @Override
    public int getCount() {
        return mPathList.size();
    }

    @Override
    public Object getItem(int position) {
        return mPathList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {

        if (convertView == null){
            LayoutInflater inflater = LayoutInflater.from(mContext);
            convertView = inflater.inflate(R.layout.gallary_item, parent, false);

            holder = new ViewHolder();
            holder.mImageView = (ImageView) convertView.findViewById(R.id.gallary_item_imageView);
            holder.mLocationImage = (ImageView) convertView.findViewById(R.id.location_enabler_image_view);

            convertView.setTag(holder);
        }else {
            holder = (ViewHolder) convertView.getTag();
        }

        imageViewHeight = holder.mImageView.getMaxHeight();
        imageViewWidth = holder.mImageView.getMaxWidth();

        mThumbnailThread.queueThumbnail(holder.mImageView, mPathList.get(position));

        return convertView;
    }

    public class ViewHolder {
        ImageView mImageView;
        ImageView mLocationImage;
    }
}

}

And a class that makes a bitmap:

public class PictureUtils {
private static final String TAG = "PictureUtils";

    public static Bitmap getScaledBitmap
            (String path, int bestWidth, int bestHeight){
        // Чтение размеров изображения на диске
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;

        float srcWidth = options.outWidth;
        float srcHeight = options.outHeight;

        int inSampleSize = 1;
        if(srcHeight > bestHeight || srcWidth > bestWidth){
            if(srcWidth > srcHeight){
                inSampleSize = Math.round(srcHeight / bestHeight);
            }else {
                inSampleSize = Math.round(srcWidth / bestWidth);
            }
        }
        options = new BitmapFactory.Options();
        options.inSampleSize = inSampleSize;
        options.inPreferredConfig = Bitmap.Config.RGB_565;

        return BitmapFactory.decodeFile(path, options);
    }

public static Bitmap createDrawableFromView(Activity activity, View view) {
    DisplayMetrics displayMetrics = new DisplayMetrics();
    activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    view.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
    view.measure(displayMetrics.widthPixels, displayMetrics.heightPixels);
    view.layout(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels);
    view.buildDrawingCache();
    Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);

    Canvas canvas = new Canvas(bitmap);
    view.draw(canvas);

    return bitmap;
}

    public static void cleanImageView(ImageView view){
        if (!(view.getDrawable() instanceof BitmapDrawable))
            return;
        // Стирание изображения для экономии памяти
        BitmapDrawable drawable = (BitmapDrawable) view.getDrawable();
        drawable.getBitmap().recycle();
        view.setImageDrawable(null);
        System.gc();
    }
}

Solution

  • Try using a thread instead of extending HandlerThread:

    public (abstract if you want to extend and add something on top) class WorkerThread extends Thread {
    
        private static final String TAG = WorkerThread();
        private List<WorkTask> syncQueue = new ArrayList< WorkTask >();
        private boolean clearQueue = false;
        public WorkerThread() {
    
        }
    
    
        public void stopThread(boolean clear) {
            clearQueue = clear;
            this.stopWorker = true;
        }
    
        public void addTask(WorkerTask task) {
            synchronized (syncQueue) {
                if (task != null && !getSynQueue().contains(task)) {
                    getSynQueue().add(task);
                }
            }
        }
    
        public List<WorkerTask> getSynQueue() {
            return this.syncQueue;
        }
    
        @Override
        public void run() {
            while (!stopWorker) {
                WorkerTask task = null;
                synchronized (syncQueue) {
                    if (!getSynQueue().isEmpty()) {
                        task = getSynQueue().get(0);
                    }
                }
                if (task != null) {
                    try {
                        task.run();
                        synchronized (syncQueue) {
                            if (!getSynQueue().isEmpty()) {
                                getSynQueue().remove(task);
                                //notify something/someone
                            }
                        }
                    } catch (Exception e) {
                        Log.e(TAG, "Error in running the task." + e.getMessage());
                        synchronized (syncQueue) {
                           //again u can notify someone
                        }
                    } finally {
                        //here you can actually notify someone of success
                    }
                }
            }
            if(clearQueue){
               getSynQueue().clear();
            }
        }
    }