0

So I've made this database, and for some reasone I get an error message: Cannot access database on the main thread since it may potentially lock the UI for a long period of time. The log says that the error is in: wordList = (ArrayList<Note>) wDb.noteDao().getAllNotes();, but I can't see what I did wrong?

ListActivity.java

    private NoteDatabase wDb;
    ArrayList<Note> wordList;


    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        s1 = getResources().getStringArray(R.array.PL);//Kald denne først
        startService(new Intent(ListActivity.this,WordLeanerService.class).putExtra("inputExtra", s1));
        setupConnectionToWLservice();

        LoadData(); //Load SharedPreferences
        setContentView(R.layout.activity_main);
        s2 = getResources().getStringArray(R.array.DC);
        s3 = getResources().getStringArray(R.array.DC2);

        recyclerView = findViewById(R.id.recyclerView);
        Exit = findViewById(R.id.CloseApp);
        editSearch = findViewById(R.id.EditText);
        searchBtn = findViewById(R.id.search_button);
        getData(); //get set data from EditActivity and set Arrays

        wDb = NoteDatabase.getInstance(getApplicationContext());
        wordList = (ArrayList<Note>) wDb.noteDao().getAllNotes();
        if(wordList.isEmpty()){
            for (int i = 0; i < images.length; i++) {
                Note nWord = new Note(s1[i], s3[i], s2[i], images[i], items[i], Notes[i]);
                wDb.noteDao().insert(nWord);
            }
        }
    }

Note.java

package com.example.word_learner_app;

import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity(tableName = "note_table")
public class Note {
    @PrimaryKey(autoGenerate = true)
    private int id;
    @ColumnInfo(name = "wordTitle")
    private String title;
    @ColumnInfo(name = "description")
    private String description;
    @ColumnInfo(name = "prounciation")
    private String prounciation;
    @ColumnInfo(name = "image")
    private int Image;
    @ColumnInfo(name = "rating")
    private String rating;
    @ColumnInfo(name = "notes")
    private String notes;

    public Note(String title, String description, String prounciation, int Image, String rating, String notes) {
        this.title = title;
        this.description = description;
        this.prounciation = prounciation;
        this.Image = Image;
        this.rating = rating;
        this.notes = notes;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public String getDescription() {
        return description;
    }

    public String getProunciation() {
        return prounciation;
    }

    public int getImage() {
        return Image;
    }

    public String getRating() {
        return rating;
    }

    public String getNotes() {
        return notes;
    }
}

NoteDao.java

package com.example.word_learner_app;

import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;

import java.util.List;

@Dao
public interface NoteDao {
    @Insert
    void insert(Note note);

    @Update
    void update(Note note);

    @Delete
    void delete(Note note);

    @Query("SELECT * FROM note_table")
    List<Note> getAllNotes();
}

NoteDatabase.java

package com.example.word_learner_app;

import android.content.Context;

import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;

@Database(entities = {Note.class}, version = 1, exportSchema = false)
public abstract class NoteDatabase extends RoomDatabase {

    private static NoteDatabase instance;

    public abstract NoteDao noteDao();

    public static synchronized NoteDatabase getInstance(Context contexte){
        if(instance == null){
            instance = Room.databaseBuilder(contexte.getApplicationContext(),
                    NoteDatabase.class, "note_database")
                    .fallbackToDestructiveMigration()
                    .build();
        }
        return instance;
    }
}

Logcat

2020-03-27 16:40:06.697 18007-18007/? E/ord_learner_ap: Unknown bits set in runtime_flags: 0x8000
2020-03-27 16:40:08.226 18007-18007/com.example.word_learner_app E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.word_learner_app, PID: 18007
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.word_learner_app/com.example.word_learner_app.ListActivity}: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3270)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
     Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
        at androidx.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:267)
        at androidx.room.RoomDatabase.query(RoomDatabase.java:323)
        at androidx.room.util.DBUtil.query(DBUtil.java:83)
        at com.example.word_learner_app.NoteDao_Impl.getAllNotes(NoteDao_Impl.java:158)
        at com.example.word_learner_app.ListActivity.onCreate(ListActivity.java:67)
        at android.app.Activity.performCreate(Activity.java:7802)
        at android.app.Activity.performCreate(Activity.java:7791)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016) 
        at android.os.Handler.dispatchMessage(Handler.java:107) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.app.ActivityThread.main(ActivityThread.java:7356) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) 
Mads
  • 61
  • 1
  • 8
  • The error message is pretty straightforward: do not do database I/O on the main application thread. If you wish to keep your DAO as it is, call your DAO methods on a background thread. If you are willing to change your DAO, consider using `LiveData` or RxJava, which will cause Room to arrange to do your database I/O on background threads. Long-term, as Biscuit suggests, you might consider coroutines if/when you start programming in Kotlin. – CommonsWare Mar 27 '20 at 15:52

4 Answers4

0

What you're trying to do is unauthorised in Android, you're trying to fetch data in the database from the MainThread which can cause a freeze of the application. That's why it is advise to use Coroutines to do such functions.

EDIT: In java you can use callback as in this codelabs

Biscuit
  • 4,840
  • 4
  • 26
  • 54
0

Use allowMainThreadQueries() to allow main thread queries for room by default the flag is false else u need to insert in back ground thread.

So to insert data synchornously use this with the builder.

instance = Room.databaseBuilder(contexte.getApplicationContext(),
                    NoteDatabase.class, "note_database")
                    .fallbackToDestructiveMigration()
                    .allowMainThreadQueries() // this allows u to build in main thread
                    .build();

Edit

But as @CommonsWare suggested try to use background thread to insert probably using Rx / Executors / AsyncTasks for Kotlin you can use Coroutines. As main thread insertion might freeze the UI

Also @See this

Santanu Sur
  • 10,997
  • 7
  • 33
  • 52
  • 1
    This is not a very good idea. Freezing the UI to perform database I/O results in user-visible jank, particularly on low-end devices, or with lots of data, or with complex database operations. – CommonsWare Mar 27 '20 at 15:51
  • `AsyncTask` in your link are being deprecated – Biscuit Mar 27 '20 at 15:59
0

You need to perform your actions in the background thread. publish your results to UI thread

  • You achieve this using Async task/Rx java/Kotlin Coroutines

It's not recommended run DB operations in the main thread but you can access to the database on the main thread with allowMainThreadQueries()

MyApp.database =  Room.databaseBuilder(this, AppDatabase::class.java, "MyDatabase").allowMainThreadQueries().build()
Arunachalam k
  • 734
  • 1
  • 7
  • 20
  • 1
    `allowMainThreadQueries()` is not a very good idea. Freezing the UI to perform database I/O results in user-visible jank, particularly on low-end devices, or with lots of data, or with complex database operations. – CommonsWare Mar 27 '20 at 15:57
0

You are trying to access the Room database in the main thread, and that is why you got java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time..

Whenever you do an operation that has nondeterministic or long time, you must do it in a Worker (background) thread instead of the main thread.

There are several ways to do a background thread, such as AsyncTask, and Executors.

Note: AsyncTask is deprecated as of android R.

In your provided code you did access to the database a couple of times (getting all the notes and inserting notes into database), so I am going to fix them with Executors, specifically with ExecutorService.

Also you may consider LiveData for returning back database list asynchronously where it handles all the background thread for free.

LoadNoteListListener is created to be triggered whenever the list is returned back from database, so it's time now to handler the list in your main thread with its onNotesListLoaded() callback.

class MainActivity extends AppCompatActivity implements LoadNoteListListener {

    private NoteDatabase wDb;
    ArrayList<Note> wordList;

    private static Executor executor = Executors.newSingleThreadExecutor(); // to run DB access operations in background thread

    // Listen when the list of notes are returned back from Room database
    public interface LoadNoteListListener {
        void onNotesListLoaded(ArrayList<Note> notes);
    }


    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        s1 = getResources().getStringArray(R.array.PL);//Kald denne først
        startService(new Intent(ListActivity.this,WordLeanerService.class).putExtra("inputExtra", s1));
        setupConnectionToWLservice();

        LoadData(); //Load SharedPreferences
        setContentView(R.layout.activity_main);
        s2 = getResources().getStringArray(R.array.DC);
        s3 = getResources().getStringArray(R.array.DC2);

        recyclerView = findViewById(R.id.recyclerView);
        Exit = findViewById(R.id.CloseApp);
        editSearch = findViewById(R.id.EditText);
        searchBtn = findViewById(R.id.search_button);
        getData(); //get set data from EditActivity and set Arrays

        wDb = NoteDatabase.getInstance(getApplicationContext());

        getAllNotes(this);

    }



    public void getAllNotes(LoadNoteListListener listener) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                listener.onNotesListLoaded((ArrayList<Note>) wDb.noteDao().getAllNotes());
            }
        });
    }



    @Override
    void onNotesListLoaded(ArrayList<Note> notes) {
        wordList = notes;
        if(wordList.isEmpty()){
            for (int i = 0; i < images.length; i++) {
                Note nWord = new Note(s1[i], s3[i], s2[i], images[i], items[i], Notes[i]);
                insertNote(nWord);
            }
        }       
    }


    public void insertNote(Note note) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                wDb.noteDao().insertNote(note);
            }
        });
    }

    ....
}   
Zain
  • 37,492
  • 7
  • 60
  • 84