【问题标题】:How to invalidate ViewModel/LiveData after some time?一段时间后如何使 ViewModel/LiveData 失效?
【发布时间】:2021-07-21 15:10:20
【问题描述】:

根据最新文档的建议,我正在使用 ViewModel/LiveData 来显示一些数据:

public class EventDatesViewModel extends AndroidViewModel {

    private final MutableLiveData<String> timeToEvent = new MutableLiveData<>();

    ...

    public LiveData<String> getTimeToEvent() {
        if (timeToEvent == null)
            new Thread(this::calculateTimeToEvent).start();
        return timeToEvent;
    }

    private void calculateTimeToEvent() {

        ...

        timeToEvent.postValue(t);
    }
}

我在我的片段中使用它:

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

        View root = inflater.inflate(R.layout.fragment_garbage, container, false);

        EventDatesViewModel model = new ViewModelProvider(requireActivity(), new ViewModelProvider.AndroidViewModelFactory(requireActivity().getApplication())).get(EventDatesViewModel.class);
        model.getTimeToEvent().observe(this, timeToEvent -> {
            ((TextView)root.findViewById(R.id.textTimeToEvent)).setText(timeToEvent);
        });
        return root;
    }

现在的问题是timeToEvent 类似于“2 天 4 小时”。因此,如果我让应用程序保持打开状态,在某些时候它会过时并且需要重新计算。如果应用程序未打开或在后台,我不想运行相当昂贵的计算。

如何实现这种定期重新计算?


什么没用

我添加了一个使用postDelayed() 来安排重新计算的处理程序。

    private final Handler handler = new Handler();
    private final Runnable runnable = new Runnable() {
        @Override
        public void run() {
            new Thread(() -> calculateTimeToEvent()).start();
            handler.postDelayed(this, 10000);
        }
    };
 public LiveData<String> getTimeToEvent() {
        if (timeToEvent == null) {
            new Thread(this::calculateTimeToEvent).start();
            handler.postDelayed(runnable, 10000);
        }
        return timeToEvent;
    }
    @Override
    protected void onCleared() {
        super.onCleared();
        handler.removeCallbacks(runnable);
    }

不幸的是,即使应用程序在后台或设备处于睡眠状态,它也会运行计算。我将带有时间戳的 calculateTimeToEvent() 的运行记录到一个文件中,我可以看到它们每 10 秒发生一次,没有中断。

【问题讨论】:

  • 您是否在问如何安排每个2 days 4 hours 的重复操作?
  • @tyczj 我在问如何安排每隔一小时左右重复一次操作,使其有权访问 ViewModel 以发布新值并且仅在以下情况下运行当前实际使用的是 ViewModel。
  • 1.) 为什么要明确使用`new ViewModelProvider.AndroidViewModelFactory(requireActivity().getApplication()))`? Fragment 已经有一个具有更好行为的默认视图模型提供程序工厂 2.) 为什么使用 this 作为 LifecycleOwner 而不是 getViewLifecycleOwner()
  • @EpicPandaForce 1) 在我的 ViewModel 中,我需要一个 Context 来显示 toast,所以它是一个 AndroidViewModel,这是我能找到的最短的可以实例化 AndroidViewModel 的代码。 2) 我从here 复制了该代码,但似乎我混淆了这两个示例。
  • @EpicPandaForce 确实,在将 appcompat 更新到 1.3.1(从 1.2.0)之后,默认工厂工作并且它以某种方式触发了我应该使用 getViewLifecycleOwner() 的 lint this,就像你说的那样。谢谢。

标签: android mvvm android-livedata android-viewmodel


【解决方案1】:

由于您使用的是 Java,因此您可以使用 postDelayed 的处理程序在 X 时间后触发可运行对象,然后在触发后再次安排它

这是 kotlin,但可以轻松转回 Java

在您的视图模型中

private val _handler = Handler()

private val _runnable = object : Runnable {
    override fun run() {
        calculateTimeToEvent()
        _handler.postDelayed(this, 3600000)
    }

}

然后在某个时候在您的视图模型中调用它以开始重复操作

_handler.postDelayed(_runnable, 3600000)

然后在 onCleared 上的视图模型中,您可以删除回调以停止它

_handler.removeCallbacks(_runnable)

还有使用RxJava和使用interval的选项

【讨论】:

  • 这个postDelayed在视图不显示时自动挂起?比如当用户打开另一个应用程序或手机屏幕关闭时?
  • 由于 Activity 不再在 ViewModel 周围,因此不再需要 onCleared(您调用 removeCallbacks)被称为停止重复操作
  • @AndreKR 那么这意味着 ViewModel 仍在使用中,即。活动仍处于可以恢复的状态。您始终可以在 ViewModel 中创建一个方法来取消 onPause 中活动的回调,例如,活动不再处于活动状态(暂停)但未销毁
  • 那为什么不简单地让片段调用 ViewModel 上的刷新方法呢?但我觉得我应该使用 LifecycleOwner/Observer 的东西?
  • 你当然可以,但你说你想让它在 ViewModel 处于活动状态时工作。由于在诸如屏幕旋转之类的配置更改时,活动/片段将取消/重新启动计时器,因为它们被重新创建,不像 ViewModel 对象在配置更改时与它们的数据一起保留,因此理论上 1 小时计时器可以变成 2 小时计时器
猜你喜欢
  • 1970-01-01
  • 2017-04-15
  • 2019-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-29
  • 1970-01-01
  • 2023-04-05
相关资源
最近更新 更多