Android:DAO中带有Room数据库和LiveData的干净架构
我正在尝试将干净的体系结构方法应用于我的项目() 我使用Room数据库进行本地存储,我希望它成为应用程序中的单一数据源—这意味着从网络呼叫收集的所有数据首先保存在数据库中,然后才传递给演示者。Room提供了从DAO返回LiveData的功能,这正适合我的需要 但是,我也希望使用存储库作为访问数据的单一方式。下面是域层中的存储库接口示例(最抽象的一个):Android:DAO中带有Room数据库和LiveData的干净架构,android,kotlin,android-room,clean-architecture,android-livedata,Android,Kotlin,Android Room,Clean Architecture,Android Livedata,我正在尝试将干净的体系结构方法应用于我的项目() 我使用Room数据库进行本地存储,我希望它成为应用程序中的单一数据源—这意味着从网络呼叫收集的所有数据首先保存在数据库中,然后才传递给演示者。Room提供了从DAO返回LiveData的功能,这正适合我的需要 但是,我也希望使用存储库作为访问数据的单一方式。下面是域层中的存储库接口示例(最抽象的一个): 接口存储库{ fun findByUsername(用户名:字符串):列表 趣味添加(实体:T):长 乐趣移除(实体:T) 趣味更新(实体:T)
接口存储库{
fun findByUsername(用户名:字符串):列表
趣味添加(实体:T):长
乐趣移除(实体:T)
趣味更新(实体:T):Int
}
这里我遇到了一个问题——我需要在我的ViewModel中从Room的DAO中获取一个LiveData,我希望使用存储库实现来获取它。但为了实现这一点,我需要:
android.arch.lifecycle.LiveData
导入到我的存储库界面中,它将打破域层的抽象,因为它现在依赖于android体系结构库val entities:LiveData=database.entityDao.findByUsername(username)
,那么我打破了所有数据访问都必须使用Reposiotry进行的规则,我需要创建一些样板代码,以便与远程存储等同步如何使用LiveData、Room的DAO和Clean architecure模式实现单一数据源方法?当问及使用RxJava的类似问题时,开发人员通常会回答,没关系,RxJava现在是语言的一部分,因此,您可以在域层中使用它。在我看来,你可以做任何事情,如果它对你有帮助的话,那么,如果使用LiveData不会产生问题,那么就使用它,或者你可以使用RxJava,或者Kotlin协同程序来代替它。从技术上讲,你会遇到麻烦,因为你不想要同步数据获取
fun findByUsername(username: String) : List<T>
然后像
public class LiveDataSubscription<T> implements Subscription<T> {
private LiveData<T> liveData;
private LifecycleOwner lifecycleOwner;
private List<Observer<T>> foreverObservers = new ArrayList<>();
public LiveDataSubscription(LiveData<T> liveData) {
this.liveData = liveData;
}
@Override
public void observe(final Observer<T> observer) {
if(lifecycleOwner != null) {
liveData.observe(lifecycleOwner, new android.arch.lifecycle.Observer<T>() {
@Override
public void onChange(@Nullable T t) {
observer.onChange(t);
}
});
} else {
Observer<T> foreverObserver = new android.arch.lifecycle.Observer<T>() {
@Override
public void onChange(@Nullable T t) {
observer.onChange(t);
}
};
foreverObservers.add(foreverObserver);
liveData.observeForever(foreverObserver);
}
}
@Override
public void clear() {
if(lifecycleOwner != null) {
liveData.removeObservers(lifecycleOwner);
} else {
for(Observer<T> observer: foreverObservers) {
liveData.removeObserver(observer);
}
}
}
public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
this.lifecycleOwner = lifecycleOwner;
}
}
在域中使用流作为返回类型 因为流是Kotlin语言的一部分,所以在您的域中使用这种类型是完全可以接受的。 这里有一个例子 Repository.kt
package com.example.www.myawsomapp.domain
import com.example.www.myawsomapp.domain.model.Currency
import com.example.www.myawsomapp.domain.model.Result
import kotlinx.coroutines.flow.Flow
interface Repository {
fun getCurrencies(): Flow<List<Currency>>
suspend fun updateCurrencies(): Result<Unit>
}
从dao接收数据/模型包的数据类,而理想情况下,viewmodel应该接收域/模型包的数据类,以便将其映射到域模型
这是道课
package com.example.www.myawsomapp.data.database.dao
import com.blogspot.soyamr.cft.data.database.model.Currency
import kotlinx.coroutines.flow.Flow
import com.blogspot.soyamr.cft.data.database.model.Currency
@Dao
interface CurrencyDao {
@Query("SELECT * FROM currency")
fun getAll(): Flow<List<Currency>>
}
我正在考虑这种方法,其他开发人员也坚持将LiveData移动到域层,或者切换到反应式,而不是实施固定的解决方法。我想我会朝着建议的方向走,谢谢你的回答!干净的建筑很有趣DHi EpicPandaForce,你能回答我的问题吗?大多数答案都很复杂和过分。看看谷歌的例子。该函数正在返回
LiveData
,但由于kotlin的语言特性,您不必导入它。好好研究一下回购协议。它可能会解决你的大部分问题
public class LiveDataSubscription<T> implements Subscription<T> {
private LiveData<T> liveData;
private LifecycleOwner lifecycleOwner;
private List<Observer<T>> foreverObservers = new ArrayList<>();
public LiveDataSubscription(LiveData<T> liveData) {
this.liveData = liveData;
}
@Override
public void observe(final Observer<T> observer) {
if(lifecycleOwner != null) {
liveData.observe(lifecycleOwner, new android.arch.lifecycle.Observer<T>() {
@Override
public void onChange(@Nullable T t) {
observer.onChange(t);
}
});
} else {
Observer<T> foreverObserver = new android.arch.lifecycle.Observer<T>() {
@Override
public void onChange(@Nullable T t) {
observer.onChange(t);
}
};
foreverObservers.add(foreverObserver);
liveData.observeForever(foreverObserver);
}
}
@Override
public void clear() {
if(lifecycleOwner != null) {
liveData.removeObservers(lifecycleOwner);
} else {
for(Observer<T> observer: foreverObservers) {
liveData.removeObserver(observer);
}
}
}
public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
this.lifecycleOwner = lifecycleOwner;
}
}
val subscription = repository.findByUsernameWithChanges("blah")
if(subscription is LiveDataSubscription) {
subscription.lifecycleOwner = this
}
subscription.observe { data ->
// ...
}
package com.example.www.myawsomapp.domain
import com.example.www.myawsomapp.domain.model.Currency
import com.example.www.myawsomapp.domain.model.Result
import kotlinx.coroutines.flow.Flow
interface Repository {
fun getCurrencies(): Flow<List<Currency>>
suspend fun updateCurrencies(): Result<Unit>
}
package com.example.www.myawsomapp.data
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
class RepositoryImpl @Inject constructor(
private val currencyDao: CurrencyDao,
private val api: CurrencyApi,
private val connectivity: Connectivity
) :
Repository {
override fun getCurrencies(): Flow<List<Currency>> =
currencyDao.getAll().map { list -> list.map { it.toDomain() } }
override suspend fun updateCurrencies(): Result<Unit> =
withContext(Dispatchers.IO) {
val rowsInDataBase = currencyDao.getCount()
if (rowsInDataBase <= 0) {
if (connectivity.hasNetworkAccess()) {
return@withContext updateDataBaseFromApi()
} else {
return@withContext Failure(HttpError(Throwable(NO_INTERNET_CONNECTION)))
}
} else {
return@withContext Success(Unit)
}
}
}
currencyDao.getAll().map { list -> list.map { it.toDomain() } }
package com.example.www.myawsomapp.data.database.dao
import com.blogspot.soyamr.cft.data.database.model.Currency
import kotlinx.coroutines.flow.Flow
import com.blogspot.soyamr.cft.data.database.model.Currency
@Dao
interface CurrencyDao {
@Query("SELECT * FROM currency")
fun getAll(): Flow<List<Currency>>
}
val currencies =
getCurrenciesUseCase()
.onStart { _isLoading.value = true }
.onCompletion { _isLoading.value = false }.asLiveData()