Android Room - Simple Select Query - Cannot Access Database on the Main Thread

Android Room - simple select query - Cannot access database on the main thread

Database access on main thread locking the UI is the error, like Dale said.

--EDIT 2--

Since many people may come across this answer...
The best option nowadays, generally speaking, is Kotlin Coroutines. Room now supports it directly (currently in beta).
https://kotlinlang.org/docs/reference/coroutines-overview.html
https://developer.android.com/jetpack/androidx/releases/room#2.1.0-beta01

--EDIT 1--

For people wondering... You have other options.
I recommend taking a look into the new ViewModel and LiveData components. LiveData works great with Room.
https://developer.android.com/topic/libraries/architecture/livedata.html

Another option is the RxJava/RxAndroid. More powerful but more complex than LiveData.
https://github.com/ReactiveX/RxJava

--Original answer--

Create a static nested class (to prevent memory leak) in your Activity extending AsyncTask.

private static class AgentAsyncTask extends AsyncTask<Void, Void, Integer> {

//Prevent leak
private WeakReference<Activity> weakActivity;
private String email;
private String phone;
private String license;

public AgentAsyncTask(Activity activity, String email, String phone, String license) {
weakActivity = new WeakReference<>(activity);
this.email = email;
this.phone = phone;
this.license = license;
}

@Override
protected Integer doInBackground(Void... params) {
AgentDao agentDao = MyApp.DatabaseSetup.getDatabase().agentDao();
return agentDao.agentsCount(email, phone, license);
}

@Override
protected void onPostExecute(Integer agentsCount) {
Activity activity = weakActivity.get();
if(activity == null) {
return;
}

if (agentsCount > 0) {
//2: If it already exists then prompt user
Toast.makeText(activity, "Agent already exists!", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(activity, "Agent does not exist! Hurray :)", Toast.LENGTH_LONG).show();
activity.onBackPressed();
}
}
}

Or you can create a final class on its own file.

Then execute it in the signUpAction(View view) method:

new AgentAsyncTask(this, email, phone, license).execute();

In some cases you might also want to hold a reference to the AgentAsyncTask in your activity so you can cancel it when the Activity is destroyed. But you would have to interrupt any transactions yourself.

Also, your question about the Google's test example...
They state in that web page:

The recommended approach for testing your database implementation is
writing a JUnit test that runs on an Android device. Because these
tests don't require creating an activity, they should be faster to
execute than your UI tests.

No Activity, no UI.

Cannot access database on the main thread since it may potentially lock the UI for a long period of time' Also, I Accessed room using coroutine

I replaced this funtion -

fun resetAllAccess(){
viewModelScope.launch {
passwordDao.resetAccessForAll()
}
}

-with

fun resetAllAccess(){
CoroutineScope(Dispatchers.IO).launch {
passwordDao.resetAccessForAll()
}
}

so now, it is no longer running on the main thread.

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

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.

Android Room - Cannot access database on the main thread

Try to switch context in RadioRepository:

suspend fun insert(radio: Radio) = withContext(Dispatchers.IO) {
radioDao.insert(radio)
}

You are using @Suppress("RedundantSuspendModifier") annotation, which suppresses the RedundantSuspendModifier error. This error means your insert function is non suspend and will be running in thread, which invoked it.

withContext(Dispatchers.IO) switches the context of coroutine, making insert function to run in background(worker) thread.

Cannot access database on the main thread since it may potentially lock the UI for a long period of time. error on my Coroutine

The error says that it should NOT run on the main thread. Database operations (and every other form of IO) can take a long time and should be run in the background.

You should use Dispatchers.IO which is designed for running IO operations.

Why do I get 'Cannot access database on the main thread since it may potentially lock the UI for a long period of time.' error in Android Studio?

Option A uses viewModelScope.launch. The default dispatcher for viewModelScope is Dispatchers.Main.immediate as per the documentation. As add isn't a suspending method, it runs on that dispatcher directly - i.e., it runs on the main thread.

Option B uses viewModelScope.launch(Dispatchers.IO) which means the code runs on the IO dispatcher. As this isn't the main thread, it succeeds.

Option C makes add a suspending function. As per the Async queries with Kotlin coroutines guide, this automatically moves the database access off of the main thread for you, no matter what dispatcher you are using. Option C is always the right technique to use when using Room + Coroutines

Insert data with room from view : Cannot access database on the main thread

You have to make your DAO methods suspend, so they don't block the UI thread

@Dao
interface AccountConfigurationDao {
@Query("SELECT * FROM accountconfiguration LIMIT 1")
fun get(): Flow<AccountConfiguration>

@Query("DELETE FROM accountconfiguration")
suspend fun clear() //make this suspend

@Insert
suspend fun insert(account_configuration: AccountConfiguration) //make this suspend
}

I've tried your github code, and you've to uncomment implementation "androidx.room:room-runtime:$room_version".

I think there's a bug in Room 2.3.0 as it's giving Not sure how to handle query method's return type (java.lang.Object). error, on adding suspend keyword to DAO. You should use Room 2.4.0-beta02

def room_version = "2.4.0-beta02"
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"

Cannot access database on the main thread - Android Room - Using ThreadPoolExecutor

You can change your implementation to:

private void refresh() {

if (!dataSource.hasData()) {
recipeService.getAllRecipes().enqueue(new Callback<List<Recipe>>() {
@Override
public void onResponse(Call<List<Recipe>> call, Response<List<Recipe>> response) {

executor.execute(() -> {
dataSource.save(response.body());
});
}

@Override
public void onFailure(Call<List<Recipe>> call, Throwable t) {
throw new RuntimeException(t);
}
});
}
}

The onResponse function is always called in the UIThread.

Cannot access database on the main thread since it may potentially lock the UI for a long period of time error on lauching coroutine

You have to mark your Dao function as a suspend function if you want Room to run it on a background thread. Otherwise all you're doing is calling a synchronous function from a coroutine scope.

@Query("UPDATE word_table SET shown = 1")
suspend fun turnAllWordsOn()

As a side note, suspend functions don't automatically run on a background thread, however Room does the necessary work behind the scenes when you mark a query as suspend.



Related Topics



Leave a reply



Submit