Home > OS >  Cannot get correct SharedPreferences value on onClick
Cannot get correct SharedPreferences value on onClick

Time:06-28

I'm currently making a small app and I'm getting stuck on changing fragments using an onClick listener. I've searched the site and could find similar situations, but none of the proposed solutions worked.

So, when a user logs in, it sets a few values in SharedPreferences such as username, email and account level using a method from a class used to set and get SharedPreferences values. Afterwards, it should automatically redirect the user to a different Fragment. What's not happening, is redirecting the user to the other fragment.

I'm using AsyncTask for accessing the database. This is my code for the Login AsyncTask:

public class LoginSync extends AsyncTask <String, Void, String> {

    AlertDialog dialog;
    Context context;
    String result;
    JSONObject jObject;
    String username, password;
    String jEmail, jLevel;

    public LoginSync(Context context){
        this.context = context;
    }

    @Override
    protected void onPreExecute() {
        dialog = new AlertDialog.Builder(context).create();
        dialog.setTitle("Login Status");
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
        if(result.equals("login")) {
            dialog.setMessage("Logged in successfully!");
        }else{
            dialog.setMessage("Failed to login! Please check username/password.");
        }
        dialog.show();
    }

    @Override
    protected String doInBackground(String... voids) {

        username = voids[0];
        password = voids[1];

        String connstr = "URL HERE";

        try{
            URL url = new URL(connstr);
            HttpURLConnection http = (HttpURLConnection) url.openConnection();
            http.setRequestMethod("POST");
            http.setDoInput(true);
            http.setDoOutput(true);

            OutputStream ops = http.getOutputStream();
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(ops, StandardCharsets.UTF_8));
            String data = URLEncoder.encode("username","UTF-8") "=" URLEncoder.encode(username,"UTF-8")
                     "&&" URLEncoder.encode("password","UTF-8") "=" URLEncoder.encode(password,"UTF-8");

            writer.write(data);
            writer.flush();
            writer.close();
            ops.close();

            InputStream ips = http.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(ips, StandardCharsets.ISO_8859_1));
            String line;

            while((line = reader.readLine()) != null){
                jObject = new JSONObject(line);
                result = "false";

                if (jObject != null){
                    jEmail = jObject.getString("email");
                    jLevel = jObject.getString("account_level");
                    result = "login";
                }
            }

            if(result.equals("login")) {
                AppPreferences.setUserInfo(context.getApplicationContext(), username,jEmail,jLevel);
                AppPreferences.setLoggedStatus(context.getApplicationContext(), true);
            }

            reader.close();
            ips.close();
            http.disconnect();
            return result;

        }catch (MalformedURLException e){
            result = e.getMessage();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            e.printStackTrace();
        }

        return result;
    }
}

Using the debugger, I see that the values are being set as intended in the SharedPreferences. However, in the onClick check on the Login Fragment, it's set to false until the onClick method ends.

This is my Login Fragment code:

public class LoginFragment extends Fragment {

    private FragmentLoginBinding binding;

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

        binding = FragmentLoginBinding.inflate(inflater, container, false);
        View root = binding.getRoot();

        binding.btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                final String user = binding.username.getText().toString().trim();
                final String pass = binding.password.getText().toString().trim();

                LoginSync login = new LoginSync(getActivity());
                login.execute(user,pass);

                if(AppPreferences.getLoggedStatusBool(getActivity()).equals(true)){
                    NavHostFragment.findNavController(getParentFragment()).navigate(R.id.action_nav_login_to_nav_home);
                }

            }
        });

        binding.lnkRegister.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                NavHostFragment.findNavController(getParentFragment()).navigate(R.id.action_nav_login_to_nav_register);
            }
        });

        return root;
    }

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

On the first click, the values are set correctly, but checking the onClick with the debugger tells me that it's still false, after running the AsyncTask, and it doesn't trigger the fragment change in the if clause. On the second click, it changes the fragment.

What am I doing wrong? How can I make it change the fragment on the same click as it sets the information?

Thank you.

CodePudding user response:

You are getting correct value from sharedPreference, only your timing to get that value is not correct. You are using async task, which works on a different thread. in your onCLick you have these lines:

LoginSync login = new LoginSync(getActivity());
login.execute(user,pass);

if(AppPreferences.getLoggedStatusBool(getActivity()).equals(true)){
     NavHostFragment.findNavController(getParentFragment()).navigate(R.id.action_nav_login_to_nav_home);
}

you must have assumed that your if statement will execute after your login async task is completed, but this will not happen, it will execute straight after starting the login process and will check the sharedPref before the value is even set. You are doing network call and IO operation which will take some time and shared pref should be checked after the async task has been completed. So yo should write your if statement in async class's onPostExecute method like this:

@Override
protected void onPostExecute(String s) {
    super.onPostExecute(s);
    if(result.equals("login")) {
        dialog.setMessage("Logged in successfully!");
        if(AppPreferences.getLoggedStatusBool(getActivity()).equals(true)){
NavHostFragment.findNavController(getParentFragment()).navigate(R.id.action_nav_login_to_nav_home);
    }
        }else{
            dialog.setMessage("Failed to login! Please check username/password.");
        }
        dialog.show();
    }
  • Related