Cannot go to MainActivity when using LiveData - android

I have splash screen without a layout file. This is what I have tried:
public class SplashActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!isNotAuthenticated()) {
openLoginInActivity();
} else {
openMainActivity();
}
finish();
}
private void openMainActivity() {
viewModel.idLiveData.observe(this, new Observer<String>() {
#Override
public void onChanged(String id) {
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
intent.putExtra("id", id);
startActivity(intent); //Go to MainActivity
}
});
}
}
Using this code, I get this error:
2019-09-03 12:03:06.615 1871-1934/? E/ViewRootImpl[myapp]: Could not unlock surface
java.lang.IllegalArgumentException
at android.view.Surface.nativeUnlockCanvasAndPost(Native Method)
at android.view.Surface.unlockSwCanvasAndPost(Surface.java:382)
at android.view.Surface.unlockCanvasAndPost(Surface.java:363)
at android.view.ViewRootImpl.drawSoftware(ViewRootImpl.java:3451)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:3339)
If i get the creation if the intent outside onChanged(), everything works fine. I have added a log statement and onChanged is not even triggered. So how can I move to the next activity without that error?
Edit:
public class SplashViewModel extends ViewModel {
private SplashRepository splashRepository;
MutableLiveData<String> idLiveData;
#Inject
SplashViewModel(SplashRepository splashRepository) {
this.splashRepository = splashRepository;
idLiveData = splashRepository.addIdToLiveData();
}
}

How I used live data in Splash Activity
class SplashActivity : BaseActivity<ActivitySplashBinding, SplashViewModel>() {
override val mViewModel: SplashViewModel by currentScope.inject()
override fun getLayoutResId(): Int {
return R.layout.activity_splash
}
override fun initialization() {
}
override fun initializeObserver() {
mViewModel.liveData.observe(this, Observer {
if (it) {
launchActivityWithFinish<LoginActivity>()
}
})
}}
Here is My SplashViewModel
class SplashViewModel : BaseViewModel() {
val liveData = MutableLiveData<Boolean>()
init {
viewModelScope.launch {
delay(SPLASH_TIME_OUT)
liveData.postValue(true)
}
}}
My SplashActivity In java
public class SplashActivity1 extends BaseActivity<ActivitySplashBinding, SplashViewModel> {
#NotNull
#Override
protected SplashViewModel getMViewModel() {
return new SplashViewModel();
}
#Override
public int getLayoutResId() {
return R.layout.activity_splash;
}
#Override
public void initialization() {
}
#Override
public void initializeObserver() {
getMViewModel().getLiveData().observe(this, aBoolean -> {
if (aBoolean) {
Intent intent = new Intent(SplashActivity1.this, MainActivity.class);
intent.putExtra("id", "id");
startActivity(intent); //Go to MainActivity
finish();
}
});
}}
My SplashViewModel In Java
public class SplashViewModel extends BaseViewModel {
public MutableLiveData<Boolean> liveData = new MutableLiveData<>(false);
public SplashViewModel(){
new Handler().postDelayed(() -> liveData.postValue(true),SPLASH_TIME_OUT);
}}
It perfectly works for me in Kotlin & Java both

I had similar problems with navigation in the past. I usually fixed them by verifying in onChanged that the parameter (in your case the String id) is not null and inside call a view model method that changes the id to null.
viewModel.idLiveData.observe(this, new Observer<User>() {
#Override
public void onChanged(String id) {
if(id != null) {
viewModel.navigationDone();
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
intent.putExtra("id", id);
startActivity(intent); //Go to MainActivity
}
}
});
where navigationDone is a view model method like:
void onNavigationDone() {
idLiveData.setValue(null);
}

Related

Cannot observe LiveData from parent Activity

I created an abstract GlobalActivity extending AppCompatActivity and a GlobalViewModel extending ViewModel, in order to have some LiveData always ready to show Dialog messages and Toast messages, as well as displaying and hiding a ProgressBar. Problem is that the LoginActivity is not observing the LiveData object I mentioned above, so is not reacting to changes nor calls. Here is my code:
GlobalActivity:
public abstract class GlobalActivity extends AppCompatActivity {
protected GlobalViewModel mGlobalViewModel = new GlobalViewModel();
private Consumer<Throwable> errorHandler = throwable -> {
Timber.e(throwable);
DialogUtils.showOneButtonDialog(this, R.string.unexpected_error, null);
};
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RxJavaPlugins.setErrorHandler(this.errorHandler);
setUpBasicViewModel();
mGlobalViewModel.getDialogMessage().observe(this, mssg -> DialogUtils.showOneButtonDialog(GlobalActivity.this, mssg, null));
mGlobalViewModel.getToastMessage().observe(this, mssg -> DialogUtils.showMessage(mssg));
mGlobalViewModel.getIsLoading().observe(this, bool -> setLoadingState(bool));
}
public abstract void setLoadingState(boolean bool);
public abstract void setUpBasicViewModel();
}
GlobalViewModel:
public class GlobalViewModel extends ViewModel {
protected MutableLiveData<String> dialogMessage = new MutableLiveData<>();
protected MutableLiveData<String> toastMessage = new MutableLiveData<>();
protected SingleLiveEvent<Boolean> isLoading = new SingleLiveEvent<>();
public GlobalViewModel(){}
public MutableLiveData<String> getDialogMessage() {
return dialogMessage;
}
public MutableLiveData<String> getToastMessage() {
return toastMessage;
}
public SingleLiveEvent<Boolean> getIsLoading() {
return isLoading;
}
}
LoginActivity:
public class LoginActivity extends GlobalActivity {
private LoginViewModel mLoginViewModel;
private ActivityLoginBinding mDataBinding;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLoginViewModel = new ViewModelProvider(this, new LoginViewModelFactory()).get(LoginViewModel.class);
mDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_login);
mDataBinding.setLifecycleOwner(this);
mDataBinding.setViewModel(mLoginViewModel);
}
#Override
public void setLoadingState(boolean bool) {
mDataBinding.progressBar.setVisibility(mDataBinding.progressBar.isShown() ? View.GONE : View.VISIBLE);
}
#Override
public void setUpBasicViewModel() {
mGlobalViewModel = ViewModelProviders.of(this).get(GlobalViewModel.class);
}
...
}

Recyclerview data disappears when device is rotated

Even though I am using ViewModel, whenever the device is rotated, the data in the Recyclerview disappears. I had to put the makeSearch() method inside the onClick() method because I need to get the text that the button grabs and use it as the search parameter. Is there a better way I can handle this to avoid this problem? My code is right here:
SearchActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
// What happens when the search button is clicked
materialButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (Objects.requireNonNull(textInputEditText.getText()).toString().isEmpty()) {
textInputEditText.setError("Type a search query");
} else {
mSearchInput = Objects.requireNonNull(textInputEditText.getText()).toString();
textInputEditText.setText("");
makeSearch();
}
}
});
}
// Gets the ViewModel, Observes the Question LiveData and delivers it to the Recyclerview
private void makeSearch() {
final SearchAdapter searchAdapter = new SearchAdapter();
SearchViewModel mSearchViewModel = new ViewModelProvider(this,
new CustomSearchViewModelFactory(new SearchRepository())).get(SearchViewModel.class);
mSearchViewModel.setQuery(mSearchInput);
mSearchViewModel.getQuestionLiveData().observe(this, new Observer<List<Question>>() {
#Override
public void onChanged(List<Question> questions) {
mQuestions = questions;
searchAdapter.setQuestions(questions);
}
});
mRecyclerView.setAdapter(searchAdapter);
searchAdapter.setOnClickListener(mOnClickListener);
}
SearchViewModel:
public class SearchViewModel extends ViewModel {
private SearchRepository mSearchRepository;
private MutableLiveData<String> mSearchLiveData = new MutableLiveData<>();
private LiveData<List<Question>> mQuestionLiveData = Transformations.switchMap(mSearchLiveData, (query) -> {
return mSearchRepository.getQuestions(query);
});
SearchViewModel(SearchRepository searchRepository) {
this.mSearchRepository = searchRepository;
}
public LiveData<List<Question>> getQuestionLiveData() {
return mQuestionLiveData;
}
public void setQuery(String query) {
mSearchLiveData.setValue(query);
}
}
SearchRepository:
public class SearchRepository {
//private String inTitle;
private MutableLiveData<List<Question>> mQuestions = new MutableLiveData<>();
public SearchRepository() {
//getQuestionsWithTextInTitle();
}
private void getQuestionsWithTextInTitle(String inTitle) {
ApiService apiService = RestApiClient.getApiService(ApiService.class);
Call<QuestionsResponse> call = apiService.getQuestionsWithTextInTitle(inTitle);
call.enqueue(new Callback<QuestionsResponse>() {
#Override
public void onResponse(Call<QuestionsResponse> call, Response<QuestionsResponse> response) {
QuestionsResponse questionsResponse = response.body();
if (questionsResponse != null) {
mQuestions.postValue(questionsResponse.getItems());
//shouldShowData = true;
} else {
Log.d("SearchRepository", "No matching question");
//shouldShowData = false;
}
}
#Override
public void onFailure(Call<QuestionsResponse> call, Throwable t) {
//shouldShowData = false;
t.printStackTrace();
}
});
}
public LiveData<List<Question>> getQuestions(String inTitle) {
getQuestionsWithTextInTitle(inTitle);
return mQuestions;
}
}
Your approach of passing the search input in through your CustomSearchViewModelFactory and into the constructor for the ViewModel and into the constructor for your SearchRepository isn't going to work in any case. While the first time you search your CustomSearchViewModelFactory creates the ViewModel, the second time you hit search, your SearchViewModel is already created and your factory is not invoked a second time, meaning you never get the second query.
Instead, you should file the ViewModel Overview documentation, and use Transformations.switchMap() to convert your input (the search string) into a new LiveData<List<Question>> for that given query.
This means that your ViewModel would look something like
public class SearchViewModel extends ViewModel {
private SearchRepository mSearchRepository;
private MutableLiveData<String> mSearchLiveData = new MutableLiveData<String>();
private LiveData<List<Question>> mQuestionLiveData =
Transformations.switchMap(mSearchLiveData, (query) -> {
return mSearchRepository.getQuestions(query);
});
public SearchViewModel() {
mSearchRepository = new SearchRepository();
}
public void setQuery(String query) {
mSearchLiveData.setValue(query);
}
public LiveData<List<Question>> getQuestionLiveData() {
return mQuestionLiveData;
}
}
You'd then update your Activity to:
Always observe the getQuestionLiveData() (note that you won't get a callback to your Observer until you actually set the first query)
Call setQuery() on your SearchViewModel in your makeSearch()
Remove your CustomSearchViewModelFactory entirely (it would no longer be needed).

I got java.lang.IllegalStateException: No activity error when I update androidX libraries

I got this error while I am going to show a dialog box
Error:-
This error coming when I update below androidx's Libraries
1. implementation 'androidx.appcompat:appcompat:1.1.0-rc01
2. implementation 'com.google.android.material:material:1.1.0-alpha09'
----- This is my Confirm Dialog ----
public class ConfirmDialog extends BaseDialogFragment {
public static ConfirmDialog newInstance() {
return new ConfirmDialog();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Error Comes
}
#Override
protected Dialog createDialog(Context activityContext) {
AlertDialog.Builder builder;
builder = new AlertDialog.Builder(activityContext, android.R.style.Theme_Material_Light_Dialog_Alert);
AlertDialog alertDialog = builder.create();
alertDialog.setCanceledOnTouchOutside(false);
return alertDialog;
}
#Override
public void onClick(DialogInterface dialog, int which) {
super.onDialogClick(dialog, which, which);
}
}
----- This is my BaseDialog ----
public abstract class BaseDialogFragment extends DialogFragment implements DialogInterface.OnClickListener {
protected OnDialogClickListener onClickListener;
protected BaseDialogFragment() {
}
protected BaseDialogFragment(OnDialogClickListener onClickListener) {
this.onClickListener = onClickListener;
}
public static void show(BaseDialogFragment dialogFragment, Context context) {
dialogFragment.onCreate(null);
Dialog dialog = dialogFragment.createDialog(context);
dialog.show();
}
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return createDialog(getActivity());
}
protected abstract Dialog createDialog(Context activityContext);
public void setOnClickListener(OnDialogClickListener onClickListener) {
this.onClickListener = onClickListener;
}
public void removeOnClickListener() {
this.onClickListener = null;
}
public void onDialogClick(DialogInterface dialog, int which, Object o) {
if (this.onClickListener != null) {
this.onClickListener.onDialogClick(dialog, which, o);
}
}
}
--- And Last, This is when I call my Confirmdialog ---
ConfirmDialog confirmDialog = ConfirmDialog.newInstance();
confirmDialog.setOnClickListener(new OnDialogClickListener() {
#Override
public void onDialogClick(DialogInterface dialog, int which, Object o) {
}
});
ConfirmDialog.show(confirmDialog, activity); // When I call this
You can try:
ConfirmDialog.show(confirmDialog, activity);
instead
ConfirmDialog.show(confirmDialog, this);
Pass context or instance of the activity
Dialog Fragment should handle click events inside, not in activity/fragment where you are initializing it.
And for comunication between dialog fragment and activity create interface implemented by activity.
For example
interface DialogFragmentResultListener {
fun onDialogResultReceived(requestCode: Int, isPositive: Boolean)
}
and pass result from DialogFragment like this:
if (activity is DialogFragmentResultListener) {
......
}
You Can Try like this method -
BaseDialogFragment -
abstract class BaseDialogFragment : DialogFragment(), View.OnClickListener, BaseView {
override fun onClick(v: View?) {
}
abstract fun initObjects()
abstract fun registerListeners()
abstract fun initWidgets()
protected fun setFullScreen() {
val width = ViewGroup.LayoutParams.MATCH_PARENT
val height = ViewGroup.LayoutParams.MATCH_PARENT
dialog?.window?.setLayout(width, height)
}
override fun onNetworkFailure(errorCode: Int, errorMessage: String?) {
(activity!! as BaseActivity).onNetworkFailure(errorCode, errorMessage)
}
override fun onResponseFailure(error: CloudError?) {
(activity!! as BaseActivity).onResponseFailure(error)
}
override fun showLoader(msg: String?) {
(activity!! as BaseActivity).showLoader(msg)
}
override fun showLoader(msg: String?, isCancellable: Boolean?) {
(activity!! as BaseActivity).showLoader(msg, isCancellable)
}
override fun dismissLoader() {
(activity!! as BaseActivity).dismissLoader()
}
}
Create your confirm Dialog like this -
public class AddEmailDialogFragment extends BaseDialogFragment implements View.OnClickListener {
public static final String TAG = AddEmailDialogFragment.class.getSimpleName();
private Button btnSubmit;
private EditText etEmail;
private GetEmailCallback getEmailCallBack;
public static AddEmailDialogFragment newInstance() {
return new AddEmailDialogFragment();
}
public void setListener(GetEmailCallback listener) {
getEmailCallBack = listener;
}
#Nullable
#Override
public View onCreateView(#NotNull LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_askemail, container, false);
}
#Override
public void onViewCreated(#NotNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initObjects();
initWidgets();
registerListeners();
}
#Override
public void initObjects() {
}
#Override
public void registerListeners() {
btnSubmit.setOnClickListener(this);
}
#Override
public void initWidgets() {
View view = getView();
btnSubmit = view.findViewById(R.id.btn_submit);
etEmail = view.findViewById(R.id.et_email);
}
#Override
public void onClick(View view) {
if (view.getId() == R.id.btn_submit) {
saveEmail();
}
}
private boolean isValidEmail(String target) {
return (!TextUtils.isEmpty(target) && Patterns.EMAIL_ADDRESS.matcher(target).matches());
}
private void saveEmail() {
if (isValidEmail(etEmail.getText().toString()))
getEmailCallBack.gotEMail(etEmail.getText().toString());
else
etEmail.setError(getString(R.string.err_invalidEmail));
}
}
And call the dialog fragment using this-
val activity = activity
if (null != activity) {
addEmailDialogFragment = AddEmailDialogFragment.newInstance()
addEmailDialogFragment!!.show(getActivity()!!.supportFragmentManager, AddEmailDialogFragment.TAG)
addEmailDialogFragment!!.isCancelable = false
addEmailDialogFragment!!.setListener(this)
}
I got this issue after updating the version of androidx appcompat dependency.
For Temporary I have resolved this issue by the below solution.
Use
implementation 'androidx.appcompat:appcompat:1.0.2'
instead of
implementation 'androidx.appcompat:appcompat:1.1.0'

When i try to open new activity this is = null when using dagger 2

this is my mvp module
#Module
public class LoginModule {
#Provides
public LoginPresenterImpl providePresenter(LoginView loginView , LoginInteractor loginInteractor) {
return new LoginPresenterImpl(loginView,loginInteractor);
}
#Provides
public LoginView loginView(){
return new LoginActivity();
}
#Provides
public LoginInteractor loginInteractor(Repository repository){
return new LoginInteractorImpl(repository);
}
#Provides
public Repository provideRepository(APIInterfaces.LoginInterface loginInterface){
return new Repository(loginInterface,loginView());
}
#Provides
public LoginPresenter loginPresenter(APIInterfaces.LoginInterface loginInterface){
return providePresenter(loginView(),loginInteractor(provideRepository(loginInterface)));
}
}
this my login activity
{
ProgressHUD progressHUD;
private LinearLayout main_layout;
private boolean flag = false;
private EditText email_edt, password_edt;
private ImageView login_btn;
private TextView sign_up_tv, forget_password_tv;
#Inject
public LoginPresenter loginPresenter;
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//getWindow().setStatusBarColor(Color.TRANSPARENT);
// flag = getIntent().getBooleanExtra("from_splash", false);
// loginPresenter = new LoginPresenterImpl(this,new LoginInteractorImpl());
MyApplication.getComponent().inject(this);
setControls();
} // onCreate()
#Override
protected void onDestroy() {
loginPresenter.onDestroy();
super.onDestroy();
}
private void setControls() {
main_layout = findViewById(R.id.main_login_layout);
email_edt = findViewById(R.id.edt_txt_email);
password_edt = findViewById(R.id.edt_txt_password);
sign_up_tv = findViewById(R.id.sign_up_txt);
forget_password_tv = findViewById(R.id.forget_password_txt);
login_btn = findViewById(R.id.login_btn);
login_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
loginPresenter.validateEmailPassword(email_edt.getText().toString().trim(),password_edt.getText().toString().trim());
}
});
sign_up_tv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
loginPresenter.openRegistrationPage();
}
});
forget_password_tv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
}
});
if(flag)
Logging.Snackbar(main_layout, getString(R.string.check_internet_msg));
} // setControls()
#Override
public void showProgress() {
//progressHUD = ProgressHUD.show(LoginActivity.this, getString(R.string.loading_txt), false, null, null);
}
#Override
public void hideProgress() {
//progressHUD.dismiss();
}
#Override
public void passwordError(int x) {
switch (x)
{
case 0 :
password_edt.setError(getString(R.string.password_length_error_msg));
break;
case 1 :
password_edt.setError(getString(R.string.empty_field_error_msg));
break;
}
}
#Override
public void emailError(int y) {
switch (y)
{
case 0 :
email_edt.setError(getString(R.string.empty_field_error_msg));
break;
case 1 :
email_edt.setError(getString(R.string.invalid_email_error_msg));
break;
case 2 :
email_edt.setError(getString(R.string.invalid_email_error_msg));
break;
}
}
#Override
public void openRegistration() {
Intent intent = new Intent(LoginActivity.this, RegistrationActivity.class);
startActivity(intent);
finish();
}
#Override
public void forgetPassword() {
}
#Override
public void navigateToHome(Response<LoginResponse> response) {
if (response.body().getError() == 0 && response.body().getMessage().equalsIgnoreCase("Login Success"))
{
UserData userData = new UserData();
Intent intent = new Intent(this,MainPageActivity.class);
userData = response.body().getData();
intent.putExtra("UserData",userData);
startActivity(intent);
finish();
}
else
{
Logging.Toast(this,response.body().getMessage());
}
}
#Override
public void onError() {
Logging.Toast(this,"Check Your Internet Connection");
}
}
when i try to navigate to home activity LoginActivity.this is return null how to solve this.
this is the error that i get
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
at android.content.ContextWrapper.getPackageName(ContextWrapper.java:141)
I Solved the problem by edit the MVP Module Like this
#Module
public class LoginModule {
private LoginView loginView;
public LoginModule(LoginView loginView) {
this.loginView = loginView;
}
#ActivityScope
#Provides
public LoginPresenterImpl providePresenter(LoginView loginView , LoginInteractor loginInteractor) {
return new LoginPresenterImpl(loginView,loginInteractor);
}
#ActivityScope
#Provides
public LoginView loginView(){
return loginView;
}
#ActivityScope
#Provides
public LoginInteractor loginInteractor(Repository repository){
return new LoginInteractorImpl(repository);
}
#ActivityScope
#Provides
public Repository provideRepository(APIInterfaces.LoginInterface loginInterface,APIInterfaces.getEventsInterface getEventsInterface){
return new Repository(loginInterface,getEventsInterface);
}
#ActivityScope
#Provides
public LoginPresenter loginPresenter(APIInterfaces.LoginInterface loginInterface,APIInterfaces.getEventsInterface getEventsInterface){
return providePresenter(loginView(),loginInteractor(provideRepository(loginInterface,getEventsInterface)));
}
}
and create activity scope for it

Android ViewModel LiveData update view on button click

I am following this tutorial to learn ViewModel and LiveData. In my case, instead of getting data from network, I am simply generating random string on button click and trying to update a textview. The problem is that the textview does not get updated when the data is changed by button click, but only gets updated when orientation is toggled.
Activity Class (extends LifecycleActivity)
public class PScreen extends BaseActivity {
#Override protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.places_screen);
final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
viewModel.init();
viewModel.getUser().observe(this, new Observer<User>() {
#Override public void onChanged(#Nullable User user) {
((TextView) findViewById(R.id.name)).setText(user.getName());
}
});
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
final MutableLiveData<User> data = new MutableLiveData<>();
User user = new User();
user.setName(String.valueOf(Math.random() * 1000));
data.postValue(user);
viewModel.setUser(data); // Why it does not call observe()
}
});
}
}
ViewModel Class
package timsina.prabin.tripoptimizer.model;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.ViewModel;
public class UserModel extends ViewModel {
private LiveData<User> user;
public void init() {
if (this.getUser() != null) {
return;
}
this.user = new LiveData<User>() {
#Override protected void setValue(User value) {
value.setName("Fresh New Name");
super.setValue(value);
}
};
}
public LiveData<User> getUser() {
return user;
}
public void setUser(LiveData<User> user) {
this.user = user;
}
}
You are creating a new LiveData instance each time! You are not supposed to do that. If you do that all previous observers will be ignored.
In this case you could replace your setUSer(LiveData<User>) method on your ViewModel to setUser(User u) (taking a User instead of a LiveData) and then do user.setValue(u) inside it.
Of course, will have to initialize the LiveData member in your ViewModel class, like this:
final private LiveData<User> user = new MutableLiveData<>();
It will work then because it will notify the existing observers.
I was somehow able to resolve this by using MutableLiveData instead of LiveData.
Model class
private MutableLiveData<User> user2;
public void init() {
if (user2 == null) {
user2 = new MutableLiveData<>();
}
}
public MutableLiveData<User> getUser2() {
return user2;
}
public void setUser2(final User user) {
user2.postValue(user);
}
Activity
viewModel.getUser2().observe(this, new Observer<User>() {
#Override public void onChanged(#Nullable User user) {
((TextView) findViewById(R.id.name)).setText(user.getName());
}
});
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
User user = new User();
viewModel.getUser().postValue(user);
}
});
You replace the reference to the object inside UserModel, try to swap the lines of code
data.postValue(user);
viewModel.setUser(data); // Why it does not call observe()
replace on
viewModel.setUser(data); // Why it does not call observe()
data.postValue(user);
Try to modify your code as #niqueco mentioned, set your updated method inside setUser() method and change your onclick() listener in the activity to send the new user data info only. Other works the LiveData will help u.
public class UserModel extends ViewModel {
private LiveData<User> user;
public void init() {
if (this.getUser() != null) {
return;
}
this.user = new LiveData<User>() {
#Override protected void setValue(User value) {
value.setName("Fresh New Name");
super.setValue(value);
}
};
}
public LiveData<User> getUser() {
return user;
}
public void setUser(LiveData<User> user) {
this.user.setValue(user); //the live data will help u push data
}
}
Activity Class
public class PScreen extends BaseActivity {
#Override protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.places_screen);
final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
viewModel.init();
viewModel.getUser().observe(this, new Observer<User>() {
#Override public void onChanged(#Nullable User user) {
((TextView) findViewById(R.id.name)).setText(user.getName());
}
});
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
//final MutableLiveData<User> data = new MutableLiveData<>();
User user = new User();
user.setName(String.valueOf(Math.random() * 1000));
//data.postValue(user);
viewModel.setUser(user); // Why it does not call observe()
}
});
}
}

Categories

Resources