Android Room-단순 선택 쿼리-기본 스레드에서 데이터베이스에 액세스 할 수 없음


126

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

여기에 빠진 게 있나요? 충돌없이 실행하려면 어떻게해야합니까? 제안 해주세요.


1
Kotlin 용으로 작성되었지만 이 기사 에서는 근본적인 문제를 매우 잘 설명합니다.
Peter Lehnhardt


이 답을보세요. 이 답변은 나를 위해 작동합니다. stackoverflow.com/a/51720501/7655085
Somen Tushir

답변:


59

Dale이 말한 것처럼 UI를 잠그는 주 스레드에 대한 데이터베이스 액세스는 오류입니다.

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

또는 자체 파일에 최종 클래스를 만들 수 있습니다.

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

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

경우에 따라 활동이 삭제 될 때 취소 할 수 있도록 활동에 AgentAsyncTask에 대한 참조를 보유 할 수도 있습니다. 그러나 모든 거래를 스스로 중단해야합니다.

또한 Google의 테스트 예에 대한 질문 ... 해당 웹 페이지에 다음과 같이 명시되어 있습니다.

데이터베이스 구현을 테스트하는 데 권장되는 접근 방식은 Android 기기에서 실행되는 JUnit 테스트를 작성하는 것입니다. 이러한 테스트는 활동을 만들 필요가 없기 때문에 UI 테스트보다 실행 속도가 더 빠릅니다.

활동 없음, UI 없음.

--편집하다--

궁금한 사람들을 위해 ... 다른 옵션이 있습니다. 새로운 ViewModel 및 LiveData 구성 요소를 살펴 보는 것이 좋습니다. LiveData는 Room과 잘 작동합니다. https://developer.android.com/topic/libraries/architecture/livedata.html

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

-편집 2--

많은 사람들이이 답변을 접할 수 있기 때문에 ... 요즘 가장 좋은 옵션은 일반적으로 Kotlin Coroutines입니다. 이제 Room에서 직접 지원합니다 (현재 베타 버전). https://kotlinlang.org/docs/reference/coroutines-overview.html https://developer.android.com/jetpack/androidx/releases/room#2.1.0-beta01


31
데이터베이스에 액세스해야 할 때마다이 거대한 비동기 작업 (코드 샘플에서 제안한)을 만들어야합니까? db에서 데이터를 가져 오는 것은 하나가 아니라 수십 줄의 코드입니다. 새 클래스 생성을 제안했지만 각 삽입 / 선택 데이터베이스 호출에 대해 새 AsyncTask 클래스를 만들어야 함을 의미합니까?
Piotrek

7
다른 옵션이 있습니다. 새로운 ViewModel 및 LiveData 구성 요소를 살펴볼 수 있습니다. LiveData를 사용할 때 AsyncTask가 필요하지 않은 경우 개체가 변경 될 때마다 알림을받습니다. developer.android.com/topic/libraries/architecture/… developer.android.com/topic/libraries/architecture/… AndroidRx (LiveData가하는 일을 거의 수행하지만)와 Promise도 있습니다. AsyncTask를 사용할 때 하나의 AsyncTask에 여러 작업을 포함하거나 각각을 분리 할 수있는 방식으로 아키텍처를 만들 수 있습니다.
mcastro

@Piotrek-Kotlin은 이제 비동기로 구워졌습니다 (실험적으로 플래그 지정됨). 비교적 사소한 내 대답을 참조하십시오. Samuel Robert의 답변은 Rx를 다룹니다. 여기에 LiveData 답변이 표시되지 않지만 관찰 가능 항목을 원하는 경우 더 나은 선택 일 수 있습니다.
AjahnCharles

일반 AsyncTask를 사용하는 것은 지금도 작동하지 않는 것처럼 보이지만 여전히 잘못된 상태 예외가 발생합니다
Peterstev Uremgba

@Piotrek 당신은 전체 경험에서 메인 스레드에서 데이터베이스 액세스를 실행하는 데 익숙하다고 말하고 있습니까?
mr5

142

권장하지는 않지만 기본 스레드의 데이터베이스에 액세스 할 수 있습니다. allowMainThreadQueries()

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

10
Room은 allowMainThreadQueries()오랫동안 UI를 잠글 수 있기 때문에 빌더 를 호출하지 않는 한 기본 스레드에서 데이터베이스에 액세스하는 것을 허용하지 않습니다 . 비동기 쿼리 (반환하는 쿼리 LiveData또는 RxJava Flowable)는 필요할 때 백그라운드 스레드에서 쿼리를 비동기 적으로 실행 하므로이 규칙에서 제외됩니다.
pRaNaY

4
덕분에 나는 라이브 데이터에 로더에서 변환하기 전에 룸이 예상대로 작동 테스트하기 원하는이, 마이그레이션을위한 매우 유용합니다
SammyT

5
@JideGuruTheProgrammer 아니요, 안됩니다. 어떤 경우에는 앱이 많이 느려질 수 있습니다. 작업은 비동기 적으로 수행되어야합니다.
Alex

@Alex 메인 스레드에서 쿼리를 작성하는 경우가 없습니까?
Justin Meiners

2
@JustinMeiners 그것은 나쁜 습관입니다. 데이터베이스가 작게 유지되는 한 괜찮습니다.
lasec0203

53

Kotlin 코 루틴 (명확하고 간결한)

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'

-업데이트 :
2019 년 5 월 8 일 : 이제 Room 2.1 suspend
에서 2019 년 9 월 13 일 지원 : 아키텍처 구성 요소 범위 를 사용하도록 업데이트 됨


1
@Query abstract suspend fun count()by using suspend 키워드에 컴파일 오류가 있습니까? 당신은 친절이 비슷한 질문으로 볼 수 : stackoverflow.com/questions/48694449/...
로빈

@Robin-네, 그렇습니다. 내 실수; 보호 된 비 일시 중단 @Query기능 을 호출하는 공용 (주석이없는) DAO 메서드에서 일시 중단을 사용하고있었습니다. suspend 키워드를 내부 @Query메서드에도 추가하면 실제로 컴파일에 실패합니다. suspend & Room clash에 대한 영리한 물건처럼 보입니다 (다른 질문에서 언급했듯이 suspend의 컴파일 된 버전은 Room이 처리 할 수없는 연속을 반환합니다).
AjahnCharles

많은 의미가 있습니다. 대신 코 루틴 함수로 호출 할 것입니다.
로빈

1
참고로 그들은 :) 룸 2.1에서 중단에 대한 지원 추가 - @Robin
AjahnCharles

launch더 이상 키워드 가없는 것 같습니다. 다음과 같은 범위로 시작합니다.GlobalScope.launch
nasch

48

모든 RxJava 또는 RxAndroid 또는 RxKotlin 애호가를위한

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

3
이 코드를 메서드 안에 넣으면 데이터베이스 작업에서 결과를 반환하는 방법은 무엇입니까?
Eggakin Baconwalker 2011

내가 가진 @EggakinBaconwalker override fun getTopScores(): Observable<List<PlayerScore>> { return Observable .fromCallable({ GameApplication.database .playerScoresDao().getTopScores() }) .applySchedulers() }applySchedulers()난 그냥 할fun <T> Observable<T>.applySchedulers(): Observable<T> = this.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())
noloman

IntentService에서는 작동하지 않습니다. 스레드가 완료되면 IntentService가 완료되기 때문입니다.
Umang Kothari

1
@UmangKothari IntentService#onHandleIntent이 메서드는 작업자 스레드에서 실행되므로 Room 데이터베이스 작업을 수행하는 데 스레딩 메커니즘이 필요하지 않기 때문에 켜져있는 경우 예외가 발생하지 않습니다.
Samuel Robert

@SamuelRobert, 네, 내 잘못에 동의합니다. 깜빡 잊어 버렸습니다.
Umang Kothari

27

대신 핸들러, 비동기 또는 작업 스레드를 사용하여 주 스레드에서 실행할 수 없습니다. 여기에서 샘플 코드를 사용할 수 있으며 여기에서 룸 라이브러리에 대한 기사를 읽으십시오. Android의 Room Library

/**
 *  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()


이 메서드를 사용하여 데이터를 가져 오는 경우 (이 경우에는 getAllUsers () 만)이 메서드에서 데이터를 반환하는 방법은 무엇입니까? "run"안에 "return"이라는 단어를 넣으면 오류가 나타납니다.
Eggakin Baconwalker 2011

1
인터페이스 메서드를 어딘가에 만들고 익명 클래스를 추가하여 여기에서 데이터를 가져옵니다.
Rizvan 2017

1
이것은 삽입 / 업데이트를위한 가장 간단한 솔루션입니다.
Beer Me

12

람다를 사용하면 AsyncTask로 쉽게 실행할 수 있습니다.

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

2
이것은 편리합니다. 감사합니다. 그건 그렇고, Kotlin은 훨씬 더 쉽습니다. AsyncTask.execute {}
alexrnov

1
그러나이 방법을 사용하여 결과를 어떻게 얻습니까?
leeCoder

11

Jetbrains Anko 라이브러리를 사용하면 doAsync {..} 메서드를 사용하여 데이터베이스 호출을 자동으로 실행할 수 있습니다. 이것은 mcastro의 대답으로 겪었던 것처럼 보였던 자세한 문제를 처리합니다.

사용 예 :

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

삽입 및 업데이트에 자주 사용하지만 일부 쿼리의 경우 RX 워크 플로를 사용하는 것이 좋습니다.



6

백그라운드에서 요청을 실행해야합니다. 간단한 방법은 Executors를 사용하는 것입니다 .

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

결과를 어떻게 반환합니까?
leeCoder

5

우아한 RxJava / Kotlin 솔루션은 Completable.fromCallable값을 반환하지 않지만 다른 스레드에서 관찰하고 구독 할 수있는 Observable을 제공 하는를 사용 하는 것입니다.

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

또는 Kotlin에서 :

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

평소처럼 관찰하고 구독 할 수 있습니다.

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

5

주 스레드에서 데이터베이스 액세스를 허용 할 수 있지만 디버깅 목적으로 만 프로덕션에서는이 작업을 수행하면 안됩니다.

여기에 이유가 있습니다.

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


4

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

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()자신의 코드로 바꿀 수 있습니다 .


4

asyncTask는 더 이상 사용되지 않으므로 실행기 서비스를 사용할 수 있습니다. 또는 당신은 또한에 뷰 모델을 사용할 수 있습니다 LiveData 다른 답변에 설명 된대로.

executor 서비스를 사용하기 위해서는 아래와 같은 것을 사용할 수 있습니다.

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를 사용하므로 onFetchDataSuccess콜백 에서 UI 요소에 접근 할 수 있습니다 .


3

오류 메시지,

오랫동안 UI를 잠글 수 있으므로 주 스레드의 데이터베이스에 액세스 할 수 없습니다.

매우 설명적이고 정확합니다. 문제는 주 스레드에서 데이터베이스에 액세스하지 않는 방법입니다. 그것은 거대한 주제이지만 시작하려면 AsyncTask 에 대해 읽어보십시오 (여기를 클릭하십시오)

-----편집하다----------

단위 테스트를 실행할 때 문제가있는 것 같습니다. 이 문제를 해결할 수있는 몇 가지 선택 사항이 있습니다.

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

  2. 주석 @RunWith(AndroidJUnit4.class) 을 사용하여 Android 기기에서 테스트를 실행하지만 UI가있는 활동에서는 실행하지 않습니다. 이에 대한 자세한 내용은 이 튜토리얼에서 찾을 수 있습니다.


귀하의 요점을 이해합니다. JUnit을 통해 db 작업을 테스트하려고 할 때 동일한 점이 유효하다고 가정합니다. 그러나 developer.android.com/topic/libraries/architecture/room.html 에서 샘플 테스트 메서드 writeUserAndReadInList는 백그라운드 스레드에서 삽입 쿼리를 호출하지 않습니다. 여기에 빠진 게 있나요? 제안 해주세요.
Devarshi

이것이 문제가있는 테스트라는 사실을 놓쳐서 죄송합니다. 더 많은 정보를 추가하기 위해 내 답변을 편집하겠습니다.
Dale Wilson

3

비동기 작업에 더 익숙한 경우 :

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

2

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

@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가 필요하지 않습니다.


1

빠른 쿼리를 위해 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);
        }
    }

1

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

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

이것이 우리가 Callable / Future를 사용하는 이유입니다. 안드로이드는 메인 스레드에서 쿼리 실행을 허용하지 않기 때문입니다. 위의 질문에서 질문 한대로
초보자

1
내 대답의 코드가 백그라운드 스레드에서 쿼리를 작성하더라도 주 스레드는 차단되고 쿼리가 완료되면 대기합니다. 그래서 결국 allowMainThreadQueries(). 여전히 두 경우 모두에서 차단 메인 쓰레드
eugeneek

0

내 생각에 옳은 일은 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()
        );
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.