简体   繁体   中英

observe() method gets called twice, how to prevent it?

The observe method gets called twice, first with the last value of the variable, and then with the updated value. For me it's a big problem, because I need to show a Toast on different situations and I don't know how to fix this. For example, if I call the function with the right credentials, it shows the Toast ("Login effettuato"), but then if I try to login with wrong credentials, first it shows the Toast ("Login effettuato"), then the Toast ("Errore"). Please help me.

public class LoginFragment extends Fragment {

    private LoginViewModel loginViewModel;
    private FragmentLoginBinding binding;
    public static final String SHARED_PREFS = "CREDENTIAL";
    public static final String key = "accessToken";
    private ExecutorService threadPool;

    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        threadPool = Executors.newFixedThreadPool(5);

        loginViewModel = new ViewModelProvider(this).get(LoginViewModel.class);
        binding = FragmentLoginBinding.inflate(inflater, container, false);
        View root = binding.getRoot();

        ImageView backgroundLoginImg = binding.backgroundLoginImg;
        Glide.with(getContext()).load(R.drawable.wood).into(backgroundLoginImg);

        EditText email = binding.emailText;
        EditText password = binding.editTextPassword;
        Button login = binding.loginButton;

        login.setOnClickListener(view -> {

            if (!email.getText().toString().equals("") && !password.getText().toString().equals("")) {

                loginViewModel.login(email.getText().toString(), password.getText().toString())
                        .observe(getViewLifecycleOwner(), accessToken -> {

                            if (accessToken != null) {

                                threadPool.execute(() -> {
                                    SharedPreferences sharedPreferences = LoginFragment.this.getActivity()
                                            .getSharedPreferences(SHARED_PREFS, Context.MODE_PRIVATE);
                                    SharedPreferences.Editor editor = sharedPreferences.edit();
                                    editor.putString(key, accessToken.getAccessToken());
                                    editor.apply();
                                });
                                Toast.makeText(LoginFragment.this.getContext(), "Login effettuato",
                                        Toast.LENGTH_SHORT).show();

                            } else {
                                Toast.makeText(LoginFragment.this.getContext(), "Errore", Toast.LENGTH_SHORT)
                                        .show();
                            }

                        });
                email.getText().clear();
                password.getText().clear();

            } else {
                Toast.makeText(getContext(), "Compilare tutti i campi", Toast.LENGTH_SHORT).show();
            }
        });

        return root;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }
}

public class LoginViewModel extends ViewModel {

    private final Repository repository;

    public LoginViewModel() {
        repository = new Repository();
    }

    public MutableLiveData<AccessToken> login(String email, String password) {
        return repository.onLoginClick(email, password);
    }
}

public class Repository {

    public static MutableLiveData<AccessToken> accessToken;
    private ExecutorService threadPool;

    public Repository() {
        accessToken = new MutableLiveData();
        threadPool = Executors.newFixedThreadPool(8);
    }

    public MutableLiveData<AccessToken> onLoginClick(String email, String password) {

        threadPool.execute(() -> {
            accessToken.postValue(null);
            UtenteLogin utenteLogin = new UtenteLogin(email, password);
            Call<AccessToken> call = RetrofitClient.getInstance().getMyApi().login(utenteLogin);

            call.enqueue(new Callback<AccessToken>() {
                @Override
                public void onResponse(Call<AccessToken> call, Response<AccessToken> response) {
                    if (response.code() == 201) {
                        if (response.body() != null) {
                            accessToken.postValue(response.body());
                        }
                    } else {

                    }
                }

                @Override
                public void onFailure(Call<AccessToken> call, Throwable t) {
                    Log.println(Log.INFO, "Login", "Login fallito");
                }
            });
        });
        return accessToken;
    }
}

Don't call observe() inside of setOnClickListener { ... }

Put your call to observe and logic within outside of setOnClickListener, but keep it within onCreateView or in a separate function that's called just once within onCreateView.

LiveData preserves the last value and updates observers when they begin observing. This is a nice feature so that if your view gets destroyed and recreated the data is still there and the view gets reloaded with that data, so it's not meant to be observed on every action. Only need to start observing once in onCreateView.

The observer doesn't die after one update, it lives until the lifecycle you give it dies, in this case the Fragment's viewLifecycle, and can handle repeat updates. So again, you shouldn't set up observers in setOnClickListener because that will create extra observers every time a click happens which will cause bugs like this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM