I have a room DB class that creates 3 user objects -
#Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class UserDatabase extends RoomDatabase {
private static UserDatabase instance;
public abstract UserDao userDao();
public static synchronized UserDatabase getInstance(Context context) {
Log.d("inside observe - ", "inside database");
if (instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(), UserDatabase.class, "user_database").fallbackToDestructiveMigration().addCallback(roomUserCallback).build();
}
return instance;
}
private static RoomDatabase.Callback roomUserCallback = new RoomDatabase.Callback() {
#Override
public void onCreate(#NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
new PopulateDbAsyncTask(instance).execute();
}
};
//TODO - delete this in the future. This is just for populating.
private static class PopulateDbAsyncTask extends AsyncTask<Void, Void, Void> {
static final String URL = "https://www.shortlist.com/media/images/2019/05/40-favourite-songs-of-famous-people-28-1556672663-9rFo-column-width-inline.jpg";
static final String URL2 = "https://img-s-msn-com.akamaized.net/tenant/amp/entityid/BBR9VUw.img?h=416&w=624&m=6&q=60&u=t&o=f&l=f&x=2232&y=979";
static final String URL3 = "https://dz9yg0snnohlc.cloudfront.net/new-what-famous-people-with-depression-have-said-about-the-condition-1.jpg";
private UserDao userDao;
private PopulateDbAsyncTask(UserDatabase db) {
userDao = db.userDao();
}
#Override
protected Void doInBackground(Void... voids) {
userDao.insert(new User(URL, "Barak Obama1", "/#BarakObama1"));
userDao.insert(new User(URL2, "Barak Obama2", "/#BarakObama2"));
userDao.insert(new User(URL3, "Barak Obama3", "/#BarakObama3"));
return null;
}
}
}
I am using viewmodel in order to fetch the users as LiveData.
For some reason, at the first time I install my app, I get one extra "barak obama1" user created, and immedeatly after than all 3 "normal" users by order - barak obama3, 2 and 1.
Here is my MainActivity -
private ArrayList<User> usersList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fetchUserList();
}
private void fetchUserList() {
userViewModel = ViewModelProviders.of(this).get(UserViewModel.class);
final Observer<List<User>> userObserver = users -> {
Log.d("inside observe - ", "inside main activity, list size - " + users.size());
usersList = (ArrayList) users;
initViewsAndListeners();
addCards();
};
userViewModel.getAllUsers().observe(this, userObserver);
}
private void addCards(){
TinderCardView tinderCardView;
for (int i = 0; i < usersList.size(); i++) {
tinderCardView = new TinderCardView(this);
tinderCardView.bind(usersList.get(i));
Log.d("inside observe - ", "inside main activity, user value - " + usersList.get(i).getUsername());
tinderStackLayout.addCard(tinderCardView);
Log.d("addCardCalled - ", "\nindex value - " + i + "\n" +
"userlist size - " + usersList.size());
}
}
#Override
public void onClick(View view) {
int buttonTag = Integer.valueOf(String.valueOf(view.getTag()));
TinderCardView topCardOnStack = tinderStackLayout.getTopCardOnStack();
topCardOnStack.handleButtonPressed(buttonTag);
// if (buttonTag == 1) { // TODO - move logic to card class
// userViewModel.delete(usersList.get(0));
// //fetchUserList();
// }
}
private void initViewsAndListeners() {
tinderStackLayout = findViewById(R.id.activity_main_tinder_stack_layout);
mDeleteButton = findViewById(R.id.activity_main_delete_button);
mPassButton = findViewById(R.id.activity_main_pass_button);
mApproveButton = findViewById(R.id.activity_main_approve_button);
mDeleteButton.setOnClickListener(this);
mApproveButton.setOnClickListener(this);
mPassButton.setOnClickListener(this);
}
As you can see I have log messages all over so you can understand what I am about to show you now. I am getting one extra user, "barak obama1" user first and then after that all other 3 -
The livedata figures out that there was 1 user in the list, adds in as a card and than the DB creates new objects and the livedata recalls the method, adding 3 more users.
Why is this happening?? I would glady kiss someone's leg if he solves this issue, no joke.
edit -
here is my ViewModel -
public class UserViewModel extends AndroidViewModel {
private UserRepository repository;
private LiveData<List<User>> allUsers;
public UserViewModel(#NonNull Application application) {
super(application);
repository = new UserRepository(application);
allUsers = repository.getAllUsers();
}
public void insert(User user) {
repository.insert(user);
}
public void update(User user) {
repository.update(user);
}
public void delete(User user) {
repository.delete(user);
}
public void deleteAllUsers(){
repository.deleteAllUsers();
}
public LiveData<List<User>> getAllUsers() {
Log.d("inside observe - ", "inside viewmodel");
return allUsers;
}
}
and my respository -
public class UserRepository {
private UserDao userDao;
private LiveData<List<User>> allUsers;
public UserRepository(Application application) {
UserDatabase database = UserDatabase.getInstance(application);
userDao = database.userDao();
allUsers = userDao.getAllUsers();
}
public void insert(User user) {
new InsertUserAsyncTask(userDao).execute(user);
}
public void update(User user) {
new UpdateUserAsyncTask(userDao).execute(user);
}
public void delete(User user) {
new DeleteUserAsyncTask(userDao).execute(user);
}
public void deleteAllUsers() {
new DeleteAllUsersAsyncTask();
}
public LiveData<List<User>> getAllUsers() {
Log.d("inside observe - ", "inside repository");
return allUsers;
}
//TODO - migrate all 4 async tasks into one.
private static class InsertUserAsyncTask extends AsyncTask<User, Void, Void> {
private UserDao userDao;
private InsertUserAsyncTask(UserDao userDao) {
this.userDao = userDao;
}
#Override
protected Void doInBackground(User... users) {
userDao.insert(users[0]);
return null;
}
}
private static class UpdateUserAsyncTask extends AsyncTask<User, Void, Void> {
private UserDao userDao;
private UpdateUserAsyncTask(UserDao userDao) {
this.userDao = userDao;
}
#Override
protected Void doInBackground(User... users) {
userDao.update(users[0]);
return null;
}
}
private static class DeleteUserAsyncTask extends AsyncTask<User, Void, Void> {
private UserDao userDao;
private DeleteUserAsyncTask(UserDao userDao) {
this.userDao = userDao;
}
#Override
protected Void doInBackground(User... users) {
userDao.delete(users[0]);
return null;
}
}
private static class DeleteAllUsersAsyncTask extends AsyncTask<Void, Void, Void> {
private UserDao userDao;
private DeleteAllUsersAsyncTask() {
this.userDao = userDao;
}
#Override
protected Void doInBackground(Void... voids) {
userDao.deleteAllUsers();
return null;
}
}
}
edit -
here is my dao -
#Dao
public interface UserDao {
#Insert
void insert(User user);
#Update
void update(User user);
#Delete
void delete(User user);
#Query("DELETE FROM user_table")
void deleteAllUsers();
#Query("SELECT * FROM user_table ORDER BY id DESC")
LiveData<List<User>> getAllUsers();
}
Insert all users in 1 transaction.
2 approaches:
1. Create a function in dao that receive list of users.
2. Create a transaction in roomDB (google how. Very simple)
I prefer the first one
try to understand
i have made comments
this is raw code just to give you idea
private ArrayList<User> usersList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.usersList = new ArrayList(); // initalise the array list here
fetchUserList();
}
private void fetchUserList() {
userViewModel = ViewModelProviders.of(this).get(UserViewModel.class);
final Observer<List<User>> userObserver = users -> {
Log.d("inside observe - ", "inside main activity, list size - " + users.size());
usersList = (ArrayList) users; //dont do this ! instead follow the below instructions
// to do
for(User user : users){
if(!usersList.contains(user)){
usersList.add(user);
}
}
// to do ends here
initViewsAndListeners();
addCards();
};
userViewModel.getAllUsers().observe(this, userObserver);
}
}
look what i have done:
initalised the usersList
and when observing the users live data i use loop and in that loop i
check if this user is already added
do you get it ?
Related
I am new to android development and trying to build the UI for my application.
The app integrates with REST backend which accepts a search query and a list of items as response.
interface RetrofitEndpoint {
#GET("paged/list/endpoint")
Call<PagedList<Object>> getPagedList(#Query("query") String query, #Query("pageSize") int pageSize, Query("pageOffset") int pageOffset);
}
The UI displays one item at a time to the user.
I am loading the list into a recyclerview
public class SomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<Object> list;
// .. other overridden members
public void setList(List<Object> list) {
this.list = list;
notifyDataSetChanged();
}
public void addAll(List<Object> newList) {
int lastIndex = list.size() - 1;
list.addAll(newList);
notifyItemRangeInserted(lastIndex, newList.size());
}
}
The part that I am not able to figure out is how do I load more data when I reach the end(or before to avoid latency) of my recyclerview, is there any library/API that does this?
For paged list to work properly it requires bit more stuff in your app.This implementation uses view model, live data and room persistence so it works offline.
your build.gradle:
// ViewModel
def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
def paging_version = "2.1.2"
implementation "androidx.paging:paging-runtime:$paging_version"
Retrofit api:
interface RetrofitEndpoint {
#GET("paged/list/endpoint")
Call<List<YourObject>> getYourObjectList(#Query("query") String query, #Query("pageSize") int pageSize, Query("pageOffset") int pageOffset);
}
YourObject:
#Entity(tableName = "your_object")
public class YourObject implements Serializable {
#PrimaryKey(autoGenerate = true)
private int db_id;
...
Dao:
#Dao
public interface YourObjectDao{
/**
* Get your objects from the table.
* -------------------------------
* We get update every time the database update.
*
*
* #return your object from the table
*/
#Insert
void insert(YourObject yourObject);
#Insert
void insertList(List<YourObject> yourObjectList);
#Query("SELECT * FROM your_object")
DataSource.Factory<Integer, YourObject> getAllResults();
#Query("DELETE FROM your_object")
void deleteAll();
}
Database:
#androidx.room.Database(entities = {YourObject.class}, version = 1)
public abstract class Database extends RoomDatabase {
private static Database instance;
public abstract YourObjectDao get_your_object_dao();
public static synchronized Database getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(),
Database.class, DATABASE_NAME)
.fallbackToDestructiveMigration()
.addCallback(roomCallback)
.build();
}
return instance;
}
}
YourObjectBoundaryCallback:
public class YourObjectBoundaryCallback extends PagedList.BoundaryCallback<YourObject> {
private AppExecutors executors;
private Database database;
private YourObjectDao dao;
private Integer page_number;
public YourObjectBoundaryCallback (Application application, AppExecutors executors) {
//super();
this.executors = executors;
database = Database.getInstance(application);
dao = database.get_your_object_dao();
page_number=1;
}
#Override
public void onZeroItemsLoaded() {
super.onZeroItemsLoaded();
Log.d("log", "yourObjects onzeroitemsloaded");
fetchYourObjects(page_number);
}
#Override
public void onItemAtFrontLoaded(#NonNull YourObject itemAtFront) {
super.onItemAtFrontLoaded(itemAtFront);
Log.d("log", "yourObjects onItemAtFrontLoaded");
}
#Override
public void onItemAtEndLoaded(#NonNull YourObject itemAtEnd) {
super.onItemAtEndLoaded(itemAtEnd);
Log.d("log", "yourObjects onItemAtEndLoaded");
page_number=page_number+1;
fetchYourObjects(page_number);
}
public void fetchYourObjects(int pageNumber) {
RetrofitApi retrofitApi = RetrofitInstance.getRetrofitEndpoint();
Call<List<YourObject>> call = retrofitApi.getYourObjectList(query, pageSize,pageNumber);
call.enqueue(new Callback<List<YourObject>>() {
#Override
public void onResponse(Call<List<YourObject>> call, Response<List<YourObject>> response) {
if (!response.isSuccessful()) {
Log.d("log", "YourObjects Response unsuccesful: " + response.code());
return;
}
Log.d("log", "YourObjects Response ok: " + response.code());
List<YourObject> yourObjectsList = response.body();
insertListToDb(yourObjectsList );
}
#Override
public void onFailure(Call<List<YourObject>> call, Throwable t) {
Log.d("log", "yourObjects onFailure: " + t.getMessage());
}
});
}
public void insertListToDb(List<YourObject> list) {
Runnable runnable = () -> {
dao.insertList(list);
};
Runnable diskRunnable = () -> database.runInTransaction(runnable);
executors.diskIO().execute(diskRunnable);
}
}
YourObjects Repository:
public class YourObjectsRepository {
private LiveData<PagedList<YourObject>> yourObjectsPagedList;
private YourObjectBoundaryCallback yourObjectsBoundaryCallback;
private AppExecutors executors;
public YourObjectsRepository (Application application, AppExecutors executors) {
this.executors = executors;
Database database = Database.getInstance(application);
YourObjectDao dao = database.get_your_object_dao();
yourObjectsBoundaryCallback= new YourObjectBoundaryCallback (application, executors);
createYourObjectsPagedList(dao );
}
//this is configuration for your paged list, adjust per your requirements
private PagedList.Config getPagedListConfig(){
return (new PagedList.Config.Builder())
.setEnablePlaceholders(false)
.setPrefetchDistance(40)
.setInitialLoadSizeHint(60)
.setPageSize(20).build();
}
private void createYourObjectsPagedList(YourObjectDao dao){
yourObjectsPagedList= new LivePagedListBuilder<>(dao.getAllResults(), getPagedListConfig())
.setBoundaryCallback(yourObjectsBoundaryCallback).setFetchExecutor(executors.networkIO())
.build();
}
public LiveData<PagedList<YourObject>> getYourObjectsPagedList() {
return yourObjectsPagedList;
}
}
YourObjectsViewModel:
public class YourObjectsViewModel extends AndroidViewModel {
private YourObjectsRepository repo;
public YourObjectsViewModel (#NonNull Application application) {
super(application);
AppExecutors executors = new AppExecutors();
repo= new YourObjectsRepository (application, executors);
}
public LiveData<PagedList<YourObject>> getYourObjectsPagedList() {
return repo.getYourObjectsPagedList();
}
}
AppExecutors:
public class AppExecutors {
private final Executor diskIO;
private final Executor networkIO;
private final Executor mainThread;
private final Executor others;
private final Executor paging;
public AppExecutors(Executor diskIO, Executor networkIO, Executor mainThread, Executor others, Executor paging) {
this.diskIO = diskIO;
this.networkIO = networkIO;
this.mainThread = mainThread;
this.others = others;
this.paging = paging;
}
public AppExecutors() {
this(Executors.newSingleThreadExecutor(), Executors.newFixedThreadPool(3),
new MainThreadExecutor(), Executors.newSingleThreadExecutor(),
Executors.newFixedThreadPool(4));
}
public Executor diskIO() {
return diskIO;
}
public Executor networkIO() {
return networkIO;
}
public Executor mainThread() {
return mainThread;
}
public Executor others() {
return others;
}
public Executor paging() {
return paging;
}
private static class MainThreadExecutor implements Executor {
private Handler mainThreadHandler = new Handler(Looper.getMainLooper());
#Override
public void execute(#NonNull Runnable command) {
mainThreadHandler.post(command);
}
}
}
in your activity / fragment:
yourObjectsViewModel = new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(getActivity().getApplication())).get(YourObjectsViewModel.class);
yourObjectsViewModel.getYourObjectPagedList().observe(getViewLifecycleOwner(), new Observer<PagedList<TopRatedMovie>>() {
#Override
public void onChanged(PagedList<YourObject> results) {
Log.d("log", " onChanged list size: " + results.size());
yourAdapter.submitList(results);
}
});
In your adapter:
public class YourPagedListAdapter extends PagedListAdapter<YourObject,
RecyclerView.ViewHolder> {
If u have any questions feel free to ask.
you could add onScrollStateChanged listener to your RecyclerView to detect the current position of your RecyclerView, then add your logic to fetch in you desired certain condition
This question already has answers here:
Paging Library Filter/Search
(3 answers)
Closed 2 years ago.
I decided to optimize my code and therefore switch to liveData. I followed a tutorial on youtube (youtube link) but I do not quite understand how I can filter my recyclerView when the user enters a word since I do not store any list in my Adapter. I use a simple searchview filter system on my MainActivity.
Moreover, I use DiffUtil to update my recyclerView and I update my Adapter thanks to:
noteViewModel = new ViewModelProvider.AndroidViewModelFactory(getApplication()).create(NoteViewModel.class);
noteViewModel.getAllNotes().observe(this, adapter::submitList);
My code is almost identical to the video but here is a part of it:
ViewModel:
public class NoteViewModel extends AndroidViewModel {
private NoteRepository repository;
private LiveData<List<Note>> allNotes;
public NoteViewModel(#NonNull Application application) {
super(application);
repository = new NoteRepository(application);
allNotes = repository.getAllNotes();
}
public void insert(Note note) {
repository.insert(note);
}
public void update(Note note) {
repository.update(note);
}
public void delete(List<Note> notes) {
repository.delete(notes);
}
public LiveData<List<Note>> getAllNotes() {
return allNotes;
}
}
My repository:
public class NoteRepository {
private NotesDAO notesDAO;
private LiveData<List<Note>> allNotes;
public NoteRepository(Application application) {
NotesDB database = NotesDB.getInstance(application);
notesDAO = database.notesDAO();
allNotes = notesDAO.getAllNotes();
}
public void insert(Note note) {
new InsertNoteAsyncTask(notesDAO).execute(note);
}
public void update(Note note) {
new UpdateNoteAsyncTask(notesDAO).execute(note);
}
public void delete(List<Note> note) {
new DeleteNoteAsyncTask(notesDAO).execute(note);
}
public LiveData<List<Note>> getAllNotes() {
return allNotes;
}
private static class InsertNoteAsyncTask extends AsyncTask<Note, Void, Void> { // SOME STUFF }
private static class UpdateNoteAsyncTask extends AsyncTask<Note, Void, Void> { // SOME STUFF }
private static class DeleteNoteAsyncTask extends AsyncTask<List<Note>, Void, Void> { // SOME STUFF }
}
Finally, thanks to #EpicPandaForce, I did that:
My ViewModel:
public class NoteViewModel extends AndroidViewModel {
private NoteRepository repository;
private final LiveData<List<Note>> allNotes;
private MutableLiveData<String> filterText = new MutableLiveData<>();
public NoteViewModel(#NonNull Application application) {
super(application);
repository = new NoteRepository(application);
allNotes = Transformations.switchMap(filterText, (input) ->
{
if(input == null || input.equals(""))
return repository.getAllNotes();
else
return repository.filter(input);
});
}
public void setFilter(String query) {
filterText.setValue(query);
}
public LiveData<List<Note>> getAllNotes() {
return allNotes;
}
}
In my repository:
public LiveData<List<Note>> filter(String input) {
try {
return new FilterNoteAsyncTask(notesDAO).execute(input).get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
return null;
}
private static class FilterNoteAsyncTask extends AsyncTask<String, Void, LiveData<List<Note>>> {
private NotesDAO notesDAO;
private FilterNoteAsyncTask(NotesDAO notesDAO) {
this.notesDAO = notesDAO;
}
#Override
protected LiveData<List<Note>> doInBackground(String... strings) {
return notesDAO.filter(strings[0]);
}
}
And I perform the request in the database thanks to:
#Query("SELECT * FROM note_table WHERE LOWER(title) LIKE '%' || :search || '%'")
LiveData<List<Note>> filter(String search);
I am using room as data store for my app. I am trying to save a list of sessions from a successful network call in viewmodel class. I have used a repository for interacting with the dao and asynctask for making crud operations async.
Now, I'm trying to display the "saved data" in a recyclerview but it shows nothing. On inspection of my database table, I find that nothing was saved. Here's my model class:
#Entity(tableName = "sessions")
public class Sessions{
// #PrimaryKey(autoGenerate = true)
// public int id;
#SerializedName("prg_session_image")
public String sessionImage;
#SerializedName("prg_session_name")
public String session_name;
#SerializedName("prg_session_id") // used session id as PK
#PrimaryKey
#NonNull
public String prog_sessionId;
#SerializedName("prg_session_description")
public String session_desc;
#SerializedName("reference_id")
public String reference_id;
#SerializedName("prg_name")
public String program_name;
#SerializedName("batch_name")
public String batch_name;
#SerializedName("player_count")
public String participants_count;
#SerializedName("prg_session_focus_points")
public String session_focus_points;
#SerializedName("prg_session_equipment")
public String equipments_reqd;
#SerializedName("session_complete")
public String is_complete;
public Sessions() {
}
// public int getId() {
// return id;
// }
public String getSessionImage() {
return sessionImage;
}
public void setSessionImage(String sessionImage) {
this.sessionImage = sessionImage;
}
public String getSession_name() {
return session_name;
}
public void setSession_name(String session_name) {
this.session_name = session_name;
}
public String getProg_sessionId() {
return prog_sessionId;
}
public void setProg_sessionId(String prog_sessionId) {
this.prog_sessionId = prog_sessionId;
}
public String getSession_desc() {
return session_desc;
}
public void setSession_desc(String session_desc) {
this.session_desc = session_desc;
}
public String getReference_id() {
return reference_id;
}
public void setReference_id(String reference_id) {
this.reference_id = reference_id;
}
public String getProgram_name() {
return program_name;
}
public void setProgram_name(String program_name) {
this.program_name = program_name;
}
public String getBatch_name() {
return batch_name;
}
public void setBatch_name(String batch_name) {
this.batch_name = batch_name;
}
public String getParticipants_count() {
return participants_count;
}
public void setParticipants_count(String participants_count) {
this.participants_count = participants_count;
}
public String getSession_focus_points() {
return session_focus_points;
}
public void setSession_focus_points(String session_focus_points) {
this.session_focus_points = session_focus_points;
}
public String getEquipments_reqd() {
return equipments_reqd;
}
public void setEquipments_reqd(String equipments_reqd) {
this.equipments_reqd = equipments_reqd;
}
public String getIs_complete() {
return is_complete;
}
public void setIs_complete(String is_complete) {
this.is_complete = is_complete;
}
}
And Dao class:
#Dao
public interface SessionsDAO {
// #Insert
// LiveData<List<Sessions>> saveSessions(List<Sessions> sessions);
#Insert
void addSessions(List<Sessions> list);
#Query("select * from sessions")
LiveData<List<Sessions>> getAllSessions();
#Query("select * from sessions where prog_sessionId = :id")
Sessions getSessionById(String id);
}
In repository, I have asynctasks for various operations with the Dao:
public class SessionsRepository {
public SessionsDAO dao;
private MutableLiveData<List<Sessions>> querySingleSession;
private LiveData<List<Sessions>> allSessions;
public SessionsRepository(Application application){
SportsDatabase database = SportsDatabase.getInstance(application);
dao = database.sessionsDAO();
querySingleSession = new MutableLiveData<>();
allSessions = dao.getAllSessions();
}
public void saveSessions(List<Sessions> sessions){
new SaveSessionsTask(dao).execute(sessions);
}
public LiveData<List<Sessions>> getAllSessions() {
return allSessions;
}
public void getSessionById(List<Sessions> sessions){
querySingleSession.setValue(sessions);
}
public class SaveSessionsTask extends AsyncTask<List<Sessions>, Void, Void>{
private SessionsDAO dao;
public SaveSessionsTask(SessionsDAO dao) {
this.dao = dao;
}
#Override
protected Void doInBackground(List<Sessions>... lists) {
dao.addSessions(lists[0]);
return null;
}
}
// public void getSessions(){
// new GetSessionsTask(dao).execute();
// }
// public class GetSessionsTask extends AsyncTask<Void, >
}
I am trying to at the moment save all the results from network call and display them from the database. Here's my operation in viewmodel class:
public class HomeSessionsViewModel extends AndroidViewModel {
private static final String TAG = HomeSessionsViewModel.class.getSimpleName();
private MutableLiveData<SessionDetails> liveDetails;
private SessionsRepository repository;
public HomeSessionsViewModel(#NonNull Application application) {
super(application);
repository = new SessionsRepository(application);
}
// public HomeSessionsViewModel (Application application){
// repository = new SessionsRepository(application);
// }
public MutableLiveData<SessionDetails> getSessions(){
if (liveDetails == null){
liveDetails = new MutableLiveData<>();
fetchSessions();
}
return liveDetails;
}
private void fetchSessions(){
String coachId = "4086";
Call<SessionDetails> call = RestClient.getRestInstance().getSessionsService().fetchSessions(coachId);
call.enqueue(new Callback<SessionDetails>() {
#Override
public void onResponse(Call<SessionDetails> call, Response<SessionDetails> response) {
if (response.isSuccessful()){
SessionDetails details = response.body();
List<Sessions> sessions = details.getSessions();
Log.d(TAG, "N/w sesh size:\t" + sessions.size());
liveDetails.setValue(details); // now just displaying from network
saveSessions(sessions);
}
}
#Override
public void onFailure(Call<SessionDetails> call, Throwable t) {
}
});
}
private void saveSessions(List<Sessions> sessions) {
repository.saveSessions(sessions);
}
public LiveData<List<Sessions>> fetchSessionsDB(){
return repository.getAllSessions();
}
}
and in ui controller (fragment), I have called the viewmodel's fetchSessionsDB() method but no data is shown. The network request works well as I was displaying from there before adding room. What could be wrong here? Thank you.
API Response:
{
"session_details": [
{
"prg_session_name": "Session-16",
"prg_session_id": "987",
"prg_session_equipment": null,
"prg_session_description": "",
"prg_session_focus_points": "",
"prg_session_image": "http://devsports.copycon.in/includes/uploads/Jellyfish5.jpg",
"session_complete": "0",
"prg_name": "cricket coaching",
"reference_id": "293",
"batch_id": "57",
"batch_name": "Batch 3",
"player_count": "10"
}, .... ]}
and SessionDetails POJO:
public class SessionDetails {
#SerializedName("session_details")
#Expose
private List<Sessions> sessions;
#SerializedName("status")
private String status;
#SerializedName("message")
private String msg;
public List<Sessions> getSessions() {
return sessions;
}
}
fragment class where db data should be displayed:
private void populateSessions() {
sessionsRV = fragmentBinding.sessionsRV;
sessionsRV.setHasFixedSize(false);
LinearLayoutManager hlm = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
sessionsRV.setLayoutManager(hlm);
sessionsViewModel = ViewModelProviders.of(this).get(HomeSessionsViewModel.class);
// sessionsViewModel.fetchSessions(""); // TODO: 3/16/2019 Use coach id from db
// calling db from viewmodel
sessionsViewModel.fetchSessionsDB().observe(this, new Observer<List<Sessions>>() {
#Override
public void onChanged(#Nullable List<Sessions> sessions) {
sessionsAdapter = new SessionsAdapter(getActivity(), sessions);
sessionsRV.setAdapter(sessionsAdapter);
Log.d(TAG, "Sessions Count:\t" + sessionsAdapter.getItemCount()); // logs 0
}
});
// previously from network directly displayed
// sessionsViewModel.getSessions().observe(this, new Observer<SessionDetails>() {
// #Override
// public void onChanged(#Nullable SessionDetails details) {
// List<Sessions> list = details.getSessions();
// sessionsAdapter = new SessionsAdapter(getActivity(), list);
// sessionsRV.setAdapter(sessionsAdapter);
// Log.d(TAG, "Sessions Count:\t" + sessionsAdapter.getItemCount());
// }
// });
}
Sports Database class:
#Database(entities = {CoachDB.class, Sessions.class}, version = 1, exportSchema = false)
public abstract class SportsDatabase extends RoomDatabase {
private static SportsDatabase instance;
public abstract CoachDAO coachDAO();
public abstract SessionsDAO sessionsDAO();
public static synchronized SportsDatabase getInstance(Context context) {
if (instance == null){
instance = Room.databaseBuilder(context.getApplicationContext(), SportsDatabase.class, "sports_db")
.fallbackToDestructiveMigration()
.build();
}
return instance;
}
}
I have solved this issue by modifying my #insert method in dao like
#Dao
public interface SessionsDAO {
#Insert
void addSessions(List<Sessions> sessions);
#Query("select * from sessions")
LiveData<List<Sessions>> getAllSessions();
#Query("select * from sessions where prog_sessionId = :id")
Sessions getSessionById(String id);
}
and run my async task with a list of sessions as input and it worked successfully.
private void saveSessions(List<Sessions> sessions) {
new SaveSessionsTask(dao).execute(sessions);
}
public class SaveSessionsTask extends AsyncTask<List<Sessions>, Void, Void> {
private SessionsDAO dao;
public SaveSessionsTask(SessionsDAO dao) {
this.dao = dao;
}
#Override
protected Void doInBackground(List<Sessions>... lists) {
dao.addSessions(lists[0]);
return null;
}
}
How do you get data from your Room entity using AsyncTask? I've been looking through the answers regarding AsyncTask but I don't get it.
This is my Dao:
#Dao
public interface BudgetDao {
#Insert
public void insert (Budget budget);
#Update
public void update (Budget budget);
#Query("SELECT id FROM budget_table WHERE category = :category AND date = :date")
int getId(String category, String date);
#Query("SELECT * FROM budget_table")
LiveData<List<Budget>> getAllBudgets();
#Query("SELECT category FROM budget_table")
List<String> getAllCategories();
}
How do I get the AsyncTask in my repository to return a list of strings? Right now the code is public void getAllCategories() {new getAllCatsAsyncTask(mBudgetDao).execute(); When I change the void to List, an error shows that I can't return a list of strings
My Repository:
public class BudgetRepository {
private BudgetDao mBudgetDao;
private LiveData<List<Budget>> mAllBudgets;
BudgetRepository(Application application) {
BudgetRoomDatabase db = BudgetRoomDatabase.getDatabase(application);
mBudgetDao = db.budgetDao();
mAllBudgets = mBudgetDao.getAllBudgets();
}
LiveData<List<Budget>> getAllBudgets() {return mAllBudgets;}
public void insert (Budget budget) {new insertAsyncTask(mBudgetDao).execute(budget);}
public void getAllCategories() {new getAllCatsAsyncTask(mBudgetDao).execute();}
//How do I return a list of strings?
private static class insertAsyncTask extends AsyncTask<Budget, Void, Void> {
//code
}
private class getAllCatsAsyncTask extends AsyncTask<Void, Void, List<String>> {
private BudgetDao mAsyncTaskDao;
getAllCatsAsyncTask(BudgetDao dao) {mAsyncTaskDao = dao;}
#Override
protected List<String> doInBackground(Void... voids) {
return mAsyncTaskDao.getAllCategories();
}
}
}
In my ViewModel:
public class BudgetViewModel extends AndroidViewModel {
private BudgetRepository mRepository;
private LiveData<List<Budget>> mAllBudgets;
public BudgetViewModel(#NonNull Application application) {
super(application);
mRepository = new BudgetRepository(application);
mAllBudgets = mRepository.getAllBudgets();
}
LiveData<List<Budget>> getAllBudgets() {return mAllBudgets;}
public void insert(Budget budget) {mRepository.insert(budget);}
public List<String> getAllCats() { return mRepository.getAllCategories();}
//Android Studios show incompatible types (Void vs List<String>)
Right now, in my ViewModel, mRepository.getAllCategories does not return a list of strings.
Do I have to call onPostExecute in my AsyncTask? How do I link the results from onPostExecute so that getAllCategories can return a list of strings in my AsyncTask? Or is there a better way to call for queries from my Dao?
Try this code..
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DisplayData displayData=new DisplayData(studentDatabase);
displayData.execute();
}
/**
* this method display student.
*/
private void DisplayStudent() {
mStudentList.addAll(studentDatabase.studentDao().getStudent());
}
private class DisplayData extends AsyncTask<Void,Void,Void>
{
private StudentDatabase studentDatabase;
public DisplayData(StudentDatabase studentDatabase) {
this.studentDatabase = studentDatabase;
}
#Override
protected Void doInBackground(Void... params) {
DisplayStudent();
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
mDialog.dismiss();
mStu_adapter=new StudentAdapter(mStudentList,getApplicationContext());
mStu_adapter.notifyDataSetChanged();
mRvStudent.setAdapter(mStu_adapter);
DividerItemDecoration divider=new DividerItemDecoration(getApplicationContext(),LinearLayout.VERTICAL);
divider.setDrawable(ContextCompat.getDrawable(getApplicationContext(),R.drawable.divider));
mRvStudent.addItemDecoration(divider);
}
}
if you want to allow on mainThread then set this line.
db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "database-name").allowMainThreadQueries().build();
I'm learning MVVM with databinding and I have problem with null objects in my TaskViewModel. I've created two constructors, and one of them is empty, but I have null in Task object. When I had only one constructor in viewmodel it made error with instance of viewmodel. What I'm doing wrong?
If you want more code please let me know.
TaskViewModel
public class TaskViewModel extends ViewModel {
private TaskRepository mTaskRepository;
private LiveData<List<Task>> taskMutableLiveData;
private String TAG = "TaskViewModel = ";
private Context context;
public final ObservableField<String> description = new ObservableField<>();
public final ObservableField<String> date = new ObservableField<>();
public final ObservableField<String> time = new ObservableField<>();
public TaskViewModel(TaskRepository taskRepository) {
mTaskRepository = taskRepository;
}
public TaskViewModel() {
}
public void insert() {
addTask(description.get(), date.get(), time.get());
Log.d(TAG, "DATA " + description + " / " + date + " / " + time);
}
public void addTask(String description, String date, String time) {
Task addTask = new Task(description, date, time);
mTaskRepository.insert(addTask);
}
}
TaskRepository
public class TaskRepository {
private TaskDao taskDao;
private LiveData<List<Task>> allTasks;
public TaskRepository(Application application) {
TaskDatabase database = TaskDatabase.getInstance(application);
taskDao = database.taskDao();
allTasks = taskDao.getAllTasks();
}
public void insert(Task task) {
new InsertTaskAsyncTask(taskDao).execute(task);
}
public void update(Task task) {
new UpdateTaskAsyncTask(taskDao).execute(task);
}
public void delete(Task task) {
new DeleteTaskAsyncTask(taskDao).execute(task);
}
public LiveData<List<Task>> getAllTasks() {
return allTasks;
}
private static class InsertTaskAsyncTask extends AsyncTask<Task, Void, Void>
{
private TaskDao taskDao;
private InsertTaskAsyncTask(TaskDao taskDao) {
this.taskDao = taskDao;
}
#Override
protected Void doInBackground(Task... tasks) {
taskDao.insert(tasks[0]);
return null;
}
}
private static class DeleteTaskAsyncTask extends AsyncTask<Task, Void, Void>
{
private TaskDao taskDao;
private DeleteTaskAsyncTask(TaskDao taskDao) {
this.taskDao = taskDao;
}
#Override
protected Void doInBackground(Task... tasks) {
taskDao.delete(tasks[0]);
return null;
}
}
private static class UpdateTaskAsyncTask extends AsyncTask<Task, Void, Void>
{
private TaskDao taskDao;
private UpdateTaskAsyncTask(TaskDao taskDao) {
this.taskDao = taskDao;
}
#Override
protected Void doInBackground(Task... tasks) {
taskDao.update(tasks[0]);
return null;
}
}
}
Instead of making parameterized constructor for ViewModel, make your initialization in default constructor. because after all, we're consuming ViewModel from ViewModelProviders which has default constructor call from it (if you haven't customized it yet).
So do like below,
public class TaskViewModel extends ViewModel {
private TaskRepository mTaskRepository;
...
public TaskViewModel() {
mTaskRepository = new TaskRepository(); // This will avoid your null pointer exception
}
public void insert() {
addTask(description.get(), date.get(), time.get());
Log.d(TAG, "DATA " + description + " / " + date + " / " + time);
}
public void addTask(String description, String date, String time) {
Task addTask = new Task(description, date, time);
mTaskRepository.insert(addTask);
}
}
If you want to have Context in your ViewModel, use AndroidViewModel instead which will provide you application context in default constructor implementation. but don't store it in your ViewModel though.