Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/221.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android:DAO中带有Room数据库和LiveData的干净架构_Android_Kotlin_Android Room_Clean Architecture_Android Livedata - Fatal编程技术网

Android:DAO中带有Room数据库和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)

我正在尝试将干净的体系结构方法应用于我的项目()

我使用Room数据库进行本地存储,我希望它成为应用程序中的单一数据源—这意味着从网络呼叫收集的所有数据首先保存在数据库中,然后才传递给演示者。Room提供了从DAO返回LiveData的功能,这正适合我的需要

但是,我也希望使用存储库作为访问数据的单一方式。下面是域层中的存储库接口示例(最抽象的一个):

接口存储库{
fun findByUsername(用户名:字符串):列表
趣味添加(实体:T):长
乐趣移除(实体:T)
趣味更新(实体:T):Int
}
这里我遇到了一个问题——我需要在我的ViewModel中从Room的DAO中获取一个LiveData,我希望使用存储库实现来获取它。但为了实现这一点,我需要:

  • 更改存储库方法findByUsername以返回LiveData>
  • 或者直接从ViewModel调用Room的DAO,完全跳过存储库实现
  • 这两种选择都有足够的缺点:

  • 如果我将
    android.arch.lifecycle.LiveData
    导入到我的存储库界面中,它将打破域层的抽象,因为它现在依赖于android体系结构库
  • 如果我在ViewModel中直接调用Room的DAO作为
    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()