How to use worker thread to get data from database and populate GridView

Viewed 134

I am working on a calendar app that retrieves events from database and populates them to my custom Calendar.

public class GridAdapter extends ArrayAdapter {


private LayoutInflater mInflater;
private List<Date> monthlyDates;
private Calendar currentDate;
private List<Calendar> allEvents;
ShiftManager shiftManager;
Calendar theDay = null;
Context context;
Calendar dateCal;

public GridAdapter(Context context, List<Date> monthlyDates, Calendar currentDate, List<Calendar> allEvents) {
    super(context, com.example.trackmypay.R.layout.single_cell_layout);
    this.monthlyDates = monthlyDates;
    this.currentDate = currentDate;
    this.allEvents = allEvents;
    shiftManager = new ShiftManager((FragmentActivity)context);
    mInflater = LayoutInflater.from(context);
    this.context = context;


}


@NonNull
@Override
public View getView(int position, View convertView, final ViewGroup parent) {
    Date mDate = monthlyDates.get(position);
    dateCal = Calendar.getInstance();
    dateCal.setTime(mDate);

    dateCal.set(Calendar.HOUR_OF_DAY, 0);
    dateCal.set(Calendar.MINUTE, 0);
    dateCal.set(Calendar.SECOND, 0);
    dateCal.set(Calendar.MILLISECOND, 0);


    final int dayValue = dateCal.get(Calendar.DAY_OF_MONTH);
    final int displayMonth = dateCal.get(Calendar.MONTH) + 1;
    final int displayYear = dateCal.get(Calendar.YEAR);
    final int currentMonth = currentDate.get(Calendar.MONTH) + 1;
    final int currentYear = currentDate.get(Calendar.YEAR);

    View view = convertView;


    theDay = shiftManager.retrieveShiftsForCalendar(dateCal.getTimeInMillis());


    if(view == null){
        view = mInflater.inflate(com.example.trackmypay.R.layout.single_cell_layout, parent, false);

    }



    if(displayMonth != currentMonth || displayYear != currentYear){

        TextView dayNum = view.findViewById(R.id.calendar_date_id);
        dayNum.setTextColor(Color.parseColor("#b4b5bf"));

    }


    //Add day to calendar
    TextView cellNumber = view.findViewById(R.id.calendar_date_id);
    cellNumber.setText(String.valueOf(dayValue));
    Calendar tempC = Calendar.getInstance();
    if (dayValue == tempC.get(Calendar.DAY_OF_MONTH) && currentMonth == tempC.get(Calendar.MONTH) + 1  && currentYear == tempC.get(Calendar.YEAR))
    {
        cellNumber.setTextColor(Color.parseColor("#01579B"));
        cellNumber.setTypeface(Typeface.DEFAULT_BOLD);
    }


            ImageView eventIndicator = view.findViewById(R.id.event_id);


            if (theDay != null && dayValue == theDay.get(Calendar.DAY_OF_MONTH) && displayMonth == theDay.get(Calendar.MONTH) + 1 && displayYear == theDay.get(Calendar.YEAR)) {
                eventIndicator.setImageResource(R.drawable.ic_work);
            }


        //Add events to the calendar

    return view;
}

There are basically three parts to the process for each date:

  1. Get event from database

     theDay = shiftManager.retrieveShiftsForCalendar(dateCal.getTimeInMillis());
    
  2. Set up a calendar cell

    TextView cellNumber = view.findViewById(R.id.calendar_date_id);
    cellNumber.setText(String.valueOf(dayValue));
    Calendar tempC = Calendar.getInstance();
    if (dayValue == tempC.get(Calendar.DAY_OF_MONTH) && currentMonth == tempC.get(Calendar.MONTH) + 1  && currentYear == tempC.get(Calendar.YEAR))
    {
        cellNumber.setTextColor(Color.parseColor("#01579B"));
        cellNumber.setTypeface(Typeface.DEFAULT_BOLD);
    }
    
  3. Populate an icon if event exists for this date

            ImageView eventIndicator = view.findViewById(R.id.event_id);
    if (theDay != null && dayValue == theDay.get(Calendar.DAY_OF_MONTH) && displayMonth == theDay.get(Calendar.MONTH) + 1 && displayYear == theDay.get(Calendar.YEAR)) {
                eventIndicator.setImageResource(R.drawable.ic_work);
            }
    

I wanted to use AsyncTask for this, but it's being deprecated in Android 11.

I've kinda new to threads, so I would like to see if someone can help me to correctly set it up, so a worker thread makes a call to a database, while UI thread sets up calendar cell, then once worker thread finished working getting an event from database, it's being populated to a calendar cell by UI thread

Thank you

2 Answers

Take a look at

The executor make it easy to perform the task on the background,

Executors.newSingleThreadExecutor().execute(()->{

        //Background task

});

While the Handler makes it easy to execute code in the main thread

new Handler(Looper.getMainLooper()).post(() -> {
        // Main thread code
});

Those are simple snippets, you can keep references to the actual executor instance, or the Handler instance to avoid massive code duplication.

You should check if possible the Architecture components (https://developer.android.com/topic/libraries/architecture) in Android (Room, ViewModel, LiveData) which solves this problem in a more elegant way, but it may need a full refactor of your current codebase.

The solution that ended up working for me:

    final Executor execDbCall = Executors.newCachedThreadPool();
    Runnable dbCall = new Runnable() {
        @Override
        public void run() {
            synchronized (execDbCall)
            {
                theDay = shiftManager.retrieveShiftsForCalendar(dateCal.getTimeInMillis());
                execDbCall.notify();

            }

        }
    };


    Runnable buildUI = new Runnable() {
        @Override
        public void run() {
            if(view[0] == null){
                view[0] = mInflater.inflate(com.example.trackmypay.R.layout.single_cell_layout, parent, false);

            }

            if(displayMonth != currentMonth || displayYear != currentYear){

                TextView dayNum = view[0].findViewById(R.id.calendar_date_id);
                dayNum.setTextColor(Color.parseColor("#b4b5bf"));

            }


            //Add day to calendar
            TextView cellNumber = view[0].findViewById(R.id.calendar_date_id);
            cellNumber.setText(String.valueOf(dayValue));
            Calendar tempC = Calendar.getInstance();
            if (dayValue == tempC.get(Calendar.DAY_OF_MONTH) && currentMonth == tempC.get(Calendar.MONTH) + 1  && currentYear == tempC.get(Calendar.YEAR))
            {
                cellNumber.setTextColor(Color.parseColor("#01579B"));
                cellNumber.setTypeface(Typeface.DEFAULT_BOLD);
            }



        }
    };

    Runnable setEvent = new Runnable() {
        @Override
        public void run() {
            ImageView eventIndicator = view[0].findViewById(R.id.event_id);

            if (theDay != null && dayValue == theDay.get(Calendar.DAY_OF_MONTH) && displayMonth == theDay.get(Calendar.MONTH) + 1 && displayYear == theDay.get(Calendar.YEAR)) {
                eventIndicator.setImageResource(R.drawable.ic_work);
            }

        }
    };


    ((FragmentActivity)context).runOnUiThread(buildUI);

    synchronized (execDbCall)
    {
        execDbCall.execute(dbCall);
        try {

            execDbCall.wait();

            ((FragmentActivity)context).runOnUiThread(setEvent);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

thank you djleop for putting me on a track for a solution

Related