-2

I am trying to understand the cause of the error below and how to fix it.

Only the original thread that created a view hierarchy can touch its views.

This is my code snippet.

public void refreshMyRecyclerView() {
    new Thread(new Runnable(){
        @Override
        public void run(){
            mValueList = mMyDatabase.fillValueList(); //pulls data from sqlite db
            mRecyclerAdapter = new MyRecyclerAdapter(getActivity(), mValueList);
            mRecyclerView.setAdapter(mRecyclerAdapter); //error on this line
        }
    }).start();

}

I am trying to follow good practice by performing SQLite reads off the UI thread. However I am encountering strange errors like the one above and I have no idea what's causing it even after reading similar questions involving the same error description.

George Mulligan
  • 11,813
  • 6
  • 37
  • 50
KaliMa
  • 1,970
  • 6
  • 26
  • 51

1 Answers1

2

You should only access UI components such as views from the UI thread. As you have found if you try to access them from a background thread you will get an error.

You can read the values from the database on the background thread but you should populate and set the adapter on the UI thread. If you are in an Activity you can use the method runOnUiThread(...). Otherwise if in a fragment getActivity().runOnUiThread(...) or mRecyclerView.post(...) will work too. All methods take a Runnable.

public void refreshMyRecyclerView() {
    new Thread(new Runnable(){
        @Override
        public void run(){
            mValueList = mMyDatabase.fillValueList(); //pulls data from sqlite db
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mRecyclerAdapter = new MyRecyclerAdapter(getActivity(), mValueList);
                    mRecyclerView.setAdapter(mRecyclerAdapter);
                }
            });
        }
    }).start();
}
George Mulligan
  • 11,813
  • 6
  • 37
  • 50
  • Can the answer be changed to use `getActivity().runOnUiThread` correctly? – KaliMa Apr 27 '16 at 03:52
  • I added that as an option. All the methods mentioned really just post a `Runnable` to a queue to be run on the UI thread. – George Mulligan Apr 27 '16 at 03:55
  • Sorry I mean in the code. Is the correct way just to replace `mRecyclerView.post` with `getActivity().runOnUiThread` and everything else is the same? – KaliMa Apr 27 '16 at 03:55
  • Yes everything else should be the same. [runOnUiThread()](http://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable)) also just takes a `Runnable`. – George Mulligan Apr 27 '16 at 03:57
  • I assume that this thread will (potentially) remain running even if the function ends first, right? Is there a way to force it to wait for the threads to complete before ending the function? Or maybe that defeats the purpose – KaliMa Apr 27 '16 at 03:59
  • Forcing it to wait for the thread to complete before returning from the function would be equivalent to blocking the UI thread so you would not want to do that. Why do you want to do that? There is likely a better way to achieve your goal. – George Mulligan Apr 27 '16 at 04:02
  • I display a Snackbar text after the refresh is complete -- it is theoretically possible that the text would display even though the thread has not finished refreshing yet. So I'd have to make the Snackbar wait to display only when the refresh thread has finished. This refresh method is used in other areas so I can't just put the Snackbar inside the Ui run() portion. But maybe that is outside the scope of this question. – KaliMa Apr 27 '16 at 04:03
  • To do that just add it to the runnable's `run()` method after `mRecyclerView.setAdapter(mRecyclerAdapter);`. You can call a method to show the `SnackBar` so it is reusable in the other areas of your code still. – George Mulligan Apr 27 '16 at 04:04
  • Right, but then that Snackbar would always display every single refresh whereas I am only trying to invoke it in certain situations (right now I display it after the call to refreshRecyclerView() in said situation) – KaliMa Apr 27 '16 at 04:05