Room: Cannot access database on the main thread since it may potentially lock the UI for a long period of time

Room: Cannot access database on the main thread since it may potentially lock the UI for a long period of time

get data from room database android
android room multiple tables
android room join query
android room query
android room transaction
android room update
android room example
room-coroutines

In the main activity, I have LiveData which contains members and a click listener. If I click on a member, then his ID is passed with intent.putExtra. That ID is later passed on to the method open in this activity. With this activity, I want to see the details of a member. In my MemberInfo activity, I marked a line where my problem lies. It shows me this error: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.

My DAO consists this code:

@Query("SELECT * FROM member_table WHERE MemberID=:id")
Member getMemberInfo(long id);

This is my main activity:

public class MemberMainActivity extends AppCompatActivity implements MemberListAdapter.MemberClickListener{

private MemberViewModel mMemberViewModel;
private List<Member> mMember;

void setMember(List<Member> members) {
    mMember = members;
}

public static final int NEW_MEMBER_ACTIVITY_REQUEST_CODE = 1;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_member);
    Toolbar toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent = new Intent(MemberMainActivity.this, NewMemberActivity.class);
            startActivityForResult(intent, NEW_MEMBER_ACTIVITY_REQUEST_CODE);
        }
    });

    RecyclerView recyclerView = findViewById(R.id.recyclerviewcard_member);
    final MemberListAdapter adapter = new MemberListAdapter(this);
    recyclerView.setAdapter(adapter);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));

    mMemberViewModel = ViewModelProviders.of(this).get(MemberViewModel.class);

    mMemberViewModel.getAllMember().observe(this, new Observer<List<Member>>() {
        @Override
        public void onChanged(@Nullable final List<Member> members) {
            mMember = members;
            // Update the cached copy of the words in the adapter.
            adapter.setMember(members);
        }
    });

}

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == NEW_MEMBER_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) {
        Member member = new Member(data.getStringExtra(NewMemberActivity.EXTRA_REPLY), data.getStringExtra(NewMemberActivity.EXTRA_REPLY2));
        mMemberViewModel.insert(member);
    } else {
        Toast.makeText(
                getApplicationContext(),
                R.string.empty_not_saved,
                Toast.LENGTH_LONG).show();
    }
}

public void onMemberClick(int position) {
    Member member = mMember.get(position);
    Intent intent = new Intent(getApplicationContext(),MemberInfo.class);
    intent.putExtra("MemberID", member.getId());
    MemberInfo.open(this, member.getId());

}

}

This is my activity:

public class MemberInfo extends AppCompatActivity {

public static void open(Activity activity, long memberid) {
    Intent intent = new Intent(activity, MemberInfo.class);
    intent.putExtra("MemberID", memberid);
    activity.startActivity(intent);
}

private List<Member> mMember;
private MemberViewModel mMemberViewModel;


void setMember(List<Member> members){
    mMember = members;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_memberinfo);

    Log.i("okay", "memberinfo");
    Intent intent = getIntent();
    if (intent != null && intent.hasExtra("MemberID")) {
        long memberid = intent.getLongExtra("MemberID", -1);
        // TODO: get customer details based on customer id
        TextView firstname = findViewById(R.id.layout_memberfirstname);
        TextView surname = findViewById(R.id.layout_membersurname);
        TextView balance = findViewById(R.id.layout_memberbalance);
        -------------Member member = MemberRoomDatabase.getDatabase().memberDao().getMemberInfo(memberid);-------------
        firstname.setText(member.getFirstname());
        surname.setText(member.getSurname());

    }
    else {
        Toast.makeText(
                getApplicationContext(),
                R.string.empty_not_saved,
                Toast.LENGTH_LONG).show();
    }
}
}

I thought that maybe it is because I'm missing a AsyncTask method. I tried this, but this also didn't work:

private static class insertMemberInfoAsyncTask extends AsyncTask<Member, Void, Void> {

    private MemberDao mAsyncTaskDao;

    insertMemberInfoAsyncTask(MemberDao dao) {
        mAsyncTaskDao = dao;
    }

    @Override
    protected Void doInBackground(Member... params) {
        Member member = params[0];
        mAsyncTaskDao.getMemberInfo(member.getId());
        return null;
    }
}

public Member getMemberInfo(long id) {
    mAllMember = mMemberDao.getAllMember();
    Member member = mMemberDao.getMemberInfo(id);
    new insertMemberInfoAsyncTask(mMemberDao).execute(member);
    return member;
}

I think I use the method wrong. Can anybody help me?


One option is to update your query to this:

@Query("SELECT * FROM member_table WHERE MemberID=:id")
LiveData<Member> getMemberInfo(long id);

(or similar, using Flowable). This avoids the need to manually create your own AsyncTask.

Returning the LiveData wrapper around the Member type automatically signals to Room that the query can/should be performed asynchronously. Per https://developer.android.com/training/data-storage/room/accessing-data (my emphasis):

Note: Room doesn't support database access on the main thread unless you've called allowMainThreadQueries() on the builder because it might lock the UI for a long period of time. Asynchronous queries—queries that return instances of LiveData or Flowable—are exempt from this rule because they asynchronously run the query on a background thread when needed.

Accessing data using Room DAOs, IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time. in case its run on the UI thread. java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time. in case its run on the UI thread.


You can use Future and Callable. So you would not be required to write a long asynctask and can perform your queries without adding allowMainThreadQueries() or using LiveData.

My dao query:-

@Query("SELECT * from user_data_table where SNO = 1")
UserData getDefaultData();

My repository method:-

public UserData getDefaultData() throws ExecutionException, InterruptedException {

    Callable<UserData> callable = new Callable<UserData>() {
        @Override
        public UserData call() throws Exception {
            return userDao.getDefaultData();
        }
    };

    Future<UserData> future = Executors.newSingleThreadExecutor().submit(callable);

    return future.get();
}

Android app example using Room database, Object-relational Mapping (ORM) database libraries, such as OrmLite, Data Access Object (DAO) At compile time, Room will verify that each SQL argument has a matching IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time. Room does not allow accessing the database on the main thread unless you called allowMainThreadQueries() on the builder because it might potentially lock the UI for a long periods of time. Asynchronous queries (queries that return LiveData or RxJava Flowable ) are exempt from this rule since they asynchronously run the query on a background thread when needed.


Using Future and Callables can be an alternative here. By using Future and Callable you can get rid of AsyncTask and forcing your queries to the main thread.

The syntax would be as follow -

@Throws(ExecutionException::class, InterruptedException::class)
private fun canContinue(id: String): UserData{

    val callable = Callable { userDao.getDefaultData() }

    val future = Executors.newSingleThreadExecutor().submit(callable)

    return future!!.get()
}

And, don't forget the null check for the data returned. Because it might be null

Getting Started With Room for Android, Cannot access database on the main thread since it may potentially lock the UI for a long period of time. at android.arch.persistence.room. Note: Room doesn't support database access on the main thread unless you've called allowMainThreadQueries() on the builder because it might lock the UI for a long period of time. Asynchronous queries—queries that return instances of LiveData or Flowable —are exempt from this rule because they asynchronously run the query on a background thread when needed.


Calling DAO on main thread issue. · Issue #6 · googlecodelabs , IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time. at android.app. java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.


Persisting Data on SQLite using Room, Cannot access database on the main thread since it may potentially lock the UI for a long period of time. This happens for two reasons: PlayerDao  Note: Room doesn't support database access on the main thread unless you've called allowMainThreadQueries() on the builder because it might lock the UI for a long period of time. Asynchronous queries - queries that return instances of LiveData or Flowable are exempt from this rule because they asynchronously run the query on a background thread when needed.


Coroutines With Room Persistence Library, In the database the best practice to put your database access into certain interfaces. We want to The room is going to give you an error at compile time.​So it goes IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long periods of time. At runtime  If we did not call this, we would see an exception indicating that we cannot access database on main thread. Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long periods of time. In the later part we will be using RxJava and we will get rid of this. For now, lets