ViewModelProvider.NewInstanceFactory - receive same instance - android

I want to re-use ViewModel and LiveData for reading nodes from Firebase. This is my code in Fragment
FirebaseDatabaseViewModel test = ViewModelProviders.of(this, new FirebaseDatabaseViewModel.Factory(getActivity().getApplication(),"node1")).get(FirebaseDatabaseViewModel.class);
LiveData<DataSnapshot> ldTest = test.getDataSnapshotLiveData();
ldTest.observe(this, new Observer<DataSnapshot>() {
#Override
public void onChanged(#Nullable DataSnapshot dataSnapshot) {
Log.d("MyTag", "liveData.observe TEST dataSnapshot = " + dataSnapshot);
}
});
FirebaseDatabaseViewModel test2 = ViewModelProviders.of(this, new FirebaseDatabaseViewModel.Factory(getActivity().getApplication(),"node2")).get(FirebaseDatabaseViewModel.class);
LiveData<DataSnapshot> ldTest2 = test2.getDataSnapshotLiveData();
ldTest2.observe(this, new Observer<DataSnapshot>() {
#Override
public void onChanged(#Nullable DataSnapshot dataSnapshot) {
Log.d("MyTag", "liveData.observe TEST2 dataSnapshot = " + dataSnapshot);
}
});
}
Here is ViewModel
public class FirebaseDatabaseViewModel extends AndroidViewModel {
private final String mRef;
private final FirebaseQueryLiveData liveData;
public FirebaseDatabaseViewModel(Application application, String ref) {
super(application);
this.mRef = ref;
this.liveData = new FirebaseQueryLiveData(FirebaseDatabase.getInstance().getReference(mRef));
}
#NonNull
public LiveData<DataSnapshot> getDataSnapshotLiveData() {
return liveData;
}
public static class Factory extends ViewModelProvider.NewInstanceFactory {
#NonNull
private final Application mApplication;
private final String mRef;
public Factory(#NonNull Application application, String ref) {
mApplication = application;
this.mRef = ref;
}
#NonNull
#Override
public <T extends ViewModel> T create(#NonNull Class<T> modelClass) {
return (T) new FirebaseDatabaseViewModel(mApplication, mRef);
}
}
}
Here is LiveData
public class FirebaseQueryLiveData extends LiveData<DataSnapshot> {
private final Query query;
private final MyValueEventListener listener = new MyValueEventListener();
public FirebaseQueryLiveData(Query query) {
this.query = query;
}
public FirebaseQueryLiveData(DatabaseReference ref) {
this.query = ref;
}
#Override
protected void onActive() {
Log.d("MyTag", "onActive");
query.addValueEventListener(listener);
}
#Override
protected void onInactive() {
Log.d("MyTag", "onInactive");
query.removeEventListener(listener);
}
private class MyValueEventListener implements ValueEventListener{
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
setValue(dataSnapshot);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e("MyTag", "Can't listen to query " + query, databaseError.toException());
}
}
}
Problem is reading same node from FirebaseDatabase
D/MyTag: liveData.observe TEST dataSnapshot = DataSnapshot { key = node1, value = {.....
D/MyTag: liveData.observe TEST2 dataSnapshot = DataSnapshot { key = node1, value = {....
Second time I expected node2

The default ViewModelProvider only keeps a single ViewModel for a given class name. The only time your Factory is invoked is when there's no already existing ViewModel - in your case, you're using the same class name for both calls, so your second factory is never used.
Generally, you should consider only having a single ViewModel and have it return multiple different LiveData instances based on the key passed into it, keeping a HashMap<String,LiveData> to avoid recreating LiveData objects it already has:
public class FirebaseDatabaseViewModel extends AndroidViewModel {
private HashMap<String, LiveData<DataSnapshot>> mLiveDataMap = new HashMap<>();
public FirebaseDatabaseViewModel(#NonNull final Application application) {
super(application);
}
public LiveData<DataSnapshot> getDataSnapshotLiveData(String ref) {
if (!mLiveDataMap.containsKey(ref)) {
// We don't have an existing LiveData for this ref
// so create a new one
mLiveDataMap.put(ref, new FirebaseQueryLiveData(
FirebaseDatabase.getInstance().getReference(ref)));
}
return mLiveDataMap.get(ref);
}
}

Related

Retrofit response from view-model to observe in fragment using RxJava

I have a Login Fragment which uses API call to login. I use mvvm and databinding to bind views with viewmodel. In viewmodel Login Response via retrofit is observed in viewmodel which uses RxJava.
I need to observe the retrofit response in the loginFragment, which is not get observed when retrofit response came. Following are the fragment and viewmodel code. I need retrofit response to pass to fragment or fragment get automatically observe response.
public class LoginFragment extends Fragment {
private LoginViewModel mLoginViewModel;
private Observable<LoginResult> dataObservable;
public static String TAG = LoginFragment.class.getSimpleName();
public Disposable disposable;
public static Fragment LoginFragmentInstance() {
Log.e(TAG, "LoginFragmentInstance: " );
Fragment fragment = new LoginFragment();
return fragment;
}
public LoginFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
FragmentLoginBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_login, container, false);
mLoginViewModel = new LoginViewModel(getActivity());
//setViewModel method name changes based on variable name declared in XML
//mLoginViewModel.loginResult.observeO
dataObservable= mLoginViewModel.loginResult;
disposable = dataObservable.subscribe(new Consumer<LoginResult>() {
#Override
public void accept(LoginResult result) throws Exception {
Log.d("TAG", result.toString());
}
});
binding.setViewModel(mLoginViewModel);
return binding.getRoot();
}
#Override
public void onDestroy() {
mLoginViewModel.destroy();
disposable.dispose();
super.onDestroy();
}
}
ViewModel File
public class LoginViewModel {
private static final String TAG = "LoginViewModel";
public ObservableField<String> userName = new ObservableField<>();
public ObservableField<String> password = new ObservableField<>();
public ObservableField<String> email = new ObservableField<>();
public ObservableField<String> userNameErr = new ObservableField<>();
public ObservableField<String> passwordErr = new ObservableField<>();
public ObservableField<String> emailErr = new ObservableField<>();
public Observable<LoginResult> loginResult = new Observable<LoginResult>() {
#Override
protected void subscribeActual(Observer<? super LoginResult> observer) {
}
};
public ObservableField<Boolean> enableLogin;
private CompositeDisposable myCompositeDisposable = new CompositeDisposable();
private HashMap<String, String> loginApiParams;
public Action signIn;
public Context context;
public LoginViewModel(final Context context) {
this.context = context;
Observable result = Observable.combineLatest(FieldUtils.toObservable(userName), FieldUtils.toObservable(password),
new BiFunction() {
#Override
public Object apply(Object userName, Object password) throws Exception {
int failCount = 0;
if (!InputValidator.validateMobileno(userName.toString())) {
++failCount;
userNameErr.set(context.getResources().getString(R.string.mobileno_incorrect));
} else {
userNameErr.set("");
}
if (!InputValidator.validatePassword(password.toString())) {
++failCount;
passwordErr.set(context.getResources().getString(R.string.password_incorrect));
} else {
passwordErr.set("");
}
return failCount == 0;
}
});
enableLogin = FieldUtils.toField(result);
signIn = new Action() {
#Override
public void run() throws Exception {
Log.d(TAG, "signIn button clicked");
loginCall();
}
};
}
private void loginCall() {
loginApiParams = new HashMap<>();
// loginApiParams.put(, paymentType.toString())
loginApiParams.put(ApiParameterKeyConstants.MOBILE,userName.get());
loginApiParams.put(ApiParameterKeyConstants.PASSWORD, password.get());
UserApi usersService = ApiService.INSTANCE.apiCall();
Disposable disposable = usersService.getLogin(loginApiParams)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<LoginResult>() {
#Override
public void accept(LoginResult result) throws Exception {
loginResult = Observable.just(result);
//loginResult.subscribe()
//loginResult = result ;
//Log.d(TAG, "Login Successfull");
}
}, new Consumer<Throwable>()
{
#Override
public void accept(Throwable throwable) throws Exception {
Log.d(TAG, "Login Failed");
}
});
myCompositeDisposable.add(disposable);
}
}
It seems like you are re-assigning the loginResult an Observable in your loginCall method of the ViewModel instead of passing the result to its Observers.
You should try calling loginResult.onNext(result) or loginResult.onComplete(result) instead of loginResult = Observable.just(result);

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 ?

java.lang.RuntimeException: Cannot create an instance of ViewModel class

I am trying to instantiate UserViewModel in my activity however it keeps giving me a java.lang.RuntimeException: Cannot create an instance of viewmodel class kindly assist.
This is how my ViewModel looks like
public class UserViewModel extends AndroidViewModel {
private NodeAuthService api;
private SharedPreferences pref;
private static MutableLiveData<List<User>> userDetails = new MutableLiveData<>();
public UserViewModel(#NonNull Application application) {
super(application);
api = AuthRetrofitClient.getInstance().create(NodeAuthService.class);
}
private String email = pref.getString("email", "");
public void loadUser(){
Call<List<User>> call;
call = api.getUser(email);
call.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
userDetails.postValue(response.body());
}
#Override
public void onFailure(Call<List<User>> call, Throwable t) {
Log.d("USER",t.getMessage());
}
});
}
public MutableLiveData<List<User>>getUserDetails(){
return userDetails;
}
}
This is how my activity is setup
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.navigation_drawer);
String nameVm;
userViewModel = ViewModelProviders.of(this).get(UserViewModel.class);
userViewModel.loadUser();
userViewModel.getUserDetails().observe(this, new Observer<List<User>>() {
#Override
public void onChanged(List<User> users) {
if (users != null){
for (int i = 0; i<users.size(); i++){
nameVm = String.valueOf(users.get(0));
}
}
}
});
}
Create ViewModelFactory class
public class MyViewModelFactory implements ViewModelProvider.Factory {
private Application mApplication;
public MyViewModelFactory(Application application) {
mApplication = application;
}
#Override
public <T extends ViewModel> T create(Class<T> modelClass) {
// Replace UserViewModel → with whatever or however you create your ViewModel
return (T) new UserViewModel(mApplication);
}
}
and init ViewModel like
UserViewModel myViewModel = ViewModelProviders.of(this, new MyViewModelFactory(this.getApplication())).get(UserViewModel.class);

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

Android MVP Null object Reference

I am trying to do the MvP Pattern and I am separating firebase queries in the model part. I get null object reference exception when I call it on the presenter.
Model:
public class LoginModelz implements LoginContract.LoginModelz {
private LoginPresenter loginPresenter;
FirebaseAuth mAuth; //= FirebaseAuth.getInstance();
FirebaseUser currentuser;// = mAuth.getCurrentUser();
public LoginModelz(LoginPresenter loginPresenter) {
this.loginPresenter = loginPresenter;
}
public void FirebaseQuery(){
mAuth = FirebaseAuth.getInstance();
currentuser = mAuth.getCurrentUser();
}
}
Presenter:
public class LoginPresenter implements LoginContract.LoginPresenter {
private LoginView loginView;
private LoginModelz loginModelz;
public LoginPresenter(LoginView loginView) {
this.loginView = loginView;
}
public LoginPresenter(LoginModelz loginModelz) {
this.loginModelz = loginModelz;
}
#Override
public void OnLogin(String email, String password,final Context context) {
loginModelz.FirebaseQuery();
Log.e("asda",context.toString());
if(email.isEmpty() && password.isEmpty()){
loginView.OnLoginFail("Email and Password is empty");
return;
}
else if(email.isEmpty()){
loginView.OnLoginFail("Email is empty");
return;
}else if(password.isEmpty()){
loginView.OnLoginFail("Password is empty");
return;
}else {
loginView.ProgressShow("ASDASDASDASDSAd");
Log.e("emailpass", email+password);
**loginModelz.mAuth.signInWithEmailAndPassword(email,password)**
.addOnCompleteListener( new OnCompleteListener<AuthResult>()
I was getting an error from the loginmodelz.mAuth. If i put the initializations in the method in the presenter class it works just fine, but when i separated it is getting a null object reference.
Interfaces
public interface LoginContract {
interface LoginView{
void OnLoginSuccess();
void OnLoginFail(String message);
void Onregister();
void ProgressShow(String messsagge);
void ProgressDismiss(String message);
void Alert();
}
interface LoginPresenter{
void OnLogin(String email,String password,final Context context);
void OnVerify(Context context);
}
interface LoginModelz{
void FirebaseQuery();
}
}
This looks suspicious:
public LoginPresenter(LoginView loginView) {
this.loginView = loginView;
}
public LoginPresenter(LoginModelz loginModelz) {
this.loginModelz = loginModelz;
}
You have to decide on your constructor, if you call the first one you don't have a model and so on.
You could create your model inside the first constructor and pass it as an argument:
public LoginPresenter(LoginView loginView) {
this.loginView = loginView;
this.loginModel = new LoginModel(this);
}
At least that's what I can suggest from the code you provided.
You have not initialized loginModelz.
Change your presenter constructor implement
public LoginPresenter(LoginView loginView) {
this.loginView = loginView;
this.loginModelz = new LoginModelz(this);
}

Categories

Resources