Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Room without LiveData

I am experimenting with Room database. I don't want my data to be observed, I just want to fetch data from the database once. How to achieve this using MVVM?

The problem I faced: If I try to fetch data without AsyncTask it gives: Can not access database on the main thread since it may potentially lock the UI for a long period of time(As expected) and if I use AsyncTask, the method returns null List as method returns before AsyncTask is complete.

Dao class:

@Query("SELECT * FROM student_table where StudentName = :studentName")List<Student> getStudentWithSameName(String studentName);

Repository:

public List<Student> getAllStudentWithSameName(String studentName) {
    new GetAllStudentWithSameNameAsyncTask(studentDao).execute(studentName);
    return studentsWithSameName;
}



private class GetAllStudentWithSameNameAsyncTask extends AsyncTask< String,Void, List<Student> > {

    StudentDao studentDao;

    public GetAllStudentWithSameNameAsyncTask(StudentDao studentDao) {
        this.studentDao = studentDao;
    }

    @Override
    protected List<Student> doInBackground(String... strings) {
        List<Student> students = studentDao.getStudentWithSameName(strings[0]);
        return students;
    }

    @Override
    protected void onPostExecute(List<Student> students) {
        studentsWithSameName = students;
        super.onPostExecute(students);
    }
}

ViewModel:

public List<Student> getStudentWithSameName(String studentName) {
    studentsWithSameName = studentRepository.getAllStudentWithSameName(studentName);
    return studentsWithSameName;
}

MainActivity:

viewModel = ViewModelProviders.of(this).get(StudentViewModel.class);
List<Student> students = viewModel.getStudentWithSameName("Bill");
like image 826
Shivam Avatar asked Feb 14 '19 23:02

Shivam


1 Answers

You'll need to use an asynchronous ("suspend") function since a database call could potentially take a long time. Then to use the result, you'll have to invoke a block of code on completion instead of running it right away.


In my YourClassDao.kt change fun to suspend fun, and LiveData<List<YourClass>> to just List<YourClass>:

// original: this returns a LiveData object
@Query("SELECT * FROM my_table WHERE my_field = :myId")
fun getMyObject(myId: String): LiveData<List<YourClass>>

becomes:

// new: this returns a normal object
@Query("SELECT * FROM my_table WHERE my_field = :myId")
suspend fun getMyObject(myId: String): List<YourClass>

To use the data, you'll need to start an asyncrnous job to get the data, and then invokeOnCompletion the code you need to use the data.

// using the second (suspend fun) version from above
fun useMyData() {
    val database = AppDatabase.getInstance(context).YourClassDao()  // context could be an activity, for example.

    // start an async job to get the data
    val getDataJob = GlobalScope.async { database.getMyObject("someId") }

    // tell the job to invoke this code when it's done
    getDataJob.invokeOnCompletion { cause ->
        if (cause != null) {
            // error!  Handle that here
            Unit
        } else {
            val myData = getDataJob.getCompleted()

            // ITEM 1
            // ***************************
            // do something with your data
            // ***************************

            Unit  // this is just because the lambda here has to return Unit
        }
    }

    // ITEM 2 - this might happen before ITEM 1
} 
like image 170
Cullub Avatar answered Nov 03 '22 01:11

Cullub