Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android -room persistent library - DAO calls are async, therefore how to get callback?

From what i have read Room doesn’t allow you to issue database queries on the main thread (as can cause delays on the main thread)). so imagine i am trying to update a textview on the UI main thread which some data how would i get a call back. Let me show you an example. Imagine i want to store my business model data into a object called Events. We would therefore have a EventDao object:

imagine we have this DAO object below:

@Dao
public interface EventDao {

   @Query("SELECT * FROM " + Event.TABLE_NAME + " WHERE " + Event.DATE_FIELD + " > :minDate" limit 1)
   LiveData<List<Event>> getEvent(LocalDateTime minDate);

   @Insert(onConflict = REPLACE)
   void addEvent(Event event);

   @Delete
   void deleteEvent(Event event);

   @Update(onConflict = REPLACE)
   void updateEvent(Event event);

}

and now in some activity i have a textview and i'd like to update its value so i do this:

 myTextView.setText(EventDao.getEvent(someDate));/*i think this is illegal as im trying to call room dao on mainthread, therefore how is this done correctly ? would i need to show a spinner while it updates ?*/

since the fetching is occuring off of the main thread i dont think i can call it like this and expect a smooth update. Whats the best approach here ?

Some more information: i wanted to use the room database as mechanism for retrieving model information instead of keeping it statically in memory. so the model would be available to me locally through the db after i download it through a rest service.

UPDATE: so since i am returning a livedata then i can do this:

eventDao = eventDatabase.eventDao();
eventDao.getEvent().observe(this, event -> {
     myTextView.setText(event.get(0));
});

and that works for something very small. but imagine my database has a million items. then when i do this call, there will be a delay retrieving the data. The very first time this gets called it will be visible to the user that there is a delay. How to avoid this ? So to be clear , there are times i do not want live data, i just need to update once the view. I need to know how to do this ? even if its not with liveData.

like image 634
j2emanue Avatar asked May 31 '17 19:05

j2emanue


4 Answers

If you want to do your query synchronously and not receive notifications of updates on the dataset, just don't wrap you return value in a LiveData object. Check out the sample code from Google.

Take a look at loadProductSync() here

like image 104
Bohsen Avatar answered Nov 11 '22 11:11

Bohsen


There is a way to turn off async and allow synchronous access.

when building the database you can use :allowMainThreadQueries()

and for in memory use: Room.inMemoryDatabaseBuilder()

Although its not recommended. So in the end i can use a in memory database and main thread access if i wanted super fast access. i guess it depends how big my data is and in this case is very small.

but if you did want to use a callback.... using rxJava here is one i made for a list of countries i wanted to store in a database:

public Observable<CountryModel> queryCountryInfoFor(final String isoCode) {
    return Observable.fromCallable(new Callable<CountryModel>() {
        @Override
        public CountryModel call() throws Exception {
            return db.countriesDao().getCountry(isoCode);
        }
    }).subscribeOn(Schedulers.io())
       
 .observeOn(AndroidSchedulers.mainThread());

}

you can then easily add a subscriber to this function to get the callback with Rxjava.

like image 36
j2emanue Avatar answered Nov 11 '22 12:11

j2emanue


As Bohsen suggested use livedata for query synchronously. But in some special case, we want to do some asynchronous operation based on logic. In below example case, I need to fetch some child comments for the parent comments. It is already available in DB, but need to fetch based on its parent_id in recyclerview adapter. To do this I used return concept of AsyncTask to get back the result. (Return in Kotlin)

Repositor Class

fun getChildDiscussions(parentId: Int): List<DiscussionEntity>? {
        return GetChildDiscussionAsyncTask(discussionDao).execute(parentId).get()
    }

private class GetChildDiscussionAsyncTask constructor(private val discussionDao: DiscussionDao?): AsyncTask<Int, Void, List<DiscussionEntity>?>() {
        override fun doInBackground(vararg params: Int?): List<DiscussionEntity>? {
            return discussionDao?.getChildDiscussionList(params[0]!!)
        }
    }

Dao Class

@Query("SELECT * FROM discussion_table WHERE parent_id = :parentId")
fun getChildDiscussionList(parentId: Int): List<DiscussionEntity>?
like image 27
Naveen Kumar M Avatar answered Nov 11 '22 12:11

Naveen Kumar M


Well, the right answer is to use ListenableFuture or Observable depending if you need one shot query or a new value emitted after database change and the framework you want to use.

From the doc "To prevent queries from blocking the UI, Room does not allow database access on the main thread. This restriction means that you must make your DAO queries asynchronous. The Room library includes integrations with several different frameworks to provide asynchronous query execution."

Exemple with a one shot query. You just have to add this in your gradle file.

// optional - Guava support for Room, including Optional and ListenableFuture
implementation "androidx.room:room-guava:$room_version"

Then your SQL query in your DAO become.

@Query("SELECT * FROM " + Event.TABLE_NAME)
ListenableFuture<List<Event>> getEventList();

Last step is the future call itself.

 ListenableFuture<List<Event>> future = dao.getEventList();
 future.addListener(new Runnable() {
    @Override
    public void run() {
        try {
            List<Event>> result = future.get();
        } catch (ExecutionException | InterruptedException e) {
        }
    }
}, Executors.newSingleThreadExecutor());

Source : https://developer.android.com/training/data-storage/room/async-queries#guava-livedata

like image 36
Zhar Avatar answered Nov 11 '22 11:11

Zhar