source

Android Room - simple select query - 기본 스레드에서 데이터베이스에 접근할 수 없음

factcode 2023. 10. 31. 22:55
반응형

Android Room - simple select query - 기본 스레드에서 데이터베이스에 접근할 수 없음

Room Persistence Library로 샘플을 시도하고 있습니다.엔티티를 만들었습니다.

@Entity
public class Agent {
    @PrimaryKey
    public String guid;
    public String name;
    public String email;
    public String password;
    public String phone;
    public String licence;
}

DAO 클래스 생성:

@Dao
public interface AgentDao {
    @Query("SELECT COUNT(*) FROM Agent where email = :email OR phone = :phone OR licence = :licence")
    int agentsCount(String email, String phone, String licence);

    @Insert
    void insertAgent(Agent agent);
}

데이터베이스 클래스를 만들었습니다.

@Database(entities = {Agent.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract AgentDao agentDao();
}

Kotlin의 하위 클래스 아래를 사용하여 노출된 데이터베이스:

class MyApp : Application() {

    companion object DatabaseSetup {
        var database: AppDatabase? = null
    }

    override fun onCreate() {
        super.onCreate()
        MyApp.database =  Room.databaseBuilder(this, AppDatabase::class.java, "MyDatabase").build()
    }
}

제 활동에서 아래의 기능을 구현합니다.

void signUpAction(View view) {
        String email = editTextEmail.getText().toString();
        String phone = editTextPhone.getText().toString();
        String license = editTextLicence.getText().toString();

        AgentDao agentDao = MyApp.DatabaseSetup.getDatabase().agentDao();
        //1: Check if agent already exists
        int agentsCount = agentDao.agentsCount(email, phone, license);
        if (agentsCount > 0) {
            //2: If it already exists then prompt user
            Toast.makeText(this, "Agent already exists!", Toast.LENGTH_LONG).show();
        }
        else {
            Toast.makeText(this, "Agent does not exist! Hurray :)", Toast.LENGTH_LONG).show();
            onBackPressed();
        }
    }

불행하게도 위 메서드를 실행할 때 아래 스택 트레이스와 함께 충돌합니다.

    FATAL EXCEPTION: main
 Process: com.example.me.MyApp, PID: 31592
java.lang.IllegalStateException: Could not execute method for android:onClick
    at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:293)
    at android.view.View.performClick(View.java:5612)
    at android.view.View$PerformClick.run(View.java:22288)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6123)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
 Caused by: java.lang.reflect.InvocationTargetException
    at java.lang.reflect.Method.invoke(Native Method)
    at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
    at android.view.View.performClick(View.java:5612) 
    at android.view.View$PerformClick.run(View.java:22288) 
    at android.os.Handler.handleCallback(Handler.java:751) 
    at android.os.Handler.dispatchMessage(Handler.java:95) 
    at android.os.Looper.loop(Looper.java:154) 
    at android.app.ActivityThread.main(ActivityThread.java:6123) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757) 
 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.
    at android.arch.persistence.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:137)
    at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:165)
    at com.example.me.MyApp.RoomDb.Dao.AgentDao_Impl.agentsCount(AgentDao_Impl.java:94)
    at com.example.me.MyApp.View.SignUpActivity.signUpAction(SignUpActivity.java:58)
    at java.lang.reflect.Method.invoke(Native Method) 
    at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288) 
    at android.view.View.performClick(View.java:5612) 
    at android.view.View$PerformClick.run(View.java:22288) 
    at android.os.Handler.handleCallback(Handler.java:751) 
    at android.os.Handler.dispatchMessage(Handler.java:95) 
    at android.os.Looper.loop(Looper.java:154) 
    at android.app.ActivityThread.main(ActivityThread.java:6123) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757) 

그 문제는 메인 스레드에서 db 작업을 실행하는 것과 관련이 있는 것 같습니다.그러나 위 링크에 제공된 샘플 테스트 코드는 별도의 스레드에서 실행되지 않습니다.

@Test
    public void writeUserAndReadInList() throws Exception {
        User user = TestUtil.createUser(3);
        user.setName("george");
        mUserDao.insert(user);
        List<User> byName = mUserDao.findUsersByName("george");
        assertThat(byName.get(0), equalTo(user));
    }

제가 뭘 놓쳤나요?어떻게 하면 충돌 없이 실행할 수 있을까요?제안 좀 해주세요.

권장되지 않지만 다음을 사용하여 기본 스레드에서 데이터베이스에 액세스할 수 있습니다.allowMainThreadQueries()

MyApp.database =  Room.databaseBuilder(this, AppDatabase::class.java, "MyDatabase").allowMainThreadQueries().build()

코틀린 코루틴 (맑음 & 간결함)

AsyncTask는 정말 투박합니다.코루틴은 더 깨끗한 대안입니다. 몇 개의 키워드를 뿌리면 동기화 코드가 비동기화됩니다.

// Step 1: add `suspend` to your fun
suspend fun roomFun(...): Int
suspend fun notRoomFun(...) = withContext(Dispatchers.IO) { ... }

// Step 2: launch from coroutine scope
private fun myFun() {
    lifecycleScope.launch { // coroutine on Main
        val queryResult = roomFun(...) // coroutine on IO
        doStuff() // ...back on Main
    }
}

종속성(아치 구성요소에 대한 코루틴 범위 추가):

// lifecycleScope:
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha04'

// viewModelScope:
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-alpha04'

-- 업데이트:
08-2019년 5월:이제 2.1호실에서 지원됩니다.suspend
13-Sep-2019: Architecture 구성 요소 범위를 사용하도록 업데이트됨

메인 스레드에서 UI를 잠그는 데이터베이스 액세스는 데일이 말했듯이 오류입니다.

--EDIT 2--

많은 사람들이 이 대답을 접하게 될 것이기 때문에...요즘 가장 좋은 선택지는 코틀린 코루틴스입니다.룸에서 직접 지원합니다(현재 베타 버전).https://kotlinlang.org/docs/reference/coroutines-overview.html https://developer.android.com/jetpack/androidx/releases/room#2.1.0-beta01

--EDIT 1--

궁금하신 분들을 위해...다른 선택지가 있습니다.새로운 ViewModel 및 LiveData 구성요소를 살펴보는 것이 좋습니다.Live Data는 Room과 잘 어울립니다.https://developer.android.com/topic/libraries/architecture/livedata.html

또 다른 옵션은 RxJava/RxAndroid입니다.LiveData보다 강력하지만 복잡합니다.https://github.com/ReactiveX/RxJava

--원래 대답은..

비동기 작업 확장 활동에서 (메모리 유출을 방지하기 위해) 정적 중첩 클래스를 만듭니다.

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();
        }
    }
}

또는 자신의 파일에 최종 클래스를 작성할 수도 있습니다.

그런 다음 signUpAction(View view) 메서드로 실행합니다.

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

경우에 따라 활동에서 AgentAsyncTask에 대한 참조를 보유하여 활동이 삭제되었을 때 취소할 수도 있습니다.하지만 모든 거래를 스스로 중단해야 할 것입니다.

그리고 구글의 테스트 예시에 대한 당신의 질문은...해당 웹 페이지에 다음과 같이 명시되어 있습니다.

데이터베이스 구현을 테스트하기 위해 권장되는 방법은 Android 장치에서 실행되는 Junit 테스트를 작성하는 것입니다.이러한 테스트는 작업을 생성할 필요가 없기 때문에 UI 테스트보다 실행 속도가 빨라야 합니다.

활동 없음, UI 없음.

RxJava, RxAndroid, RxKotlin 애호가들을 위하여

Observable.just(db)
          .subscribeOn(Schedulers.io())
          .subscribe { db -> // database operation }

핸들러, 비동기 또는 작업 스레드를 사용하는 대신 메인 스레드에서 실행할 수 없습니다. 샘플 코드는 여기서 사용할 수 있으며 여기서 룸 라이브러리를 통해 기사를 읽을 수 있습니다: Android's

/**
 *  Insert and get data using Database Async way
 */
AsyncTask.execute(new Runnable() {
    @Override
    public void run() {
        // Insert Data
        AppDatabase.getInstance(context).userDao().insert(new User(1,"James","Mathew"));

        // Get Data
        AppDatabase.getInstance(context).userDao().getAllUsers();
    }
});

기본 스레드에서 실행하려면 선호하지 않는 방법으로 실행해야 합니다.

메인 스레드에서 이 방법을 사용하여 달성할 수 있습니다.Room.inMemoryDatabaseBuilder()

람다를 사용하면 비동기식 작업으로 쉽게 실행할 수 있습니다.

 AsyncTask.execute(() -> //run your query here );

데이터베이스 작업을 별도의 스레드에서 수행하면 됩니다.이렇게(Kotlin):

Thread {
   //Do your database´s operations here
}.start()

간단히 이 코드를 사용하여 해결할 수 있습니다.

Executors.newSingleThreadExecutor().execute(new Runnable() {
                    @Override
                    public void run() {
                        appDb.daoAccess().someJobes();//replace with your code
                    }
                });

또는 람다에서는 다음 코드를 사용할 수 있습니다.

Executors.newSingleThreadExecutor().execute(() -> appDb.daoAccess().someJobes());

대체가능합니다appDb.daoAccess().someJobes()자신의 코드를 사용합니다.

Jetbrains Anko 라이브러리를 사용하면 DoAsync{..} 데이터베이스 호출을 자동으로 실행하는 메소드입니다.이것은 당신이 mcastro의 답변에 대해 가지고 있었던 것처럼 보였던 장황함 문제를 해결합니다.

사용 예시:

    doAsync { 
        Application.database.myDAO().insertUser(user) 
    }

삽입 및 업데이트를 위해 자주 사용하지만, 선택된 쿼리의 경우 RX 워크플로우를 사용하는 것이 좋습니다.

비동기 작업이 더 이상 사용되지 않으므로 실행자 서비스를 사용할 수 있습니다.또는 다른 답변에서 설명하는 것처럼 ViewModel with LiveData(라이브 데이터)를 사용할 수도 있습니다.

실행자 서비스 이용 시 아래와 같은 것을 이용할 수 있습니다.

public class DbHelper {

    private final Executor executor = Executors.newSingleThreadExecutor();

    public void fetchData(DataFetchListener dataListener){
        executor.execute(() -> {
                Object object = retrieveAgent(agentId);
                new Handler(Looper.getMainLooper()).post(() -> {
                        dataListener.onFetchDataSuccess(object);
                });
        });
    }
}

메인 루퍼(Main Looper)를 사용하여 다음에서 UI 요소에 액세스할 수 있습니다.onFetchDataSuccess

백그라운드에서 요청을 실행해야 합니다.간단한 방법으로 실행자를 사용할 수 있습니다.

Executors.newSingleThreadExecutor().execute { 
   yourDb.yourDao.yourRequest() //Replace this by your request
}

룸 데이터베이스는 사용자가 사용하지 않는 한 기본 스레드에서 데이터베이스 IO 작업(백그라운드 작업)을 실행할 수 없습니다.allowMainThreadQueries()데이터베이스 작성자와 함께.하지만 그것은 나쁜 접근입니다.


권장 접근 방식:
저는 현재 진행 중인 프로젝트의 코드를 사용하고 있습니다.

더하다suspendRepository에서 메서드 앞에 키워드를 지정합니다.

class FavRepository @Inject constructor(private val dao: WallpaperDao) {
    suspend fun getWallpapers(): List<Wallpaper> =  dao.getWallpapers()
}

당신의viewmodelclass 먼저 룸 데이터베이스에서 데이터를 가져오기 위해 Coroutine Dispature IO로 데이터베이스 작업을 실행해야 합니다.그런 다음 Coroutine Dispature MAIN으로 값을 업데이트합니다.

@HiltViewModel
class FavViewModel @Inject constructor(repo: FavRepository, @ApplicationContext context: Context) : ViewModel() {
    var favData = MutableLiveData<List<Wallpaper>>()
    init {
        viewModelScope.launch(Dispatchers.IO){
            val favTmpData: List<Wallpaper> = repo.getWallpapers()
            withContext(Dispatchers.Main){
                favData.value = favTmpData
            }
        }
    }
}

이제 활동/조각에서 관찰하여 보기 모델의 데이터를 사용합니다.

이것이 당신에게 도움이 되기를 바랍니다 :).

빠른 쿼리의 경우 UI 스레드에서 실행할 공간을 확보할 수 있습니다.

AppDatabase db = Room.databaseBuilder(context.getApplicationContext(),
        AppDatabase.class, DATABASE_NAME).allowMainThreadQueries().build();

제 경우 클릭한 사용자가 데이터베이스에 존재하는지 여부를 확인해야 했습니다.그렇지 않으면 사용자를 생성하고 다른 활동을 시작합니다.

       @Override
        public void onClick(View view) {



            int position = getAdapterPosition();

            User user = new User();
            String name = getName(position);
            user.setName(name);

            AppDatabase appDatabase = DatabaseCreator.getInstance(mContext).getDatabase();
            UserDao userDao = appDatabase.getUserDao();
            ArrayList<User> users = new ArrayList<User>();
            users.add(user);
            List<Long> ids = userDao.insertAll(users);

            Long id = ids.get(0);
            if(id == -1)
            {
                user = userDao.getUser(name);
                user.setId(user.getId());
            }
            else
            {
                user.setId(id);
            }

            Intent intent = new Intent(mContext, ChatActivity.class);
            intent.putExtra(ChatActivity.EXTRAS_USER, Parcels.wrap(user));
            mContext.startActivity(intent);
        }
    }

우아한 RxJava/Kotlin 솔루션을 사용하면 값을 반환하지 않지만 다른 스레드에서 관찰하고 구독할 수 있는 Observable을 얻을 수 있습니다.

public Completable insert(Event event) {
    return Completable.fromCallable(new Callable<Void>() {
        @Override
        public Void call() throws Exception {
            return database.eventDao().insert(event)
        }
    }
}

또는 코틀린에서:

fun insert(event: Event) : Completable = Completable.fromCallable {
    database.eventDao().insert(event)
}

일반적으로 다음과 같이 관찰하고 구독할 수 있습니다.

dataManager.insert(event)
    .subscribeOn(scheduler)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(...)

기본 스레드에서 데이터베이스 액세스를 허용할 수 있지만 디버깅을 위해서만 프로덕션에서 데이터베이스 액세스를 허용해서는 안 됩니다.

이유는 이렇습니다.

참고: 빌드에서 allowMainThreadQueries()를 호출하지 않은 경우 UI가 장시간 잠길 수 있기 때문에 기본 스레드에서 데이터베이스 액세스를 지원하지 않습니다.LiveData 또는 Flowable의 인스턴스를 반환하는 쿼리인 비동기 쿼리는 필요할 때 백그라운드 스레드에서 쿼리를 비동기적으로 실행하므로 이 규칙에서 제외됩니다.

오류 메시지,

UI가 장시간 잠길 수 있으므로 기본 스레드의 데이터베이스에 액세스할 수 없습니다.

꽤 설명적이고 정확합니다.문제는 기본 스레드에서 데이터베이스에 액세스하는 것을 어떻게 피해야 하느냐는 것입니다.엄청난 주제이지만 시작하려면 AsyncTask(비동기식 작업)에 대해 읽어 보십시오.

------ED--------------------

유닛 테스트를 할 때 문제가 생기셨군요.이 문제를 해결할 수 있는 몇 가지 방법이 있습니다.

  1. Android 기기(또는 에뮬레이터)가 아닌 개발 기기에서 직접 테스트를 실행합니다.이 기능은 데이터베이스 중심의 테스트에 적용되며 장치에서 실행되는지 여부는 크게 신경 쓰지 않습니다.

  2. 주석 사용@RunWith(AndroidJUnit4.class)안드로이드 장치에서 테스트를 실행하지만 UI가 있는 활동에서는 실행되지 않습니다.이에 대한 자세한 내용은 이 자습서에서 확인할 수 있습니다.

비동기 작업이 더 편하다면 다음을 수행할 수 있습니다.

  new AsyncTask<Void, Void, Integer>() {
                @Override
                protected Integer doInBackground(Void... voids) {
                    return Room.databaseBuilder(getApplicationContext(),
                            AppDatabase.class, DATABASE_NAME)
                            .fallbackToDestructiveMigration()
                            .build()
                            .getRecordingDAO()
                            .getAll()
                            .size();
                }

                @Override
                protected void onPostExecute(Integer integer) {
                    super.onPostExecute(integer);
                    Toast.makeText(HomeActivity.this, "Found " + integer, Toast.LENGTH_LONG).show();
                }
            }.execute();

Future와 Callable을 사용할 수 있습니다.따라서 긴 비동기 작업을 작성할 필요가 없으며 allowMainThreadQuery()를 추가하지 않고도 쿼리를 수행할 수 있습니다.

나의 dao 쿼리:-

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

나의 저장소 메소드:-

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();
}

업데이트: @RawQuery and Support를 사용하여 쿼리를 작성하려고 할 때에도 이 메시지가 표시되었습니다.DAO 내부의 SQLiteQuery.

@Transaction
public LiveData<List<MyEntity>> getList(MySettings mySettings) {
    //return getMyList(); -->this is ok

    return getMyList(new SimpleSQLiteQuery("select * from mytable")); --> this is an error

해결책: ViewModel 내부에 쿼리를 구축하고 DAO에 전달합니다.

public MyViewModel(Application application) {
...
        list = Transformations.switchMap(searchParams, params -> {

            StringBuilder sql;
            sql = new StringBuilder("select  ... ");

            return appDatabase.rawDao().getList(new SimpleSQLiteQuery(sql.toString()));

        });
    }

아니면...

다음과 같이 기본 스레드에서 데이터베이스에 직접 액세스해서는 안 됩니다.

 public void add(MyEntity item) {
     appDatabase.myDao().add(item); 
 }

작업을 업데이트, 추가 및 삭제하려면 AsyncTask를 사용해야 합니다.

예:

public class MyViewModel extends AndroidViewModel {

    private LiveData<List<MyEntity>> list;

    private AppDatabase appDatabase;

    public MyViewModel(Application application) {
        super(application);

        appDatabase = AppDatabase.getDatabase(this.getApplication());
        list = appDatabase.myDao().getItems();
    }

    public LiveData<List<MyEntity>> getItems() {
        return list;
    }

    public void delete(Obj item) {
        new deleteAsyncTask(appDatabase).execute(item);
    }

    private static class deleteAsyncTask extends AsyncTask<MyEntity, Void, Void> {

        private AppDatabase db;

        deleteAsyncTask(AppDatabase appDatabase) {
            db = appDatabase;
        }

        @Override
        protected Void doInBackground(final MyEntity... params) {
            db.myDao().delete((params[0]));
            return null;
        }
    }

    public void add(final MyEntity item) {
        new addAsyncTask(appDatabase).execute(item);
    }

    private static class addAsyncTask extends AsyncTask<MyEntity, Void, Void> {

        private AppDatabase db;

        addAsyncTask(AppDatabase appDatabase) {
            db = appDatabase;
        }

        @Override
        protected Void doInBackground(final MyEntity... params) {
            db.myDao().add((params[0]));
            return null;
        }

    }
}

선택한 작업에 LiveData를 사용하는 경우에는 AsyncTask가 필요 없습니다.

제 생각에 옳은 일은 RxJava를 사용하여 쿼리를 IO 스레드에 위임하는 것입니다.

저는 제가 방금 직면한 동등한 문제에 대한 해결책의 예를 가지고 있습니다.

((ProgressBar) view.findViewById(R.id.progressBar_home)).setVisibility(View.VISIBLE);//Always good to set some good feedback
        Completable.fromAction(() -> {
            //Creating view model requires DB access
            homeViewModel = new ViewModelProvider(this, factory).get(HomeViewModel.class);
        }).subscribeOn(Schedulers.io())//The DB access executes on a non-main-thread thread
        .observeOn(AndroidSchedulers.mainThread())//Upon completion of the DB-involved execution, the continuation runs on the main thread
        .subscribe(
                () ->
                {
                    mAdapter = new MyAdapter(homeViewModel.getExams());
                    recyclerView.setAdapter(mAdapter);
                    ((ProgressBar) view.findViewById(R.id.progressBar_home)).setVisibility(View.INVISIBLE);
                },
                error -> error.printStackTrace()
        );

솔루션을 일반화하려면 다음과 같이 하십시오.

((ProgressBar) view.findViewById(R.id.progressBar_home)).setVisibility(View.VISIBLE);//Always good to set some good feedback
        Completable.fromAction(() -> {
            someTaskThatTakesTooMuchTime();
        }).subscribeOn(Schedulers.io())//The long task executes on a non-main-thread thread
        .observeOn(AndroidSchedulers.mainThread())//Upon completion of the DB-involved execution, the continuation runs on the main thread
        .subscribe(
                () ->
                {
                    taskIWantToDoOnTheMainThreadWhenTheLongTaskIsDone();
                },
                error -> error.printStackTrace()
        );

더하다.allowMainThreadQueries()데이터베이스 파일로

@Database(entities = [Country::class], version = 1)
abstract class CountryDatabase: RoomDatabase() {
    abstract fun getCountryDao(): CountryDao
    companion object {
        @Volatile
        private var instance: CountryDatabase? = null
        private val LOCK = Any()

        operator fun invoke(context: Context) = instance ?:
        synchronized(LOCK) {
            instance ?:
            createDatabase(context).also { instance = it }
        }
        private fun createDatabase(context: Context) =
            Room.databaseBuilder(
                context.applicationContext,
                CountryDatabase::class.java,
                "country_db"
            ).allowMainThreadQueries()
             .build()
    }
}

더하다Dispatchers.IO이와 같은 흐름의 끝에서:

flow { ... }.flowOn(Dispatchers.IO)

룸 데이터베이스에서는 기본 스레드에서 데이터베이스 IO 작업을 실행할 수 없습니다.그래서 인코틀린은 코루틴 스코프(IO) 내부에서 쿼리 호출을 수행했습니다.

suspend fun executrQuery(){
     CoroutineScope(IO).launch {
         // Write your code inside.
         }
}

그리고 또 다른 방법은 allowMainThreadQuery를 아래와 같이 사용하는 것입니다.

AppDatabase db =Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, DATABASE_NAME).allowMainThreadQueries().build();

당신의 쿼리를 와의 코루틴으로 호출합니다.(Dispatchers.IO)우리는 백그라운드에서 db 작업을 해야하기 때문입니다.

아래에 첨부된 스크린샷을 확인해주시기 바랍니다.

언급URL : https://stackoverflow.com/questions/44167111/android-room-simple-select-query-cannot-access-database-on-the-main-thread

반응형