Null objects in viewmodel - android

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.

Related

How should I code if i want Query specific the with Room? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
I am now learing how to save data in a local database using Room in Android Dev Doc. And I had done the Codelab in this links.https://developer.android.com/codelabs/android-room-with-a-view#0 I can Upddate, Insert, Delete and Query all data in table.
Now, I want Query the data with specific id. How should I code in ViewModel and Repository class? Thanks.
My table named "Diary",
Diary.class
#Entity(tableName = "diary_table")
public class Diary {
#PrimaryKey(autoGenerate = true)
private int id;
#ColumnInfo(name = "diary_title")
private String diary_Title;
#ColumnInfo(name = "diary_content")
private String diary_Content;
public Diary(#NonNull String diary_Title, String diary_Content) {
this.diary_Title = diary_Title;
this.diary_Content = diary_Content;
}
public void setDiary_Title(String diary_Title) {
this.diary_Title = diary_Title;
}
public void setDiary_Content(String diary_Content) {
this.diary_Content = diary_Content;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public String getStrId() {
return String.valueOf(id);
}
public String getDiary_Title() {
return diary_Title;
}
public String getDiary_Content() {
return diary_Content;
}
}
DiaryDao.class
#Dao
public interface DiaryDao {
#Insert
void insertDiaries(Diary... diaries);
#Update
void updateDiaries(Diary... diaries);
#Delete
void deleteDiaries(Diary... diaries);
//删除到一无所有 慎用!
#Query("DELETE FROM diary_table")
void deleteAllDiaries();
#Query("SELECT * FROM diary_table ORDER BY ID ASC")
LiveData<List<Diary>>getAllDiariesLive();//get all diary
#Query("SELECT * FROM diary_table WHERE ID=:id")
LiveData<Diary> getSpecificDiariesLive(int id);//get specific diary
}
DiaryRepository
class DiaryRepository {
private LiveData<Diary> specificDiary;
private LiveData<List<Diary>> allDiariesLive;
private DiaryDao diaryDao;
DiaryRepository(Context context) {
DiaryRoomDatabase diaryRoomDatabase = DiaryRoomDatabase.getDiaryDatabase(context.getApplicationContext());
diaryDao = diaryRoomDatabase.getDiaryDao();
allDiariesLive = diaryDao.getAllDiariesLive();
specificDiary = diaryDao.getSpecificDiariesLive(/**how should i code here***/);
}
void insetDiaries(Diary... diaries) {
new InsertAsyncTask(diaryDao).execute(diaries);
}
void updateDiaries(Diary... diaries) {
new UpdateAsyncTask(diaryDao).execute(diaries);
}
void deleteDiaries(Diary... diaries) {
new DeleteAsyncTask(diaryDao).execute(diaries);
}
void deleteAllDiaries(Diary... diaries) {
new DeleteAllAsyncTask(diaryDao).execute();
}
LiveData<Diary> getSpecificDiary(int i) {return specificDiary; }
LiveData<List<Diary>> getAllDiariesLive() {
return allDiariesLive;
}
static class InsertAsyncTask extends AsyncTask<Diary, Void, Void> {
private DiaryDao diaryDao;
InsertAsyncTask(DiaryDao diaryDao) {
this.diaryDao = diaryDao;
}
#Override
protected Void doInBackground(Diary... diaries) {
diaryDao.insertDiaries(diaries);
return null;
}
}
static class UpdateAsyncTask extends AsyncTask<Diary, Void, Void> {
private DiaryDao diaryDao;
UpdateAsyncTask(DiaryDao diaryDao) {
this.diaryDao = diaryDao;
}
#Override
protected Void doInBackground(Diary... diaries) {
diaryDao.updateDiaries(diaries);
return null;
}
}
static class DeleteAsyncTask extends AsyncTask<Diary, Void, Void> {
private DiaryDao diaryDao;
DeleteAsyncTask(DiaryDao diaryDao) {
this.diaryDao = diaryDao;
}
#Override
protected Void doInBackground(Diary... diaries) {
diaryDao.deleteDiaries(diaries);
return null;
}
}
static class DeleteAllAsyncTask extends AsyncTask<Void, Void, Void> {
private DiaryDao diaryDao;
DeleteAllAsyncTask(DiaryDao diaryDao) {
this.diaryDao = diaryDao;
}
#Override
protected Void doInBackground(Void... voids) {
diaryDao.deleteAllDiaries();
return null;
}
}
}
DiaryViewModel.class
public class DiaryViewModel extends AndroidViewModel {
private DiaryRepository diaryRepository;
public DiaryViewModel(#NonNull Application application) {
super(application);
diaryRepository = new DiaryRepository(application);
}
public LiveData<Diary> getSpecificDiary(/**how should i code here?**/) {
return diaryRepository.getSpecificDiary(i);
}
public LiveData<List<Diary>> getAllDiariesLive() {
return diaryRepository.getAllDiariesLive();
}
public void insertDiaries(Diary... diaries) {
diaryRepository.insetDiaries(diaries);
}
public void updateDiaries(Diary... diaries) { diaryRepository.updateDiaries(diaries); }
public void deleteDiary(Diary... diaries) { diaryRepository.deleteDiaries(diaries);}
public void deleteAllDiaries() {
diaryRepository.deleteAllDiaries();
}
}
I think in DiaryRepository:
class DiaryRepository {
private LiveData<List<Diary>> allDiariesLive;
private DiaryDao diaryDao;
DiaryRepository(Context context) {
DDiaryRoomDatabase diaryRoomDatabase = DiaryRoomDatabase.getDiaryDatabase(context.getApplicationContext());
diaryDao = diaryRoomDatabase.getDiaryDao();
allDiariesLive = diaryDao.getAllDiariesLive();
}
void insetDiaries(Diary... diaries) {
new InsertAsyncTask(diaryDao).execute(diaries);
}
void updateDiaries(Diary... diaries) {
new UpdateAsyncTask(diaryDao).execute(diaries);
}
void deleteDiaries(Diary... diaries) {
new DeleteAsyncTask(diaryDao).execute(diaries);
}
void deleteAllDiaries(Diary... diaries) {
new DeleteAllAsyncTask(diaryDao).execute();
}
LiveData<Diary> getSpecificDiary(int i) {
return diaryDao.getSpecificDiariesLive(i);
}
LiveData<List<Diary>> getAllDiariesLive() {
return allDiariesLive;
}
...
so your ViewModel will be changed to this:
public class DiaryViewModel extends AndroidViewModel {
private DiaryRepository diaryRepository;
public DiaryViewModel(#NonNull Application application) {
super(application);
diaryRepository = new DiaryRepository(application);
}
public LiveData<Diary> getSpecificDiary(int i) {
return diaryRepository.getSpecificDiary(i);
}
...
DiaryRepository.class and ViewModel.class like hosseini sajad code. And i should use this with livedata like follow:
mDiaryViewModel = new ViewModelProvider(activity).get(DiaryViewModel.class);
mDiaryViewModel.getSpecificDiary(pos+1).observe(activity, diary -> {
Log.d("test", "Here is the Title: " + diary.getDiary_Title() + "\n" + "Here is the Content: " +diary.getDiary_Content() );
});

How to use ExecutorService with Android Room?

I am using ExecutorService with Android Room to avoid multiple in insetAsyncTasks. I was using Debanjan example from this post: link but I modified it a bit.
Problem is that I don't get any exception but nothing is inserted.
DaoAsyncProcessor:
public abstract class DaoAsyncProcessor<T> {
public interface DaoProcessCallback<T>{
void onResult(T result);
}
private DaoProcessCallback daoProcessCallback;
public DaoAsyncProcessor(DaoProcessCallback daoProcessCallback) {
this.daoProcessCallback = daoProcessCallback;
}
protected abstract T doAsync();
public void start(){
new DaoProcessAsyncTask().execute();
}
private class DaoProcessAsyncTask extends AsyncTask<Void, Void, T> {
#Override
protected T doInBackground(Void... params) {
return doAsync();
}
#Override
protected void onPostExecute(T t) {
if(daoProcessCallback != null)
daoProcessCallback.onResult(t);
}
}
}
Repository:
public void createUpdate(final Projects item){
EncertDatabase.databaseWriteExecutor.execute(() ->{
if(item!=null){
if(item.getId()==null){
encertDAO.insert(item);
}
else {
encertDAO.update(item);
}
}else {
Log.e("Krkec", "Item was null");
}
});
}
Database:
public abstract class EncertDatabase extends RoomDatabase {
private static volatile EncertDatabase INSTANCE;
public abstract EncertDAO encertDAO();
private static final int NUMBER_OF_THREADS = 4;
public static final ExecutorService databaseWriteExecutor =
Executors.newFixedThreadPool(NUMBER_OF_THREADS);
public static EncertDatabase getDatabase(final Context context){
if(INSTANCE== null){
synchronized (EncertDatabase.class){
if (INSTANCE ==null){
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), EncertDatabase.class,"encertDatabase").fallbackToDestructiveMigration().build();
}
}
}
return INSTANCE;
}
}
What am I doing wrong?
assign executor service instance to db builder instance using setQueryExecutor

Android pagination for search query

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

Room database is returning 1 extra object

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 ?

Room Insert List of POJO and Retrieve in Recyclerview

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

Categories

Resources