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
How to Solve Java.Lang.Outofmemoryerror Trouble in Android
Trying to Attach a File from Sd Card to Email
How to Make Wrap_Content Work on a Recyclerview
Android Background Music Service
How to Open the "Front Camera" on the Android Platform
How to Customize Listview Using Baseadapter
Updating Google Play Services in Emulator
How to Send String from One Activity to Another
Android Fragments and Animation
How to Display a List View in an Android Alert Dialog
Android Spinner: Get the Selected Item Change Event
How to Make Ellipsize="Marquee" Always Scroll
Android Studio Add External Project to Build.Gradle
Receive Result from Dialogfragment
How to Draw Interactive Polyline on Route Google Maps V2 Android