Created
December 8, 2019 10:10
-
-
Save juliuscanute/2d816dc833aba9bb4935b501b1f538b2 to your computer and use it in GitHub Desktop.
[Data Layer] #clean #architecture #android
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.raywenderlich.android.creatures.cache | |
| class CreatureCacheImpl @Inject constructor(private val creaturesDatabase: CreaturesDatabase, | |
| private val entityMapper: CreatureEntityMapper, | |
| private val preferencesHelper: PreferencesHelper) : | |
| CreatureCache { | |
| companion object { | |
| private const val EXPIRATION_TIME = (60 * 10 * 1000).toLong() // 10 minutes | |
| } | |
| /** | |
| * Retrieve an instance from the database, used for tests. | |
| */ | |
| internal fun getDatabase(): CreaturesDatabase { | |
| return creaturesDatabase | |
| } | |
| /** | |
| * Remove all the data from all the tables in the database. | |
| */ | |
| override fun clearCreatures(): Completable { | |
| return Completable.defer { | |
| creaturesDatabase.cachedCreatureDao().clearCreatures() | |
| Completable.complete() | |
| } | |
| } | |
| /** | |
| * Save the given list of [CreatureEntity] instances to the database. | |
| */ | |
| override fun saveCreatures(creatures: List<CreatureEntity>): Completable { | |
| return Completable.defer { | |
| creatures.forEach { | |
| creaturesDatabase.cachedCreatureDao().insertCreature(entityMapper.mapToCached(it)) | |
| } | |
| Completable.complete() | |
| } | |
| } | |
| /** | |
| * Retrieve a list of [CreatureEntity] instances from the database. | |
| */ | |
| override fun getCreatures(): Flowable<List<CreatureEntity>> { | |
| return Flowable.defer { | |
| Flowable.just(creaturesDatabase.cachedCreatureDao().getCreatures()) | |
| }.map { | |
| it.map { entityMapper.mapFromCached(it) } | |
| } | |
| } | |
| /** | |
| * Check whether there are instances of [CachedCreature] stored in the cache. | |
| */ | |
| override fun isCached(): Single<Boolean> { | |
| return Single.defer { | |
| Single.just(creaturesDatabase.cachedCreatureDao().getCreatures().isNotEmpty()) | |
| } | |
| } | |
| /** | |
| * Set a point in time at when the cache was last updated. | |
| */ | |
| override fun setLastCacheTime(lastCache: Long) { | |
| preferencesHelper.lastCacheTime = lastCache | |
| } | |
| /** | |
| * Check whether the current cached data exceeds the defined [EXPIRATION_TIME] time. | |
| */ | |
| override fun isExpired(): Boolean { | |
| val currentTime = System.currentTimeMillis() | |
| val lastUpdateTime = this.getLastCacheUpdateTimeMillis() | |
| return currentTime - lastUpdateTime > EXPIRATION_TIME | |
| } | |
| /** | |
| * Get in millis, the last time the cache was accessed. | |
| */ | |
| private fun getLastCacheUpdateTimeMillis(): Long { | |
| return preferencesHelper.lastCacheTime | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.raywenderlich.android.creatures.data | |
| class CreatureDataRepository @Inject constructor(private val factory: CreatureDataStoreFactory, | |
| private val creatureMapper: CreatureMapper) : CreatureRepository { | |
| override fun clearCreatures(): Completable { | |
| return factory.retrieveCacheDataStore().clearCreatures() | |
| } | |
| override fun saveCreatures(creatures: List<Creature>): Completable { | |
| val creatureEntities = mutableListOf<CreatureEntity>() | |
| creatures.map { creatureEntities.add(creatureMapper.mapToEntity(it)) } | |
| return factory.retrieveCacheDataStore().saveCreatures(creatureEntities) | |
| } | |
| override fun getCreatures(): Flowable<List<Creature>> { | |
| return factory.retrieveCacheDataStore().isCached() | |
| .flatMapPublisher { | |
| factory.retrieveDataStore(it).getCreatures() | |
| } | |
| .flatMap { | |
| Flowable.just(it.map { creatureMapper.mapFromEntity(it) }) | |
| } | |
| .flatMap { | |
| saveCreatures(it).toSingle { it }.toFlowable() | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.raywenderlich.android.creatures.data.source | |
| open class CreatureDataStoreFactory @Inject constructor( | |
| private val creatureCache: CreatureCache, | |
| private val creatureCacheDataStore: CreatureCacheDataStore, | |
| private val creatureRemoteDataStore: CreatureRemoteDataStore) { | |
| /** | |
| * Returns a DataStore based on whether or not there is content in the cache and the cache | |
| * has not expired | |
| */ | |
| open fun retrieveDataStore(isCached: Boolean): CreatureDataStore { | |
| if (isCached && !creatureCache.isExpired()) { | |
| return retrieveCacheDataStore() | |
| } | |
| return retrieveRemoteDataStore() | |
| } | |
| /** | |
| * Return an instance of the Cache Data Store | |
| */ | |
| open fun retrieveCacheDataStore(): CreatureDataStore { | |
| return creatureCacheDataStore | |
| } | |
| /** | |
| * Return an instance of the Remote Data Store | |
| */ | |
| open fun retrieveRemoteDataStore(): CreatureDataStore { | |
| return creatureRemoteDataStore | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.raywenderlich.android.creatures.remote.mapper | |
| open class CreatureEntityMapper @Inject constructor(): EntityMapper<CreatureModel, CreatureEntity> { | |
| override fun mapFromRemote(type: CreatureModel) = | |
| CreatureEntity(type.id, type.firstName, type.lastName, type.nickname, type.image, type.planet) | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.raywenderlich.android.creatures.data.mapper | |
| open class CreatureMapper @Inject constructor(): Mapper<CreatureEntity, Creature> { | |
| override fun mapFromEntity(type: CreatureEntity) = | |
| Creature(type.id, type.firstName, type.lastName, type.nickname, type.image, type.planet) | |
| override fun mapToEntity(type: Creature) = | |
| CreatureEntity(type.id, type.firstName, type.lastName, type.nickname, type.image, type.planet) | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.raywenderlich.android.creatures.data.source | |
| /** | |
| * Implementation of the [CreatureDataStore] interface to provide a means of communicating | |
| * with the remote data source | |
| */ | |
| open class CreatureRemoteDataStore @Inject constructor(private val creatureRemote: CreatureRemote) : | |
| CreatureDataStore { | |
| override fun clearCreatures(): Completable { | |
| throw UnsupportedOperationException() | |
| } | |
| override fun saveCreatures(creatures: List<CreatureEntity>): Completable { | |
| throw UnsupportedOperationException() | |
| } | |
| override fun getCreatures(): Flowable<List<CreatureEntity>> { | |
| return creatureRemote.getCreatures() | |
| } | |
| override fun isCached(): Single<Boolean> { | |
| throw UnsupportedOperationException() | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.raywenderlich.android.creatures.remote | |
| class CreatureRemoteImpl @Inject constructor(private val creatureService: CreatureService, | |
| private val entityMapper: CreatureEntityMapper): | |
| CreatureRemote { | |
| /** | |
| * Retrieve a list of [CreatureEntity] instances from the [CreatureService]. | |
| */ | |
| override fun getCreatures(): Flowable<List<CreatureEntity>> { | |
| return creatureService.getCreatures() | |
| .map { it.creatures } | |
| .map { | |
| val entities = mutableListOf<CreatureEntity>() | |
| it.forEach { entities.add(entityMapper.mapFromRemote(it)) } | |
| entities | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.raywenderlich.android.creatures.cache | |
| @Database(entities = arrayOf(CachedCreature::class), version = 1) | |
| abstract class CreaturesDatabase @Inject constructor() : RoomDatabase() { | |
| abstract fun cachedCreatureDao(): CachedCreatureDao | |
| private var instance: CreaturesDatabase? = null | |
| private val lock = Any() | |
| fun getInstance(context: Context): CreaturesDatabase { | |
| if (instance == null) { | |
| synchronized(lock) { | |
| if (instance == null) { | |
| instance = Room.databaseBuilder(context.applicationContext, | |
| CreaturesDatabase::class.java, "creatures.db").build() | |
| } | |
| return instance!! | |
| } | |
| } | |
| return instance!! | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.raywenderlich.android.creatures.remote.mapper | |
| /** | |
| * Interface for model mappers. It provides helper methods that facilitate | |
| * retrieving of models from outer data source layers | |
| * | |
| * @param <M> the remote model input type | |
| * @param <E> the entity model output type | |
| */ | |
| interface EntityMapper<in M, out E> { | |
| fun mapFromRemote(type: M): E | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment