Android Odası - basit seçim sorgusu - Ana iş parçacığındaki veritabanına erişilemiyor


126

Room Persistence Library ile bir örnek deniyorum . Bir Varlık oluşturdum:

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

DAO sınıfı oluşturuldu:

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

Veritabanı sınıfı oluşturuldu:

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

Kotlin'de aşağıdaki alt sınıf kullanılarak açığa çıkarılan veritabanı:

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

Faaliyetimde aşağıdaki işlevi uyguladı:

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

Maalesef yukarıdaki yöntemin yürütülmesi sırasında aşağıdaki yığın izleme ile çöküyor:

    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) 

Görünüşe göre bu sorun ana iş parçacığı üzerinde db işleminin yürütülmesi ile ilgili. Ancak yukarıdaki bağlantıda verilen örnek test kodu ayrı bir iş parçacığında çalışmaz:

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

Burada bir şey eksik mi? Kilitlenme olmadan nasıl çalıştırabilirim? Lütfen önerin.


1
Kotlin için yazılmış olmasına rağmen, bu makale temeldeki sorunu çok iyi açıklıyor!
Peter Lehnhardt


Bu cevaba bak. Bu cevap benim için çalışıyor stackoverflow.com/a/51720501/7655085
Somen Tushir

Yanıtlar:


59

Dale'in dediği gibi, UI'yi kilitleyen ana iş parçacığındaki veritabanı erişimi hatadır.

AsyncTask genişletme faaliyetinizde statik iç içe geçmiş bir sınıf oluşturun (bellek sızıntısını önlemek için).

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

Veya kendi dosyasında son bir sınıf oluşturabilirsiniz.

Ardından bunu signUpAction (Görünüm görünümü) yönteminde yürütün:

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

Bazı durumlarda, aktivitenizde AgentAsyncTask'e bir referans tutmak isteyebilirsiniz, böylece Aktivite yok edildiğinde onu iptal edebilirsiniz. Ancak herhangi bir işlemi kendiniz kesmeniz gerekir.

Ayrıca, Google'ın test örneğiyle ilgili sorunuz ... Bu web sayfasında şöyle yazıyorlar:

Veritabanı uygulamanızı test etmek için önerilen yaklaşım, bir Android cihazda çalışan bir JUnit testi yazmaktır. Bu testler bir aktivite oluşturmayı gerektirmediğinden, bunların yürütülmesi UI testlerinizden daha hızlı olmalıdır.

Etkinlik Yok, Kullanıcı Arayüzü Yok.

--DÜZENLE--

Merak edenler için ... Başka seçenekleriniz var. Yeni ViewModel ve LiveData bileşenlerine bir göz atmanızı tavsiye ederim. LiveData, Room ile harika çalışıyor. https://developer.android.com/topic/libraries/architecture/livedata.html

Diğer bir seçenek de RxJava / RxAndroid. LiveData'dan daha güçlü ancak daha karmaşık. https://github.com/ReactiveX/RxJava

- DÜZENLEME 2--

Pek çok kişi bu cevaba rastlayabileceği için ... Günümüzde genel anlamda en iyi seçenek Kotlin Coroutines. Oda artık bunu doğrudan destekliyor (şu anda beta sürümünde). https://kotlinlang.org/docs/reference/coroutines-overview.html https://developer.android.com/jetpack/androidx/releases/room#2.1.0-beta01


31
Veritabanına her erişmem gerektiğinde bu büyük eşzamansız görevi (kod örneğinde önerdiğiniz) yapmam mı gerekiyor? DB'den bazı verileri almak için bir yerine bir düzine kadar kod satırı vardır. Ayrıca yeni sınıf oluşturmayı da önerdiniz, ancak bu, her ekleme / seçme veritabanı çağrısı için yeni AsyncTask sınıfı oluşturmam gerektiği anlamına mı geliyor?
Piotrek

7
Başka seçenekleriniz var, evet. Yeni ViewModel ve LiveData bileşenlerine bakmak isteyebilirsiniz. LiveData'yı kullanırken AsyncTask'a ihtiyacınız yoktur, her şey değiştiğinde nesne bilgilendirilecektir. developer.android.com/topic/libraries/architecture/… developer.android.com/topic/libraries/architecture/… Ayrıca AndroidRx (LiveData'nın yaptığını hemen hemen yapsa da) ve Promises vardır. AsyncTask'ı kullanırken, bir AsyncTask'a birden fazla işlemi dahil edebilecek veya her birini ayırabilecek şekilde mimari yapabilirsiniz.
mcastro

@Piotrek - Kotlin'de artık eşzamansız fırın var (deneysel olarak işaretlenmiş olsa da). Nispeten önemsiz olan cevabıma bakın. Samuel Robert'in cevabı Rx'i kapsıyor. Burada bir LiveData yanıtı göremiyorum, ancak gözlemlenebilir istiyorsanız bu daha iyi bir seçim olabilir.
AjahnCharles

Normal AsyncTask kullanmak şu anda işe yaramıyor gibi görünse de, yasadışı durum istisnasını almaya devam ediyor
Peterstev Uremgba

@Piotrek bana tüm deneyiminiz boyunca ana iş parçacığında Veritabanı erişimini yürütmeye alışkın olduğunuzu mu söylüyorsunuz?
mr5

142

Önerilmez, ancak ana iş parçacığı üzerindeki veritabanına erişebilirsiniz. allowMainThreadQueries()

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

10
Oda allowMainThreadQueries(), kullanıcı arayüzünü uzun bir süre kilitleyebileceği için oluşturucuyu çağırmadığınız sürece ana iş parçacığındaki veritabanına erişime izin vermez . Zaman uyumsuz sorgular (dönen sorgular LiveDataveya RxJava Flowable), gerektiğinde sorguyu arka planda eşzamansız olarak çalıştırdıkları için bu kuraldan muaftır.
pRaNaY

4
Teşekkürler, bu taşıma için çok yararlı, çünkü Odanın Yükleyicilerden Canlı Verilere dönüştürmeden önce beklendiği gibi çalıştığını test etmek istiyorum
SammyT

5
@JideGuruTheProgrammer Hayır, olmamalı. Bazı durumlarda uygulamanızı çok yavaşlatabilir. İşlemler asenkron olarak yapılmalıdır.
Alex

@Alex Asla ana iş parçacığında sorgu yapmak için bir durum yok mu?
Justin Meiners

2
@JustinMeiners bu sadece kötü bir uygulama, veritabanı küçük kaldığı sürece bunu yapmakta sorun olmayacak.
lasec0203

54

Kotlin Coroutines (Açık ve Kısa)

AsyncTask gerçekten hantal. Coroutinler daha temiz bir alternatiftir (sadece birkaç anahtar kelime serpiştirin ve senkronizasyon kodunuz eşzamansız hale gelir).

// 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
    }
}

Bağımlılıklar (ark bileşenleri için eşgüdüm kapsamları ekler):

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

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

- Güncellemeler:
08 Mayıs 2019: Oda 2.1 artık suspend
13 Eylül 2019'u destekliyor : Mimari bileşenleri kapsamını kullanmak için güncellendi


1
@Query abstract suspend fun count()Suspend anahtar sözcüğünü kullanarak ile herhangi bir derleme hatası var mı? Bu benzer soruya nazikçe bakar mısınız: stackoverflow.com/questions/48694449/…
Robin

@Robin - Evet yaparım. Benim hatam; Askıya alma özelliğini, korumalı bir askıya alınmayan @Queryişlev olarak adlandırılan genel (açıklamasız) bir DAO yönteminde kullanıyordum . Suspend anahtar sözcüğünü dahili @Queryyönteme de eklediğimde, gerçekten derleme başarısız oluyor. Askıya alma ve Oda çatışması için kaputun altındaki akıllıca şeylere benziyor (diğer sorunuzda da belirttiğiniz gibi, askıya almanın derlenmiş versiyonu Room'un idare edemeyeceği bir devamı döndürüyor).
AjahnCharles

Çok mantıklı. Onun yerine ona koroutin fonksiyonları diyeceğim.
Robin

1
@Robin - Bilginize, Oda 2.1'de askıya alma desteği
eklediler

Görünüşe göre launchartık anahtar kelime yok, gibi bir kapsamla GlobalScope.launch
başlatıyorsunuz

48

Dışarıdaki tüm RxJava veya RxAndroid veya RxKotlin sevenler için

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

3
Bu kodu bir yöntemin içine koyarsam, veritabanı işleminden sonuç nasıl döndürülür?
Eggakin Baconwalker

Ben @EggakinBaconwalker override fun getTopScores(): Observable<List<PlayerScore>> { return Observable .fromCallable({ GameApplication.database .playerScoresDao().getTopScores() }) .applySchedulers() }nerede applySchedulers()Ben sadece yapmakfun <T> Observable<T>.applySchedulers(): Observable<T> = this.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())
noloman

Bu, IntentService için çalışmaz. Çünkü IntentService, iş parçacığı tamamlandıktan sonra yapılacaktır.
Umang Kothari

1
Eğer iseniz @UmangKothari Sen istisna alamadım IntentService#onHandleIntentişçi iş parçacığı üzerinde bu yöntem yürütür Eğer Odası veritabanı işlemi yapmak için herhangi bir iş parçacığı mekanizması orada gerek olmazdı böylece çünkü
Samuel Robert

@SamuelRobert, Evet benim hatam katılıyorum. Aklımdan çıkmış.
Umang Kothari

27

İşleyicileri, zaman uyumsuz veya çalışan evreleri kullanmak yerine ana iş parçacığı üzerinde çalıştıramazsınız. Örnek bir koda buradan ulaşabilirsiniz ve burada oda kitaplığı ile ilgili makaleyi okuyun: Android'in Oda Kitaplığı

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

Tercih edilmeyen bir şekilde ana iş parçacığı üzerinde çalıştırmak istiyorsanız.

Ana iş parçacığına ulaşmak için bu yöntemi kullanabilirsiniz. Room.inMemoryDatabaseBuilder()


Verileri almak için bu yöntemi kullanırsam (bu durumda yalnızca getAllUsers ()), verileri bu yöntemden nasıl döndürürüm? "Çalıştır" ın içine "dönüş" sözcüğünü koyarsam bir hata görünür.
Eggakin Baconwalker

1
bir yerde bir arayüz yöntemi oluşturun ve buradan veri almak için anonim sınıf ekleyin.
Rizvan

1
Bu, ekleme / güncelleme için en basit çözümdür.
Beer Me

12

Lambda ile AsyncTask ile çalıştırılması kolaydır

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

2
Bu uygun, teşekkürler. Bu arada, Kotlin daha da kolay: AsyncTask.execute {}
alexrnov

1
ama bu yöntemi kullanarak sonucu nasıl elde edersiniz?
leeCoder

11

Jetbrains Anko kitaplığıyla, veritabanı çağrılarını otomatik olarak yürütmek için doAsync {..} yöntemini kullanabilirsiniz. Bu, mcastro'nun cevabıyla yaşadığınız ayrıntı problemini halleder.

Örnek kullanım:

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

Bunu sık sık eklemeler ve güncellemeler için kullanıyorum, ancak belirli sorgular için RX iş akışını kullanmayı öneriyorum.


7

Veritabanı işlemlerini ayrı bir Thread içerisinde yapmanız yeterlidir. Bunun gibi (Kotlin):

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

6

İsteği arka planda yürütmelisiniz. Bir Executors kullanmak basit bir yol olabilir :

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

sonucu nasıl döndürürsün?
leeCoder

5

Completable.fromCallableSize bir değer döndürmeyen, ancak farklı bir iş parçacığında gözlemlenebilen ve abone olabilen bir Observable verecek olan zarif bir RxJava / Kotlin çözümü kullanmaktır .

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

Veya Kotlin'de:

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

Genelde yaptığınız gibi gözlemleyebilir ve abone olabilirsiniz:

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

5

Ana iş parçacığı üzerinde veritabanı erişimine izin verebilirsiniz, ancak yalnızca hata ayıklama amacıyla bunu üretimde yapmamalısınız.

Nedeni burada.

Not: Kullanıcı arabirimini uzun bir süre kilitleyebileceğinden, oluşturucuda allowMainThreadQueries () öğesini çağırmadığınız sürece, oda ana iş parçacığındaki veritabanı erişimini desteklemez. Eşzamansız sorgular (LiveData veya Flowable örneklerini döndüren sorgular), gerektiğinde sorguyu arka planda eşzamansız olarak çalıştırdıkları için bu kuraldan muaftır.


4

Basitçe çözmek için bu kodu kullanabilirsiniz:

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

Veya lambda'da bu kodu kullanabilirsiniz:

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

appDb.daoAccess().someJobes()Kendi kodunuzla değiştirebilirsiniz ;


4

AsyncTask kullanımdan kaldırıldığından, yürütme hizmetini kullanabiliriz. VEYA ViewModel'i LiveData ile de kullanabilirsiniz diğer yanıtlarda açıklandığı gibi .

Yürütme hizmetini kullanmak için aşağıdaki gibi bir şey kullanabilirsiniz.

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

Geri aramadan UI öğesine erişebilmeniz için Ana Looper kullanılır onFetchDataSuccess.


3

Hata mesajı,

Kullanıcı arabirimini uzun süre kilitleyebileceği için ana iş parçacığındaki veritabanına erişilemiyor.

Oldukça açıklayıcı ve doğru. Soru, ana iş parçacığındaki veritabanına erişimden nasıl kaçınmanız gerektiğidir. Bu çok büyük bir konu, ancak başlamak için AsyncTask hakkında okuyun (buraya tıklayın)

-----DÜZENLE----------

Bir birim testi çalıştırdığınızda sorun yaşadığınızı görüyorum. Bunu düzeltmek için birkaç seçeneğiniz var:

  1. Testi bir Android cihaz (veya emülatör) yerine doğrudan geliştirme makinesinde çalıştırın. Bu, veritabanı merkezli ve bir cihazda çalışıp çalışmadıklarıyla ilgilenmeyen testler için işe yarar.

  2. @RunWith(AndroidJUnit4.class) Testi android cihazda çalıştırmak için notu kullanın , ancak kullanıcı arayüzlü bir aktivitede değil. Bununla ilgili daha fazla ayrıntı bu eğiticide bulunabilir .


Demek istediğini anlıyorum, varsayımım JUnit aracılığıyla herhangi bir db işlemini test etmeye çalıştığınızda aynı noktanın geçerli olmasıdır. Ancak, developer.android.com/topic/libraries/architecture/room.html dosyasındaki örnek test yöntemi writeUserAndReadInList, arka plan iş parçacığındaki ekleme sorgusunu çağırmaz. Burada bir şey eksik mi? Lütfen önerin.
Devarshi

Üzgünüm, bunun sorunlu bir test olduğu gerçeğini kaçırdım. Biraz daha bilgi eklemek için cevabımı düzenleyeceğim.
Dale Wilson

3

Zaman uyumsuz görevde daha rahatsanız :

  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

Güncelleme: DAO içinde @RawQuery ve SupportSQLiteQuery kullanarak bir sorgu oluşturmaya çalışırken de bu mesajı aldım.

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

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

Çözüm: Sorguyu ViewModel içinde oluşturun ve DAO'ya iletin.

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

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

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

        });
    }

Veya...

Veritabanına doğrudan ana iş parçacığından erişmemelisiniz, örneğin:

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

Güncelleme, ekleme ve silme işlemleri için AsyncTask kullanmalısınız.

Misal:

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

    }
}

Seçili işlemler için LiveData kullanıyorsanız, AsyncTask'a ihtiyacınız yoktur.


1

Hızlı sorgular için, odanın UI iş parçacığında yürütmesine izin verebilirsiniz.

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

Benim durumumda, listede tıklanan kullanıcının veritabanında olup olmadığını anlamak zorunda kaldım. Değilse, kullanıcıyı oluşturun ve başka bir etkinlik başlatın

       @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 ve Callable'ı kullanabilirsiniz. Böylece uzun bir eşzamansız görev yazmanız gerekmez ve allowMainThreadQueries () eklemeden sorgularınızı gerçekleştirebilirsiniz.

Dao sorgum: -

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

Depo yöntemim: -

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

Bu yüzden Callable / Future kullanıyoruz çünkü android sorguların ana iş parçacığı üzerinde çalışmasına izin vermiyor. Yukarıdaki soruda sorulduğu gibi
başlangıç ​​seviyesi

1
Demek istediğim, cevabınızdan gelen kod arka planda sorgu oluştursa da, ana iş parçacığı bloke olur ve sorgu bittiğinde beklenir. Yani sonuçta bundan daha iyi değil allowMainThreadQueries(). Ana ileti dizisi her iki durumda da hala engellendi
eugeneek

0

Bence yapılacak doğru şey, sorguyu RxJava kullanarak bir IO iş parçacığına delege etmektir.

Az önce karşılaştığım eşdeğer bir soruna bir çözüm örneğim var.

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

Çözümü genellemek istiyorsak:

((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()
        );
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.