无法从 Fragment 中的 ViewModel 观察 LiveData

Fah*_*hik 2 android android-livedata android-viewmodel mutablelivedata

RecyclerView我的里面有ViewPager一个Fragment。此RecyclerView显示从服务器获取的数据。它通常可以工作,但我无法使用ViewModel和实现相同的功能LiveData,因为当从 更改时,该livedata.observe方法不会被调用。livedataViewModel

这是我的MonthlyOrderViewModel.java类的源代码

public class MonthlyOrderViewModel extends AndroidViewModel {

private Retrofit retrofit;
private RetrofitServices retrofitServices;
private MutableLiveData<MonthlyOrderHistory> monthlyOrderHistoryMutableLiveData = new MutableLiveData<>();

public MonthlyOrderViewModel(@NonNull Application application) {
    super(application);
    retrofit = RetrofitFactory.getRetrofit();
    retrofitServices = retrofit.create(RetrofitServices.class);
}

public MutableLiveData<MonthlyOrderHistory> getMonthlyOrderHistoryMutableLiveData() {
    return monthlyOrderHistoryMutableLiveData;
}
public void fetchMonthlyOrders() {
    Call<MonthlyOrderHistory> call = retrofitServices.getAllMonthlyOrderHistory(IpharmaApplication.getInstance().getLoggedInUser().getId());
    call.enqueue(new Callback<MonthlyOrderHistory>() {
        @Override
        public void onResponse(Call<MonthlyOrderHistory> call, Response<MonthlyOrderHistory> response) {

            MonthlyOrderHistory monthlyOrderHistory = response.body();
            if(monthlyOrderHistory.getStatus().equalsIgnoreCase("success")) {

                List<UserOrder> userOrders = monthlyOrderHistory.getOrders().getUserOrder();
                if(userOrders != null && userOrders.size() != 0) {
                    DefaultScheduler.INSTANCE.execute(() -> {
                        monthlyOrderHistoryMutableLiveData.postValue(monthlyOrderHistory);
                        return Unit.INSTANCE;
                    });
                }
            }
        }

        @Override
        public void onFailure(Call<MonthlyOrderHistory> call, Throwable t) {

            t.printStackTrace();
        }
    });
}

}
Run Code Online (Sandbox Code Playgroud)

DefaultScheduler是一个Scheduler默认处理AsyncScheduler. 这是我的Scheduler.kt类的源代码


interface Scheduler {

    fun execute(task: () -> Unit)

    fun postToMainThread(task: () -> Unit)

    fun postDelayedToMainThread(delay: Long, task: () -> Unit)
}

//A shim [Scheduler] that by default handles operations in the [AsyncScheduler].
object DefaultScheduler : Scheduler {

    private var delegate: Scheduler = AsyncScheduler

    //Sets the new delegate scheduler, null to revert to the default async one.
    fun setDelegate(newDelegate: Scheduler?) {
        delegate = newDelegate ?: AsyncScheduler
    }

    override fun execute(task: () -> Unit) {
        delegate.execute(task)
    }

    override fun postToMainThread(task: () -> Unit) {
        delegate.postToMainThread(task)
    }

    override fun postDelayedToMainThread(delay: Long, task: () -> Unit) {
        delegate.postDelayedToMainThread(delay, task)
    }
}

//Runs tasks in a [ExecutorService] with a fixed thread of pools
internal object AsyncScheduler : Scheduler {

    private val executorService: ExecutorService = Executors.newFixedThreadPool(NUMBER_OF_THREADS)

    override fun execute(task: () -> Unit) {
        executorService.execute(task)
    }

    override fun postToMainThread(task: () -> Unit) {
        if (isMainThread()) {
            task()
        } else {
            val mainThreadHandler = Handler(Looper.getMainLooper())
            mainThreadHandler.post(task)
        }
    }

    private fun isMainThread(): Boolean {
        return Looper.getMainLooper().thread === Thread.currentThread()
    }

    override fun postDelayedToMainThread(delay: Long, task: () -> Unit) {
        val mainThreadHandler = Handler(Looper.getMainLooper())
        mainThreadHandler.postDelayed(task, delay)
    }
}

//Runs tasks synchronously.
object SyncScheduler : Scheduler {
    private val postDelayedTasks = mutableListOf<() -> Unit>()

    override fun execute(task: () -> Unit) {
        task()
    }
    override fun postToMainThread(task: () -> Unit) {
        task()
    }
    override fun postDelayedToMainThread(delay: Long, task: () -> Unit) {
        postDelayedTasks.add(task)
    }
    fun runAllScheduledPostDelayedTasks() {
        val tasks = postDelayedTasks.toList()
        clearScheduledPostdelayedTasks()
        for (task in tasks) {
            task()
        }
    }
    fun clearScheduledPostdelayedTasks() {
        postDelayedTasks.clear()
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,我尝试使用以下方法观察片段方法的变化onCreateView

MonthlyOrderViewModel monthlyOrderViewModel = new MonthlyOrderViewModel(getActivity().getApplication());

monthlyOrderViewModel.getMonthlyOrderHistoryMutableLiveData().observe(this, monthlyOrderHistory -> {

    monthlyOrderRecyclerView = view.findViewById(R.id.monthlyOrderRecyclerView);

    monthlyOrderRecyclerViewAdapter = new OrderHistoryRecyclerViewAdapter(getContext(), monthlyOrderHistory.getOrders().getUserOrder());

    monthlyOrderRecyclerViewAdapter.notifyDataSetChanged();

    monthlyOrderRecyclerView.setAdapter(monthlyOrderRecyclerViewAdapter);       

    monthlyOrderRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

});
Run Code Online (Sandbox Code Playgroud)

我尝试调试一下,发现改造后获取数据没有问题。但是observe当我尝试在片段中使用它们时,该方法永远不会被调用。

请建议我如何解决这个问题?任何帮助,将不胜感激。

dgl*_*ano 5

您不应该实例化您的ViewModelusing new. 你必须这样做:

MonthlyOrderViewModel monthlyOrderViewModel = ViewModelProviders.of(this).get(MonthlyOrderViewModel.class);
Run Code Online (Sandbox Code Playgroud)

如果您确定正在postValue您的 ViewModel 中执行,那么我认为此更改应该使其起作用。还有一件事:每次观察到的 LiveData 通知您发生更改时,您不应该执行所有这些操作。我会在之前移动下一行observe()

monthlyOrderRecyclerView = view.findViewById(R.id.monthlyOrderRecyclerView);

monthlyOrderRecyclerViewAdapter = new OrderHistoryRecyclerViewAdapter(getContext(), monthlyOrderHistory.getOrders().getUserOrder());

monthlyOrderRecyclerView.setAdapter(monthlyOrderRecyclerViewAdapter);       

monthlyOrderRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
Run Code Online (Sandbox Code Playgroud)

更新

正如 Fahim 在评论中证实的那样,问题在于ViewModel正在创建相同的多个实例。如果您需要共享 a 的相同实例ViewModel,请确保传递作为该 ViewModel 的“所有者”的 Fragment/Activity 的引用。例如,如果MyActivity有一个MainActivityViewModel并且它包含一个名为 的子 Fragment MyFragment,那么如果该 Fragment 想要获取对其父级 ViewModel 的引用,则必须按以下方式执行:

MonthlyOrderViewModel monthlyOrderViewModel = ViewModelProviders.of(this).get(MonthlyOrderViewModel.class);
Run Code Online (Sandbox Code Playgroud)

  • @FahimAlMahmudAshik 好的,我明白了...您是从片段还是活动中观察 ViewModel 吗?如果保存片段的 Activity 是调用 fetch 的 Activity,并且您在片段和 Activity 之间共享 ViewModel,则尝试使用 `MonthlyOrderViewModel MonthlyOrderViewModel = ViewModelProviders.of(getActivity()) 在片段中获取视图模型.get(MonthlyOrderViewModel.class);` 代替。如果没有,您最终将得到两个不同的 ViewModel 实例 (3认同)
  • 抱歉迟到了@dglozano。实际上这就是问题所在,我每次都在重新创建视图模型的新实例。请编辑您的答案并添加此信息,以便其他人遇到此问题时,他们可以从您发布的答案中获得明确的答案。我接受你的回答。非常感谢您抽出时间。 (3认同)
  • 谢谢家人,这让我免去了整整两天的工作,我的数据显示在日志中,但没有显示在我的观察者中,但是在阅读本文并从拥有视图模型的片段更改为使用活动实例后,我的数据全部这里。万分感谢 (2认同)