I used MVVM architecture to build an Android application with a Repository as a mediation between the ViewModel
and Room database. In one of the functions, I retrieve a list of objects from a table in the repository class so that I can call it from the ViewModel
class.
how to retrieve the first element in the list from the repository class instead of observing the LiveData
in any one of the activities?
Here is my repository class:
public class StudentRepository {
private StudentDAO mStudentDAO;
private LiveData<List<Student>> mAllStudents;
public StudentRepository(Application application){
StudentRoomDatabase db = StudentRoomDatabase.getDatabase(application);
mStudentDAO= db.StudentDAO();
mAllStudents= mStudentDAO.getAllStudents();
}
public LiveData<List<Word>> getAllStudents(){
// What I need to return the first element in the mAllStudents list
// in order to type the first student name in the Log.d
**Log.d("First_Name",mAllStudent.get(0));
// I want to type the student name in the Log without adding a function in
the viewModel and observe that in any Activity**
return mAllStudents;
}
}
how to get the first or (any) element from a LiveData List in Android MVVM architecture
If you want to get a particular element from a LiveData list, then you need to limit your query with an offset.
By default limiting a query doesn't consider an offset to it; so the default offset value is 0.
For instance, in your Dao
:
The below queries of Example 1 & 2 are equivalent, because the default offset value is 0.
Example
@Query("SELECT * FROM students LIMIT :limit")
LiveData<List<Student>> getStudents(int limit);
// Query
getStudents(1); // returns the first row of the list
Example 2
@Query("SELECT * FROM students LIMIT :limit OFFSET :offset")
LiveData<List<Student>> getStudents(int limit, int offset);
// Query
getStudents(1, 0); // returns the first row of the list
Note: Here I am assuming your model class is Student
This is about the first row; but to return row number x
then you need to manipulate the offset to be: x-1
, as the offset is 0-based value
Example 3 (Same Dao query as Example 2)
getStudents(1, 1); // returns the second row
getStudents(1, 4); // returns the fifth row
If you want to return more than one row, then you need to manipulate the LIMIT
value, so to return x
rows from the results, then limit the query with x
.
getStudents(2, 1); // returns the second and third rows
getStudents(3, 4); // returns the fifth, sixth, and seventh rows
Hope this addresses your question
Edit as per comments
I already have a list returned by another query
@Query("SELECT * FROM
students)
LiveData<List<Student>> getStudents();
So the returned value is a list. I want to get the first element from the list. This answer returns the first element truly, but I need to pass all the steps (to define this method in theViewModel
class and observe it in theMainActivity
in order to get the list or any element in the list). What I need is to type the first value in the list while I am using the function in the repository class. –
Now you are using below query
@Query("SELECT * FROM students")
LiveData<List<Student>> getStudents();
And you want to:
ViewModel
class
and observe it in the MainActivity
in order to get the list or any
element in the list).So to do that:
In Dao:: Change your query to
@Query("SELECT * FROM students LIMIT :limit OFFSET :offset")
LiveData<List<Student>> getAllStudents(int limit, int offset);
In Repository:
public class StudentRepository {
...
public LiveData<List<Student>> getAllStudents(final int limit, final int offset) {
return mDAO.getAllStudents(limit, offset);
}
}
In ViewModel:
public LiveData<List<Student>> getAllStudents(final int limit, final int offset) {
return mRepository.getAllStudents(limit, offset);
}
In Activity:
private void getAllStudents(int limit, int offset) {
mViewModel.getAllStudents(limit, offset).observe(this, new Observer<List<Student>>() {
@Override
public void onChanged(List<Student> students) {
if (students != null) {
// Here you can set RecyclerView list with `students` or do whatever you want
}
}
});
}
And to test that:
getAllStudents(1, 0); // return first row from the table.
getAllStudents(2, 0); // return first 2 rows from the table.
getAllStudents(2, 1); // return 2nd and 3rd rows from the table.
getAllStudents(-1, 5); // remove first 5 rows from the table.
And to return the first element in the mAllStudents list in order to type the first student name in the Log.d
So, In your Activity
mViewModel.getAllStudents(1, 0).observe(this, new Observer<List<Student>>() {
@Override
public void onChanged(List<Student> students) {
if (students != null) {
Student student = students.get(0);
Log.d("First_Name", student.getName());
}
}
});
Edit is it possible to return any element of the list without following all the steps such as writing a function in the ViewModel and observe that in the MainActivity? My question is not to follow all the steps.
Yes it's possible, but the above model is the recommended model by Google, You can return a List<Student>
from Dao
query instead of LiveData<List<Student>>
, but the bad news are:
LiveData
do that for free.LiveData
; so you have to manually
refresh the list to check any update as you won't be able to use the observer pattern.So, you can omit using ViewModel
and Repository
, and do all the stuff from the activity as follows:
private Executor mExecutor = Executors.newSingleThreadExecutor();
public void getAllStudents(final int limit, final int offset) {
final StudentDAO mDAO = StudentDatabase.getInstance(getApplicationContext()).getStudentDAO();
mExecutor.execute(new Runnable() {
@Override
public void run() {
List<Student> students = mDAO.getAllStudents(limit, offset);
Student student = students.get(0);
Log.d("First_Name", student.getName());
}
});
}
// Usage: getAllStudents(1, 0);
And the query for the Dao:
@Query("SELECT * FROM students LIMIT :limit OFFSET :offset")
List<Student> getAllStudents(int limit, int offset);