在 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
完全没有。
在我的应用程序中,我 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
完全没有。