0

I once came across this AppExecutors class from some github sample project but I forgot where. I just found it very handy as it does multithreading for me. But I'm not sure if I'm executing the code in my Fragment correctly in the following case:

  1. Initialise a recyclerView with empty ArrayList.
  2. Run query to database in DiskIO thread.
  3. Then update adapter's data and the view in Main thread.

Do I need to explicitly wrap the adapter.refreshData(list); part with the Main Thread Runnable like what I do below?

AppExecutors.java

import android.os.Handler;
import android.os.Looper;

import androidx.annotation.NonNull;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class AppExecutors {

    private final Executor mDiskIO;

    private final Executor mNetworkIO;

    private final Executor mMainThread;

    private static volatile AppExecutors mInstance;

    private AppExecutors(Executor diskIO, Executor networkIO, Executor mainThread) {
        this.mDiskIO = diskIO;
        this.mNetworkIO = networkIO;
        this.mMainThread = mainThread;
    }

    public static AppExecutors getInstance() {
        if (mInstance == null) {
            synchronized (AppExecutors.class) {
                mInstance = new AppExecutors();
            }
        }
        return mInstance;
    }

    private AppExecutors() {
        this(Executors.newSingleThreadExecutor(), Executors.newFixedThreadPool(3),
                new MainThreadExecutor());
    }

    public Executor diskIO() {
        return mDiskIO;
    }

    public Executor networkIO() {
        return mNetworkIO;
    }

    public Executor mainThread() {
        return mMainThread;
    }

    public static void xDisk(@NonNull Runnable command) {
        getInstance().diskIO().execute(command);
    }
    public static void xNet(@NonNull Runnable command) {
        getInstance().networkIO().execute(command);
    }
    public static void xMain(@NonNull Runnable command) {
        getInstance().mainThread().execute(command);
    }

    private static class MainThreadExecutor implements Executor {
        private Handler mainThreadHandler = new Handler(Looper.getMainLooper());

        @Override
        public void execute(@NonNull Runnable command) {
            mainThreadHandler.post(command);
        }
    }
}

SomeFragment.java

...
void setUpRecyclerView() {
        TimeAllowedAdapter adapter = new TimeAllowedAdapter(getContext(), new ArrayList<>());
        rcView.setAdapter(adapter);
        rcView.setLayoutManager(new LinearLayoutManager(getContext()));
        // then fetch data and refresh list
        AppExecutors.xDisk(() -> {
            List<TimeAllowed> list = AppDatabase.getDatabase(getContext()).timeAllowedDao().getAllTimeAllowed();
            AppExecutors.xMain(() -> {
                adapter.refreshData(list);
            });
        });
    }
...

1 Answers1

0

In one word, yes. You need to Notify your Adapter that your Data has changed, which in this situation is Changing after you are querying your Database.

Also, as a suggestion, try looking into invalidate and notifyDataSetChanged of the Adapter Class.

Also, you can check out this Answer to know more about notifying the Adapter for Dataset Changes. But again, even if we are using Threads(), the code for notifying is executed in the UIThread, which is nothing but the Main thread.

Hope this solves your question.

Aayush Sinha
  • 377
  • 4
  • 18