繁体   English   中英

MVVM 与 Retrofit - 如何处理存储库中的大量 LiveData?

[英]MVVM with Retrofit - How to deal with a lot of LiveData in Repository?

我正在按照本教程使用 MVVM 和 Retrofit

https://medium.com/@ronkan26/viewmodel-using-retrofit-mvvm-architecture-f759a0291b49

用户将 MutableLiveData 放置在存储库 class 中的位置:

public class MovieRepository {
    private static final ApiInterface myInterface;
    private final MutableLiveData<EntityMovieOutputs> listOfMovies = new MutableLiveData<>();

private static MovieRepository newsRepository;

    public static MovieRepository getInstance(){
        if (newsRepository == null){
            newsRepository = new NewsRepository();
        }
        return movieRepository;
    }

    public MovieRepository(){
        myInterface = RetrofitService.getInterface();
    }

我正在构建一个简单的应用程序,我注意到我的存储库 class 很快就被很多 MutableLiveData 对象填充了。 这实际上是实现 MVVM、LiveData 和存储库模式的正确方法吗?

在此处输入图像描述

编辑 1:________________________________________________

我创建了一个AdminLiveData object,它只保存 LiveData 并具有吸气剂。

在此处输入图像描述

但是,如何在我的AdminRepo LiveData中获取对ViewModel的引用,以便在 Retrofit 网络调用完成时通知 ViewModel 中的 LiveData?

private AdminService  adminService;
    
public AdminRepo(Application application) {
        BaseApplication baseApplication = (BaseApplication) application;
        RetrofitClient client = baseApplication.getRetrofitClient();
        adminService = client.getRetrofit().create(AdminService.class);  

        //AdminViewModel viewModel = (AdminViewModel) .... 
        // Not sure how to get reference to the viewmodel here so I can get the 
        // LiveData object and call postValue after the retrofit calls
}

    public void getFirstPageMembers(int offset, int limit) {
        adminService.getUsersPaginitation(offset, limit).enqueue(new Callback<List<UserInfo>>() {
            @Override
            public void onResponse(@NonNull Call<List<UserInfo>> call, @NonNull Response<List<UserInfo>> response) {
                if (response.body() != null) {
                    //firstPageLiveData.postValue(response.body());
                    //Since I create the LiveData inside the ViewModel class 
                   //instead, how do I get reference to the ViewModel's LiveData?
                }
            }

            @Override
            public void onFailure(@NonNull Call<List<UserInfo>> call, @NonNull Throwable t) {
                //firstPageLiveData.postValue(null);
            }
        });
    }

AdminViewModel

public class AdminActivityViewModel extends AndroidViewModel {

    private AdminRepo repo;

    private AdminLiveData adminLiveData = new AdminLiveData();
    
    public AdminActivityViewModel(@NonNull Application application) {
        super(application);

        repo = new AdminRepo(application);
    }

如何从我的AdminRepo class 中获取对AdminViewModel的引用?

存储库和 ViewModel

android 项目中的存储库对象应被视为通往外部世界的网关。 与持久性设施(网络、SQLite、共享首选项)的通信发生在这一层。 因为传入的数据不应该符合 android 环境。 例如,传入 DTO 中的字符串日期字段应使用本地日期时间转换为日期 object,您可能需要将来自服务器的数据保存到本地 DB。 此外,可以在这一层执行其他与数据相关的任务,例如 memory 中的缓存。

ViewModel 表示用户界面中显示的数据。 该层中的数据应该准备好在屏幕上呈现。 例如,一个视图可能需要来自两个不同的 HTTP 请求的数据,您应该在这一层合并传入的数据。 (不过,您可以进一步分离职责。如果这两个请求是单个任务或目的的一部分,您可以在用例层执行此操作。)从 android 的角度来看,视图模型具有更多的责任,例如防止数据被破坏在配置更改中。 在视图模型中,建议数据应通过 LiveData 呈现给视图层。 这是因为 LiveData 保留了数据的最后一个 state 并且它知道视图层的生命周期(如果使用得当)。

Repository 和 ViewModel 之间的通信

首先,存储库层必须不知道任何视图 model 的存在,因此您不应在存储库层保留视图 model 的引用。 有一些原因,

  • 大多数情况下,用于一组交互的单个存储库 object 足以满足整个应用程序的需求。 如果保留视图 model 的引用,则会导致 memory 泄漏。
  • 如果服务端API,repo层使用的本地存储等组件设计得很好,repository层相比视图model更不容易发生变化。
  • 存储库层的职责是从某个地方获取数据,因此这意味着它与视图相关的层无关。

当然,我们需要某种对视图 model 的引用,以便在请求完成时通知它,但我们应该以系统的方式隐式执行此操作,而不是直接引用。

回调:这是一种将数据发送回视图 model 的老式方式。 在代码库中广泛使用回调会导致不需要的回调地狱。 此外,结构化取消机制很难使用回调实现。

LiveData:乍一看,它似乎非常适合此目的,但事实并非如此。 原因可以列举如下

  • LiveData 被设计为一个生命周期感知的数据持有者。 这对您的目的来说是开销,因为您与该层中视图的生命周期没有任何关系。
  • 在大多数情况下,数据获取操作是一次性的(就像您的情况一样),但 LiveData 是为流设计的。
  • 它没有对结构化取消的内置支持。
  • 从架构的角度来看,android 相关类如实时数据不应该导入到存储库层。 存储库类应实现为简单的 Kotlin 或 java class。

在您的情况下,这是特别糟糕的做法,因为 HTTP 请求不应更改存储库 object 的 state。 您将 LiveData 用作一种缓存,但对此没有这样的要求,因此您应该避免这种情况。 尽管如此,如果您需要在您的存储库中使用 LiveData,您应该将 MutableLiveData 作为参数传递给您的请求方法,以便您可以通过此 LiveData 发布响应或在您的请求方法中返回 LiveData。

RxJava:它是选项之一。 它支持一次性请求(Single)、热流(Subject)和冷流(Observable)。 它以某种方式支持结构化取消(CompositeDisposable)。 它有一个稳定的 API 并且已经普遍使用了多年。 它还通过其运算符使许多不同的网络操作变得更容易,例如并行请求、顺序请求、数据操作、线程切换等。

协程:这是另一种选择,在我看来,它是最好的。 虽然它的 API 并不完全稳定,但我在很多不同的项目中使用过它,我没有发现任何问题。 它支持一次性请求(挂起函数)、热流(Channels 和 Stateflow)和冷流(Flow)。 它在需要复杂数据流的项目中非常有用。 它是 Kotlin 中所有运算符的内置功能。 它以一种非常优雅的方式支持结构化并发。 它有许多不同的运算符函数,与 RxJava 相比,它更容易实现新的运算符函数。 它还具有与 ViewModel 一起使用的有用扩展功能。

总而言之,存储库层是传入数据的网关,这是您操作数据以符合我上面提到的应用程序要求的第一个地方。 可以在这一层中完成的操作可以简单地列出为映射传入数据、决定从哪里获取数据(本地或远程源)和缓存。 有许多选项可以将数据传回 class 请求数据,但 RxJava 和 Coroutines 比其他更好,正如我在上面解释的那样。 在我看来,如果你对它们都不熟悉,那就把你的精力放在 Coroutines 上。

您正在寻找的解决方案取决于您的应用程序的设计方式。 您可以尝试以下几件事:

  • 保持您的应用程序模块化 - 正如@ADM 提到的将您的存储库拆分为更小的
  • 将实时数据移出存储库 - 在整个应用程序生命周期中,没有必要将实时数据保留在存储库中(在您的情况下为单例),而可能只有少数屏幕需要不同的数据。
  • 话虽如此 - 将您的实时数据保存在视图模型中 - 这是最标准的做法。 您可以查看这篇解释 Retrofit-ViewModel-LiveData 存储库模式的文章
  • 如果您最终得到复杂的屏幕和许多实时数据对象,您仍然可以将 map 实体转换为带有事件/状态/命令(随意调用)的屏幕数据表示,这些在此处进行了很好的描述。 这样你就有了一个LiveData<ScreenState>并且你只需要 map 你的实体。

另外,您可以将协程与 retrofit 一起使用,因为协程现在被推荐用于处理后台操作,并且如果您想尝试一下,则支持 Kotlin。

此外,在探索不同的架构或解决方案来处理您的问题架构组件示例架构示例时,这些链接可能会阻碍您(尽管主要使用 kotlin)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM