为了账号安全,请及时绑定邮箱和手机立即绑定

获取 LiveData 的初始值总是返回 Null

获取 LiveData 的初始值总是返回 Null

互换的青春 2022-07-06 09:47:09
当应用程序启动时,我正在尝试从本地房间数据库加载loggedInUser 。如果之前保存的用户保存的身份验证令牌仍然有效,我想跳过提示用户登录!因此,从DAO中,我想返回一个包含先前登录的用户的LiveData对象,然后观察它以进行后续更改。我面临的挑战是,如果我将结果包装在LiveData中,获取当前登录用户的方法总是返回 null,但如果返回为POJO ,它将返回预期的用户。如何强制LiveData同步运行以初始化值,然后收听后续更改?我真的想结合这两种行为,因为身份验证可能会因后台同步任务或用户注销而失效(这些操作将替换或更新保存的令牌,我希望在实时数据)。这是我到目前为止所尝试的:AuthorizationDAO.javapublic 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);}AuthorizationRepository.javapublic class AuthorizationRepository {    private AuthorizationDAO mAuthorizationDAO;    private MutableLiveData<Authorization> mAuthorization = new MutableLiveData<>();    public AuthorizationRepository(Application application){        AppDatabase db = AppDatabase.getDatabase(application);        this.mAuthorizationDAO = db.mAuthorizationDAO();    }    public LiveData<Authorization> getLoggedInUser(){               mAuthorization.postValue(mAuthorizationDAO.getLoggedInUser().getValue()); //this is always null at startup        return this.mAuthorization;    }AuthorizationViewModel.javapublic class AuthorizationViewModel extends AndroidViewModel {    private AuthorizationRepository mAuthorizationRepository;    private LiveData<Resource<Authorization>> mAuthorization;    private LiveData<Authorization> loggedInUserAuth;    public AuthorizationViewModel(@NonNull Application application) {        super(application);        this.mAuthorizationRepository = new AuthorizationRepository(application);        });如您所见,我正在调用mAuthorizationViewModel.init()以便我可以从本地数据库加载或初始化loggedInUserAuth,然后在下一行使用mAuthorizationViewModel.getLoggedInUserAuth().observe()观察相同的LiveData实例!但是为loggedInUserAuth返回的值始终为 null!请帮忙,谢谢!
查看完整描述

3 回答

?
手掌心

TA贡献1942条经验 获得超3个赞

我终于在@Krishna 的大力帮助下解决了这个问题,以下是要点:

  1. DAO 方法应该返回LiveData

  2. Repository 类中,创建一个LiveData 私有成员变量而不是MutableLiveData(这是因为我们将通过更新/插入来改变数据库记录)。成员变量将保存对DAO 方法返回的LiveData对象的引用

  3. Repository 的构造函数中,将LiveData对象初始化为DAO 方法返回的结果。这样,每次activity启动时,都会加载当前保存的记录

  4. Repository 类中,创建一个getter,它将LiveData对象公开给ViewModel

  5. ViewModel 类中,创建一个将LiveData对象公开给View Controller(活动或片段)的方法

  6. Activity 或 Fragment中,只需侦听或订阅ViewModel提供的Accessor Method公开的LiveData上的更改

  7. DAO还可以公开更新LiveData的方法,允许Repository通过ViewModel启用Activity 或 FragmentLiveData发送更新,同时保持所有侦听器的响应

这是此场景的工作代码:

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();

    }

}


查看完整回答
反对 回复 2022-07-06
?
慕标琳琳

TA贡献1830条经验 获得超9个赞

mAuthorization在类中创建一个getter方法AuthorizationRepository


public MutableLiveData<Authorization> getAuthorizationResult() {

   return mAuthorization;

}

然后像下面这样修改你的AuthorizationViewModel类


public void init() {

    mAuthorizationRepository.getLoggedInUser();

}


public LiveData<Authorization> getLoggedInUserAuth() {

    return mAuthorizationRepository.getAuthorizationResult();

}


查看完整回答
反对 回复 2022-07-06
?
智慧大石

TA贡献1946条经验 获得超3个赞

为时已晚,但可能会帮助某人。


我这样做时遇到了同样的问题


MyDao myDao;

private LiveData<List<T>> liveList;

//in constructor of repo after initializing myDao;

    this.liveList = myDao.getAllData();

//somewhere in repo

    for(T t : liveList.getValue()){/*computation*/}

这就是我解决它的方法


MyDao myDao;

//in constructor of repo don't do this because called on main thread

    this.list = myDao.getAll();

//in constructor of repo initialize your Dao (in this case myDao)

//somewhere in repo  (must not be on main thread)

    for(T t : myDao.getAll()){/*computation*/} //do this on background thread

在我的道


@Query("SELECT * FROM myTable")

List<T> getAll();

@Query("SELECT * FROM myTable")

LiveData<List<T>> getAllData();

或者,如果您在其他地方(而不是存储库)访问 liveList,那么您必须为相同的地方设置一个观察者


查看完整回答
反对 回复 2022-07-06
  • 3 回答
  • 0 关注
  • 289 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信