在 Activity 中返回使用 MVVM 模型观察数据库变化的 LiveData 时出现问题

Problems returning LiveData in Activity that observes changes in database using MVVM model

在我的应用程序中,我 return 来自存储库中 Room 数据库 (SQLite) 的 LiveData,并在我的应用程序中观察数据 Activity。

问题是: 在 Activity 中有 LiveData,它使用 MVVM 模型观察数据库中的变化,并且 运行 在数据改变时有一些代码(因为这就是 observe 的工作原理)。

该方法在存储库中如下所示:

public LiveData<TourWithAllGeoPoints> getTourWithAllGeoPoints(long tourId, boolean mIsFirstTime) {
    if (!mIsFirstTime) { 
        return tourWithAllGeoPoints;
    }
    MyTourAssistentDatabase.databaseWriteExecutor.execute(()-> {
        tourWithAllGeoPoints = toursDAO.getTourWithAllGeoPoints(tourId);  //this part finishes after reuturn
    });
    return tourWithAllGeoPoints;  //this part returns
}

mIsFirstTime 检查 Activity(或片段)是否是第一次加载(如果 Bundle 是否为空)。

databaseWriteExecutor.execute() 是一个线程池,在自己的线程中执行代码。

toursDAO.getTourWithAllGeoPoints(tourId) 是我从 Room 数据库询问和获取数据的地方。它 return 是一个 LiveData 对象。

在 Activity 代码中,我确实观察到了 LiveData:

        activeTourViewModel.getTourWithAllGeoPoints(tourId, mIsFirstTime).observe(this, geoPointsPlanned -> {
            //Some code here changing UI, other variables, etc.

}

但问题是execute()部分完成之前的方法returns 'tourWithAllGeoPoints'。所以这意味着它 return 是一个空的 LiveData。或者我们在 MainActivity 上观察到的 LiveData 与我们从 toursDAO 获得的 LiveData 不同。

所以在 Activity 它观察到空的 LiveData。

我尝试的解决方案是:

1) 我可以 运行 在主线程中这样查询:

public LiveData<TourWithAllGeoPoints> getTourWithAllGeoPoints(long tourId, boolean mIsFirstTime) {
    if (!mIsFirstTime) { 
        return tourWithAllGeoPoints;
    }
  
    tourWithAllGeoPoints = toursDAO.getTourWithAllGeoPoints(tourId);

    return tourWithAllGeoPoints;
}

但随后它会发出警告消息,提示不要 运行 在主线程上查询 Room 数据库,因为这可能需要很长时间。

2) 或者我可以使 toursDAO.getTourWithAllGeoPoints(tourId) return 成为 TourWithAllGeoPoints 对象而不是 LiveData,并将其放入 LiveData 对象中,像这样:

public LiveData<TourWithAllGeoPoints> getTourWithAllGeoPoints(long tourId, boolean mIsFirstTime) {
    if (!mIsFirstTime) { 
        return tourWithAllGeoPoints;
    }
    MyTourAssistentDatabase.databaseWriteExecutor.execute(()-> {
        TourWithAllGeoPoints twagp = toursDAO.getTourWithAllGeoPoints(tourId);
        tourWithAllGeoPoints.postValue(twagp)
    });
    return tourWithAllGeoPoints;
}

以便观察LiveData的变化。但是后来我无法观察到数据库中所做的更改,因为它只是 return 一个列表。这意味着我每次在数据库中进行更改时都必须 运行 相同的方法。

3) 或者我可以把一个 LiveData 放在一个 LiveData 里面,也像这样:

public LiveData<LiveData<TourWithAllGeoPoints>> getTourWithAllGeoPoints(long tourId, boolean mIsFirstTime) {
    if (!mIsFirstTime) { 
        return tourWithAllGeoPoints;
    }
    MyTourAssistentDatabase.databaseWriteExecutor.execute(()-> {
        LiveData<TourWithAllGeoPoints> twagp = toursDAO.getTourWithAllGeoPoints(tourId); //returns LiveData
        tourWithAllGeoPoints.postValue(twagp)
    });
    return tourWithAllGeoPoints;
}

但我不知道将 LiveData 放在 LiveData 中是否是个好主意。

或者是其他的解决方法。但是我该如何解决这个问题呢?

问题是: 在 Activity 中有 LiveData,它使用 MVVM 模型观察数据库中的变化,并且 运行 在数据改变时有一些代码(因为这就是 observe 的工作原理)。

对于您描述的具体问题(即 return 第一个 TourWithAllGeoPoints 而不是其他),似乎 LiveData 不是您可以在此处使用的最合适的数据类型. LiveData 顾名思义,当底层数据是实时的并且随时可能更改时使用,您需要在每次更改时观察数据。如果您只需要一个值,最好根本不要使用 LiveData。只需使您的 DAO 方法 getTourWithAllGeoPoints return TourWithAllGeoPoints (没有 LiveData)并从后台线程调用它。查看 this link 了解一些方法。在这种情况下使用 Kotlin 协程要容易得多,但您需要为此使用 Kotlin(我推荐 :))。

但是如果您描述的问题是一般性的(不完全是 returning 一个值一次),您可以使用 MediatorLiveData 观察 LiveData 和 post 每次发出新值时都会有不同(或不同)的东西。看看这段代码:

private MediatorLiveData<TourWithAllGeoPoints> mediator;

public YourRepositoryConstructor() {
    mediator = new MediatorLiveData<>();
    mediator.addSource(toursDAO.getTourWithAllGeoPoints(tourId), data -> {
        if (mediator.getValue() != null) {
            mediator.setValue(data);
        }
    });
    return mediator;
}

public LiveData<TourWithAllGeoPoints> getTourWithAllGeoPoints(long tourId, boolean mIsFirstTime) {
    return mediator;
}

一个MediatorLiveData观察一个(或多个)其他LiveData个对象,并根据其他LiveData个对象的变化发出一个新值。它可能会发出不同的数据类型(即它不必是底层 LiveData 对象的同一类型)甚至根本不发出任何东西。这完全是根据您的 MediatorLiveData 代码。在这种情况下,每次 getTourWithAllGeoPoints 的结果发出新的东西时,你 MediatorLiveData 都会对此做出反应,并且只有在第一次发出时才发出一个新值。它可以通过检查它的值是否为 null 来做到这一点,它不需要变量 mIsFirstTime(除非 null 在这种情况下对你来说是一个有效值)。

MediatorLiveData 是一种更通用的方法,适用于您描述的场景类型,但如果您只需要该查询的一个结果,则可能需要付出太多努力,您可能会通过不使用 LiveData 完全没有。