Getting Initial Value for LiveData Always Returning Null

浪尽此生 提交于 2019-12-06 07:33:40

Create a getter method of mAuthorization in class AuthorizationRepository

public MutableLiveData<Authorization> getAuthorizationResult() {
   return mAuthorization;
}

Then modify your AuthorizationViewModel class like below

public void init() {
    mAuthorizationRepository.getLoggedInUser();
}

public LiveData<Authorization> getLoggedInUserAuth() {
    return mAuthorizationRepository.getAuthorizationResult();
}

I finally solved this problem with great help from @Krishna, and here are the main points:

  1. The DAO method should return LiveData
  2. In the Repository class, create a LiveData private member variable and not MutableLiveData(this is because we will be mutating database record via updates/inserts). The member variable will hold a reference to a LiveData object returned by the DAO Method
  3. In the Repository's constructor, initialize the LiveData object to the result returned by the DAO method. This way, every time the activity starts, the currently saved record will be loaded
  4. In the Repository class, create a getter which will expose the LiveData object to the ViewModel
  5. In the ViewModel class, create a method which will expose the LiveData object to the View Controller (activity or fragment)
  6. In the Activity or Fragment, simply listen or subscribe to changes on the LiveData exposed by the Accessor Method provided by the ViewModel
  7. The DAO can also expose a method to update the LiveData, allowing the Repository via the ViewModel to enable the Activity or Fragment to send updates to the LiveData, at the same time keeping all listeners reactive!

Here is the working code for this scenario:

AuthorizationDAO.java

public interface AuthorizationDAO {

    @Query("SELECT * FROM Authorization LIMIT 1") //Assume only one Authentication token will exist at any given time
    LiveData<Authorization> getLoggedInUser(); //I want to keep this behaviour

    @Insert(onConflict = REPLACE)
    long insertAuth(Authorization authorization);

    @Update
    void logoutCurrentUser(Authorization authorization); //this will be used to toggle login status by Activity or Fragment
}

AuthorizationRepository.java

public class AuthorizationRepository {
    private AuthorizationDAO mAuthorizationDAO;
    private AuthorizationWebAPI mAuthorizationWebAPI;
    private LiveData<Authorization> mAuthorization; //reference to returned LiveData

    public AuthorizationRepository(Application application){
        AppDatabase db = AppDatabase.getDatabase(application);
        this.mAuthorizationDAO = db.mAuthorizationDAO();
        this.mAuthorization = mAuthorizationDAO.getLoggedInUser(); //initialize LiveData
    }
    public LiveData<Authorization> getAuthorizationResult() { //getter exposing LiveData
        return mAuthorization;
    }
    public void logoutCurrentUser(){ //toggle login status
        if (this.mAuthorization != null){
            AppExecutors.getInstance().getDiskIO().execute(()->{
                Authorization mAuthorizationObj = this.mAuthorization.getValue();
                mAuthorizationObj.setLoggedIn(false);
                mAuthorizationDAO.logoutCurrentUser(mAuthorizationObj); //update LiveData and changes will be broadcast to all listeners
            });
        }
    }
}

AuthorizationViewModel.java

public class AuthorizationViewModel extends AndroidViewModel {

    private AuthorizationRepository mAuthorizationRepository;

    public AuthorizationViewModel(@NonNull Application application) {
        super(application);
        this.mAuthorizationRepository = new AuthorizationRepository(application);
    }
    public LiveData<Authorization> getLoggedInUserAuth() { //exposes LiveData to the Activity or Fragment
        return mAuthorizationRepository.getAuthorizationResult();
    }
    public void logoutCurrentUser(){ //allows activity or fragment to toggle login status
        this.mAuthorizationRepository.logoutCurrentUser();
    }
}

AppActivity.java

public class AppActivity extends AppCompatActivity {
    public AuthorizationViewModel mAuthorizationViewModel;
    public  @Nullable Authorization mAuthorization;
    private NavController mNavController;
    private NavHostFragment mNavHostFragment;
    private BottomNavigationView mBottomNavigationView;
    private boolean mIsLoggedIn;
    private ActivityAppBinding mBinding;
    private boolean mIsTokenExpired;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_app);

        mNavHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.app_nav_host_fragment);
        mNavController = mNavHostFragment.getNavController();

        mBottomNavigationView = findViewById(R.id.nav_bottom_nav_view);
        NavigationUI.setupWithNavController(mBottomNavigationView, mNavController);

        mAuthorizationViewModel = ViewModelProviders.of(this).get(AuthorizationViewModel.class);

        mAuthorizationViewModel.getLoggedInUserAuth().observe(this, new Observer<Authorization>() { //Observe changes to Authorization LiveData exposed by getLoggedInUserAuth()
            @Override
            public void onChanged(@Nullable Authorization authorization) {
                mBinding.setViewModel(authorization);
                mIsLoggedIn = authorization == null? false: authorization.isLoggedIn();
                mIsTokenExpired = authorization == null ? true : authorization.isTokenExpired();
                if(!mIsLoggedIn || mIsTokenExpired){
                    if (authorization != null){
                        Log.i("CurrentAuth", "tokenExpiresAt?: "+ authorization.getExp());
                    }
                    mNavController.navigate(R.id.start_login); //every time authorization is changed, we check if valid else we react by prompting user to login
                }
            }
        });
    }
}

LogoutFragment.java

public class LogoutFragment extends Fragment {

    private AuthorizationViewModel mAuthorizationViewModel;
    private Authorization mAuth;
    private FragmentLogoutBinding mBinding;

    public LogoutFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        mAuthorizationViewModel = ViewModelProviders.of(getActivity()).get(AuthorizationViewModel.class);
        mAuthorizationViewModel.getLoggedInUserAuth().observe(getActivity(), new Observer<Authorization>() {
            @Override
            public void onChanged(Authorization authorization) {
                mAuth = authorization;
            }
        });
        // Inflate the layout for this fragment
        mBinding = DataBindingUtil.inflate(inflater,R.layout.fragment_logout,container,false);
        View view = mBinding.getRoot();
        mBinding.setViewModel(mAuth);
        return view;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        new AlertDialog.Builder(getContext())
                .setTitle(R.string.title_logout_fragment)
                .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        mAuthorizationViewModel.logoutCurrentUser(); //toggle login status, this will mutate LiveData by updating the database record then UI will react and call login fragment
                    }
                })
                .setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialogInterface.cancel();
                        Navigation.findNavController(view).popBackStack();
                    }
                })
                .setOnDismissListener(new DialogInterface.OnDismissListener() {
                    @Override
                    public void onDismiss(DialogInterface dialogInterface) {
                    }
                })
                .show();
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!