尽管实现了 LiveData,为什么存储库不通知 viewmodel 变量的更新值?

Why repository doesn't notify viewmodel about updated value of variable in spite of implementing LiveData?

我正在使用 MVVM 架构和 LiveData 从 Cloud Firestore 获取数据并显示在 recyclerview 中。当我观察 viewmodel 的变化并通知适配器有关数据集的变化时,变化的数据没有在 recyclerview 中更新。

你看到代码有什么问题了吗?

更新:我已经按照第一个答案的建议更新了代码。但仍然没有成功。我观察到即使存储库从 firestore 获取数据后,它也不会更新关于它的视图模型。我也添加了存储库 class。你看到那里有什么问题吗?

HomeActivity.java

public class HomeActivity extends AppCompatActivity {

    RecyclerView recyclerView;
    StandardPlansAdapter adapter;
    HomeActivityViewModel viewModel;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.recyclerViewHome);

        viewModel = new ViewModelProvider(this).get(HomeActivityViewModel.class);
        adapter = new StandardPlansAdapter(viewModel.getStandardPlans().getValue());
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);

        viewModel.getStandardPlans().observe(this, plans -> {
            adapter.setStandardPlans(plans);
            adapter.notifyDataSetChanged();
        });
    }
}

StandardPlansAdapter.java

public class StandardPlansAdapter extends RecyclerView.Adapter<StandardPlansAdapter.StandardPlansViewHolder> {

    private ArrayList<Plan> standardPlans;

    public StandardPlansAdapter(ArrayList<Plan> standardPlans) {
        this.standardPlans = standardPlans;
    }

    @NonNull
    @Override
    public StandardPlansViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.home_rv_layout, parent, false);
        return new StandardPlansViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull StandardPlansViewHolder holder, int position) {
        Plan plan = standardPlans.get(position);
        holder.textView.setText(plan.getName());
    }

    @Override
    public int getItemCount() {
        return standardPlans.size();
    }

    class StandardPlansViewHolder extends RecyclerView.ViewHolder {

        TextView textView;

        public StandardPlansViewHolder(@NonNull View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.tvPlanName);
        }
    }

    public void setStandardPlans(ArrayList<Plan> standardPlans) {
        this.standardPlans = standardPlans;
    }

}

HomeActivityViewModel.java

public class HomeActivityViewModel extends ViewModel {

    private StandardPlansRepository repository;
    private MutableLiveData<ArrayList<Plan>> standardPlans;

    public HomeActivityViewModel() {
        super();
        repository = StandardPlansRepository.getInstance();
        standardPlans = repository.getStandardPlans();
    }

    public LiveData<ArrayList<Plan>> getStandardPlans() {
        return standardPlans;
    }
}

StandardPlansRepository.java

public class StandardPlansRepository {

    private static StandardPlansRepository instance;
    private ArrayList<Plan> standardPlans = new ArrayList<>();

    public static StandardPlansRepository getInstance() {
        if (instance == null) {
            instance = new StandardPlansRepository();
        }
        return instance;
    }

    public MutableLiveData<ArrayList<Plan>> getStandardPlans() {
        setStandardPlans();
        MutableLiveData<ArrayList<Plan>> plans = new MutableLiveData<>();
        plans.setValue(standardPlans);
        return plans;
    }

    private void setStandardPlans() {
        documentReference.get().addOnSuccessListener(documentSnapshot -> {
            if (documentSnapshot.exists()) {
                StandardPlans plans = documentSnapshot.toObject(StandardPlans.class);
                if (plans != null) {
                    standardPlans = plans.getStandard_plans();
                }
            } else {
                Log.e("rahul", "Document doesn't exist");
            }
        }).addOnFailureListener(e -> {
            Log.e("rahul", e.getMessage());
            e.printStackTrace();
        });
    }
}

您的 Observer 正在调用 notifyDataSetChanged 而没有更新实际数据集,它是您 StandardPlansAdapter

中的私有成员 standardPlans

向您的适配器添加 public setStandardPlans 方法:

public class StandardPlansAdapter extends RecyclerView.Adapter<StandardPlansAdapter.StandardPlansViewHolder> {
    public setStandardPlans(ArrayList<Plan> standardPlans) {
        this.standardPlans = standardPlans;
    }
}

然后在 Observer 回调

通知之前调用 setter
viewModel.getStandardPlans().observe(this, (Observer<List<Plan>>) plans -> { 
    adapter.setStandardPlans(plans);   //You update the dataset first
    adapter.notifyDataSetChanged();    //And then notify the adapter of the update
})

编辑

此外,在您的 onCreate 方法中,我注意到您在初始化适配器之前附加了观察器。如果由于竞争条件,您的观察者回调在适配器初始化之前被触发,您将得到 NullPointerException 并且 Activity 将崩溃,因为您在回调中取消引用适配器。

adapter = new StandardPlansAdapter(viewModel.getStandardPlans().getValue()); //Initialize adapter first  
viewModel.getStandardPlans().observe(this, (Observer<List<Plan>>) plans -> adapter.notifyDataSetChanged()); //Potential Null pointer otherwise

EDIT2

您似乎对 LiveData 的工作原理理解不准确。

 public MutableLiveData<ArrayList<Plan>> getStandardPlans() {
     setStandardPlans();
     MutableLiveData<ArrayList<Plan>> plans = new MutableLiveData<>();
     plans.setValue(standardPlans);
     return plans;
 }

您不能在这里手动创建 LiveData 的新实例,设置它的值并期望观察者收到通知。

  1. 您不应该创建 LiveData 实例并为其设置值。
  2. 您没有在文档的 onSuccessListener 回调中更新实时数据值。 (plans.setValue) 如果你没有在那里设置 LiveData 的值?而且,如果这个逻辑起作用,你必须让你的计划变量成为你存储库中的 class 变量,而不是你的 getStandardPlans 方法中的局部变量。

无论如何你都不应该以这种方式使用实时数据

例如,如果您正在使用 Room 数据库,you'd be getting LiveData on your tables via the Room Library observable queries,而不是手动初始化 LiveData 实例并在其上设置值。

Firestore API 不会 为您提供 LiveData 可观察查询,例如 Room 库。 (它确实为您提供了可观察的查询)。因此,如果您想通过 LiveData 和 ViewModels 观察这些查询,则必须 extend LiveData and attach to Firestore callbacks inside it.

getSavedAddresses extends LiveData