Rework injection of monarchy instances (introduce disk and memory)

This commit is contained in:
ganfra 2018-11-26 17:09:49 +01:00
parent 660ba5436b
commit af79e1d0b5
17 changed files with 136 additions and 101 deletions

View File

@ -28,8 +28,7 @@ class HomeModule(private val homeActivity: HomeActivity) : Module {
TextItemFactory()
}
factory {
val roomId = it.get(0) as String
factory { (roomId: String) ->
TimelineEventController(roomId, get(), get(), get())
}

View File

@ -22,7 +22,7 @@ import im.vector.riotredesign.features.home.AvatarRenderer
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
import kotlinx.android.synthetic.main.fragment_room_detail.*
import org.koin.android.ext.android.inject
import org.koin.core.parameter.ParameterList
import org.koin.core.parameter.parametersOf
class RoomDetailFragment : RiotFragment() {
@ -40,7 +40,7 @@ class RoomDetailFragment : RiotFragment() {
private val currentSession = matrix.currentSession
private var roomId: String by UnsafeFragmentArgumentDelegate()
private var eventId: String? by FragmentArgumentDelegate()
private val timelineEventController by inject<TimelineEventController>(parameters = { ParameterList(roomId) })
private val timelineEventController by inject<TimelineEventController> { parametersOf(roomId) }
private lateinit var room: Room
private lateinit var scrollOnNewMessageCallback: ScrollOnNewMessageCallback

View File

@ -0,0 +1,8 @@
package im.vector.matrix.android.internal.database
import com.zhuinden.monarchy.Monarchy
data class DatabaseInstances(
val disk: Monarchy,
val inMemory: Monarchy
)

View File

@ -0,0 +1,41 @@
package im.vector.matrix.android.internal.database
import android.content.Context
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.internal.session.DefaultSession
import io.realm.RealmConfiguration
import org.koin.dsl.context.ModuleDefinition
import org.koin.dsl.module.Module
import org.koin.dsl.module.module
import java.io.File
class DatabaseModule : Module {
override fun invoke(): ModuleDefinition = module {
scope(DefaultSession.SCOPE) {
val context = get<Context>()
val sessionParams = get<SessionParams>()
val directory = File(context.filesDir, sessionParams.credentials.userId)
val diskConfiguration = RealmConfiguration.Builder()
.directory(directory)
.name("disk_store.realm")
.deleteRealmIfMigrationNeeded()
.build()
val inMemoryConfiguration = RealmConfiguration.Builder()
.directory(directory)
.name("in_memory_store.realm")
.inMemory()
.build()
DatabaseInstances(
disk = Monarchy.Builder().setRealmConfiguration(diskConfiguration).build(),
inMemory = Monarchy.Builder().setRealmConfiguration(inMemoryConfiguration).build()
)
}
}.invoke()
}

View File

@ -3,6 +3,7 @@ package im.vector.matrix.android.internal.session
import android.arch.lifecycle.LiveData
import android.os.Looper
import android.support.annotation.MainThread
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.group.Group
import im.vector.matrix.android.api.session.group.GroupService
@ -10,7 +11,7 @@ import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.internal.database.DatabaseModule
import im.vector.matrix.android.internal.database.LiveEntityObserver
import im.vector.matrix.android.internal.session.group.GroupModule
import im.vector.matrix.android.internal.session.room.RoomModule
@ -46,7 +47,8 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
val syncModule = SyncModule()
val roomModule = RoomModule()
val groupModule = GroupModule()
StandAloneContext.loadKoinModules(listOf(sessionModule, syncModule, roomModule, groupModule))
val databaseModule = DatabaseModule()
StandAloneContext.loadKoinModules(listOf(sessionModule, syncModule, roomModule, groupModule, databaseModule))
scope = getKoin().getOrCreateScope(SCOPE)
liveEntityUpdaters.forEach { it.start() }
syncThread.start()

View File

@ -1,9 +1,9 @@
package im.vector.matrix.android.internal.session
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.session.group.GroupService
import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.internal.database.DatabaseInstances
import im.vector.matrix.android.internal.database.LiveEntityObserver
import im.vector.matrix.android.internal.session.events.prune.EventsPruner
import im.vector.matrix.android.internal.session.group.DefaultGroupService
@ -13,7 +13,6 @@ import im.vector.matrix.android.internal.session.room.RoomAvatarResolver
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
import im.vector.matrix.android.internal.session.room.members.RoomDisplayNameResolver
import im.vector.matrix.android.internal.session.room.members.RoomMemberDisplayNameResolver
import io.realm.RealmConfiguration
import org.koin.dsl.context.ModuleDefinition
import org.koin.dsl.module.Module
import org.koin.dsl.module.module
@ -28,20 +27,7 @@ internal class SessionModule(private val sessionParams: SessionParams) : Module
}
scope(DefaultSession.SCOPE) {
RealmConfiguration.Builder()
.name(sessionParams.credentials.userId)
.deleteRealmIfMigrationNeeded()
.build()
}
scope(DefaultSession.SCOPE) {
Monarchy.Builder()
.setRealmConfiguration(get())
.build()
}
scope(DefaultSession.SCOPE) {
val retrofitBuilder = get() as Retrofit.Builder
val retrofitBuilder = get<Retrofit.Builder>()
retrofitBuilder
.baseUrl(sessionParams.homeServerConnectionConfig.homeServerUri.toString())
.build()
@ -52,26 +38,27 @@ internal class SessionModule(private val sessionParams: SessionParams) : Module
}
scope(DefaultSession.SCOPE) {
RoomDisplayNameResolver(get(), get(), sessionParams.credentials)
RoomDisplayNameResolver(get<DatabaseInstances>().disk, get(), sessionParams.credentials)
}
scope(DefaultSession.SCOPE) {
RoomAvatarResolver(get(), sessionParams.credentials)
RoomAvatarResolver(get<DatabaseInstances>().disk, sessionParams.credentials)
}
scope(DefaultSession.SCOPE) {
DefaultRoomService(get()) as RoomService
DefaultRoomService(get<DatabaseInstances>().disk) as RoomService
}
scope(DefaultSession.SCOPE) {
DefaultGroupService(get()) as GroupService
DefaultGroupService(get<DatabaseInstances>().disk) as GroupService
}
scope(DefaultSession.SCOPE) {
val roomSummaryUpdater = RoomSummaryUpdater(get(), get(), get(), get(), sessionParams.credentials)
val groupSummaryUpdater = GroupSummaryUpdater(get())
val eventsPruner = EventsPruner(get())
val disk = get<DatabaseInstances>().disk
val roomSummaryUpdater = RoomSummaryUpdater(disk, get(), get(), get(), sessionParams.credentials)
val groupSummaryUpdater = GroupSummaryUpdater(disk)
val eventsPruner = EventsPruner(disk)
listOf<LiveEntityObserver>(roomSummaryUpdater, groupSummaryUpdater, eventsPruner)
}

View File

@ -5,9 +5,9 @@ import androidx.work.Worker
import androidx.work.WorkerParameters
import arrow.core.Option
import com.squareup.moshi.JsonClass
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.internal.database.DatabaseInstances
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.mapper.asEntity
import im.vector.matrix.android.internal.database.model.EventEntity
@ -29,13 +29,13 @@ internal class PruneEventWorker(context: Context,
val deletionIndexes: List<Int>
)
private val monarchy by inject<Monarchy>()
private val dbInstances by inject<DatabaseInstances>()
override fun doWork(): Result {
val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.FAILURE
?: return Result.FAILURE
val result = monarchy.tryTransactionAsync { realm ->
val result = dbInstances.disk.tryTransactionAsync { realm ->
params.updateIndexes.forEach { index ->
val data = params.redactionEvents[index]
pruneEvent(realm, data)
@ -49,7 +49,7 @@ internal class PruneEventWorker(context: Context,
return
}
val eventToPrune = EventEntity.where(realm, eventId = redactionEvent.redacts).findFirst()?.asDomain()
?: return
?: return
val allowedKeys = computeAllowedKeys(eventToPrune.type)
val prunedContent = allowedKeys.fold(
@ -63,22 +63,22 @@ internal class PruneEventWorker(context: Context,
private fun computeAllowedKeys(type: String): Option<List<String>> {
// Add filtered content, allowed keys in content depends on the event type
val result = when (type) {
EventType.STATE_ROOM_MEMBER -> listOf("membership")
EventType.STATE_ROOM_CREATE -> listOf("creator")
EventType.STATE_ROOM_JOIN_RULES -> listOf("join_rule")
EventType.STATE_ROOM_MEMBER -> listOf("membership")
EventType.STATE_ROOM_CREATE -> listOf("creator")
EventType.STATE_ROOM_JOIN_RULES -> listOf("join_rule")
EventType.STATE_ROOM_POWER_LEVELS -> listOf("users",
"users_default",
"events",
"events_default",
"state_default",
"ban",
"kick",
"redact",
"invite")
EventType.STATE_ROOM_ALIASES -> listOf("aliases")
EventType.STATE_CANONICAL_ALIAS -> listOf("alias")
EventType.FEEDBACK -> listOf("type", "target_event_id")
else -> null
"users_default",
"events",
"events_default",
"state_default",
"ban",
"kick",
"redact",
"invite")
EventType.STATE_ROOM_ALIASES -> listOf("aliases")
EventType.STATE_CANONICAL_ALIAS -> listOf("alias")
EventType.FEEDBACK -> listOf("type", "target_event_id")
else -> null
}
return Option.fromNullable(result)
}

View File

@ -1,5 +1,6 @@
package im.vector.matrix.android.internal.session.group
import im.vector.matrix.android.internal.database.DatabaseInstances
import im.vector.matrix.android.internal.session.DefaultSession
import org.koin.dsl.context.ModuleDefinition
import org.koin.dsl.module.Module
@ -16,7 +17,7 @@ class GroupModule : Module {
}
scope(DefaultSession.SCOPE) {
GetGroupDataRequest(get(), get(), get(), get())
GetGroupDataRequest(get(), get<DatabaseInstances>().disk, get(), get())
}
}.invoke()

View File

@ -3,7 +3,6 @@ package im.vector.matrix.android.internal.session.room
import android.arch.lifecycle.LiveData
import android.arch.lifecycle.Transformations
import android.arch.paging.PagedList
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.events.model.EnrichedEvent
import im.vector.matrix.android.api.session.events.model.Event
@ -14,6 +13,7 @@ import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.MyMembership
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.database.DatabaseInstances
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
@ -32,12 +32,12 @@ internal data class DefaultRoom(
private val loadRoomMembersRequest by inject<LoadRoomMembersRequest>()
private val syncTokenStore by inject<SyncTokenStore>()
private val monarchy by inject<Monarchy>()
private val timelineHolder by inject<TimelineHolder>(parameters = { parametersOf(roomId) })
private val sendService by inject<SendService>(parameters = { parametersOf(roomId) })
private val dbInstances by inject<DatabaseInstances>()
private val timelineHolder by inject<TimelineHolder> { parametersOf(roomId) }
private val sendService by inject<SendService> { parametersOf(roomId) }
override val roomSummary: LiveData<RoomSummary> by lazy {
val liveData = monarchy
val liveData = dbInstances.disk
.findAllMappedWithChanges(
{ realm -> RoomSummaryEntity.where(realm, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) },
{ from -> from.asDomain() })
@ -61,10 +61,10 @@ internal data class DefaultRoom(
}
private fun areAllMembersLoaded(): Boolean {
return monarchy
.fetchAllCopiedSync { RoomEntity.where(it, roomId) }
.firstOrNull()
?.areAllMembersLoaded ?: false
return dbInstances.disk
.fetchAllCopiedSync { RoomEntity.where(it, roomId) }
.firstOrNull()
?.areAllMembersLoaded ?: false
}

View File

@ -1,11 +1,11 @@
package im.vector.matrix.android.internal.session.room
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.model.MyMembership
import im.vector.matrix.android.api.session.room.model.RoomAvatarContent
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.query.last

View File

@ -4,12 +4,14 @@ import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.session.room.SendService
import im.vector.matrix.android.api.session.room.TimelineHolder
import im.vector.matrix.android.api.session.room.send.EventFactory
import im.vector.matrix.android.internal.database.DatabaseInstances
import im.vector.matrix.android.internal.session.DefaultSession
import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersRequest
import im.vector.matrix.android.internal.session.room.send.DefaultSendService
import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineHolder
import im.vector.matrix.android.internal.session.room.timeline.PaginationRequest
import im.vector.matrix.android.internal.session.room.timeline.TimelineBoundaryCallback
import im.vector.matrix.android.internal.util.PagingRequestHelper
import org.koin.dsl.context.ModuleDefinition
import org.koin.dsl.module.Module
import org.koin.dsl.module.module
@ -27,18 +29,11 @@ class RoomModule : Module {
}
scope(DefaultSession.SCOPE) {
LoadRoomMembersRequest(get(), get(), get())
LoadRoomMembersRequest(get(), get<DatabaseInstances>().disk, get())
}
scope(DefaultSession.SCOPE) {
PaginationRequest(get(), get(), get())
}
factory {
val roomId: String = it[0]
val timelineBoundaryCallback = TimelineBoundaryCallback(roomId, get(), get(), Executors.newSingleThreadExecutor())
DefaultTimelineHolder(roomId, get(), timelineBoundaryCallback) as TimelineHolder
PaginationRequest(get(), get<DatabaseInstances>().disk, get())
}
scope(DefaultSession.SCOPE) {
@ -46,9 +41,14 @@ class RoomModule : Module {
EventFactory(sessionParams.credentials)
}
factory {
val roomId: String = it[0]
DefaultSendService(roomId, get(), get()) as SendService
factory { (roomId: String) ->
val helper = PagingRequestHelper(Executors.newSingleThreadExecutor())
val timelineBoundaryCallback = TimelineBoundaryCallback(roomId, get(), get<DatabaseInstances>().disk, helper)
DefaultTimelineHolder(roomId, get<DatabaseInstances>().disk, timelineBoundaryCallback) as TimelineHolder
}
factory { (roomId: String) ->
DefaultSendService(roomId, get(), get<DatabaseInstances>().disk) as SendService
}
}.invoke()

View File

@ -19,13 +19,13 @@ package im.vector.matrix.android.internal.session.room.members
import android.content.Context
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.R
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.model.MyMembership
import im.vector.matrix.android.api.session.room.model.RoomAliasesContent
import im.vector.matrix.android.api.session.room.model.RoomCanonicalAliasContent
import im.vector.matrix.android.api.session.room.model.RoomNameContent
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity

View File

@ -4,8 +4,8 @@ import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.internal.database.DatabaseInstances
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.network.executeRequest
@ -26,12 +26,12 @@ internal class SendEventWorker(context: Context, params: WorkerParameters)
)
private val roomAPI by inject<RoomAPI>()
private val monarchy by inject<Monarchy>()
private val dbInstances by inject<DatabaseInstances>()
override fun doWork(): Result {
val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.FAILURE
?: return Result.FAILURE
if (params.event.eventId == null) {
return Result.FAILURE
@ -46,7 +46,7 @@ internal class SendEventWorker(context: Context, params: WorkerParameters)
)
}
result.flatMap { sendResponse ->
monarchy.tryTransactionSync { realm ->
dbInstances.disk.tryTransactionSync { realm ->
val dummyEventEntity = EventEntity.where(realm, params.event.eventId).findFirst()
dummyEventEntity?.eventId = sendResponse.eventId
}

View File

@ -31,9 +31,6 @@ internal class DefaultTimelineHolder(private val roomId: String,
}
override fun timeline(eventId: String?): LiveData<PagedList<EnrichedEvent>> {
if (eventId != null) {
fetchEventIfNeeded()
}
val realmDataSourceFactory = monarchy.createDataSourceFactory {
buildDataSourceFactoryQuery(it, eventId)
}
@ -60,18 +57,21 @@ internal class DefaultTimelineHolder(private val roomId: String,
return monarchy.findAllPagedWithChanges(realmDataSourceFactory, livePagedListBuilder)
}
private fun fetchEventIfNeeded() {
}
private fun buildDataSourceFactoryQuery(realm: Realm, eventId: String?): RealmQuery<EventEntity> {
val query = if (eventId == null) {
EventEntity
.where(realm, roomId = roomId)
.equalTo("${EventEntityFields.CHUNK}.${ChunkEntityFields.IS_LAST}", true)
} else {
val event = EventEntity.where(realm, eventId = eventId).findFirst()
val permalinkRealm: Realm
if (event == null) {
permalinkRealm = realm
} else {
permalinkRealm = realm
}
EventEntity
.where(realm, roomId = roomId)
.where(permalinkRealm, roomId = roomId)
.`in`("${EventEntityFields.CHUNK}.${ChunkEntityFields.EVENTS.EVENT_ID}", arrayOf(eventId))
}
return query.sort(EventEntityFields.DISPLAY_INDEX)

View File

@ -13,11 +13,9 @@ import java.util.concurrent.Executor
internal class TimelineBoundaryCallback(private val roomId: String,
private val paginationRequest: PaginationRequest,
private val monarchy: Monarchy,
ioExecutor: Executor
private val helper: PagingRequestHelper
) : PagedList.BoundaryCallback<EnrichedEvent>() {
private val helper = PagingRequestHelper(ioExecutor)
var limit = 10
override fun onZeroItemsLoaded() {

View File

@ -1,5 +1,6 @@
package im.vector.matrix.android.internal.session.sync
import im.vector.matrix.android.internal.database.DatabaseInstances
import im.vector.matrix.android.internal.session.DefaultSession
import im.vector.matrix.android.internal.session.sync.job.SyncThread
import org.koin.dsl.context.ModuleDefinition
@ -22,15 +23,15 @@ internal class SyncModule : Module {
}
scope(DefaultSession.SCOPE) {
RoomSyncHandler(get(), get())
RoomSyncHandler(get<DatabaseInstances>().disk, get())
}
scope(DefaultSession.SCOPE) {
GroupSyncHandler(get())
GroupSyncHandler(get<DatabaseInstances>().disk)
}
scope(DefaultSession.SCOPE) {
UserAccountDataSyncHandler(get())
UserAccountDataSyncHandler(get<DatabaseInstances>().disk)
}
scope(DefaultSession.SCOPE) {
@ -42,7 +43,7 @@ internal class SyncModule : Module {
}
scope(DefaultSession.SCOPE) {
SyncTokenStore(get())
SyncTokenStore(get<DatabaseInstances>().disk)
}
scope(DefaultSession.SCOPE) {

View File

@ -1,25 +1,23 @@
package im.vector.matrix.android.internal.session.sync
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.internal.database.model.SyncEntity
import io.realm.Realm
import io.realm.RealmConfiguration
internal class SyncTokenStore(private val realmConfiguration: RealmConfiguration) {
internal class SyncTokenStore(private val monarchy: Monarchy) {
fun getLastToken(): String? {
val realm = Realm.getInstance(realmConfiguration)
val token = realm.where(SyncEntity::class.java).findFirst()?.nextBatch
realm.close()
var token: String? = null
monarchy.doWithRealm { realm ->
token = realm.where(SyncEntity::class.java).findFirst()?.nextBatch
}
return token
}
fun saveToken(token: String?) {
val realm = Realm.getInstance(realmConfiguration)
realm.executeTransaction {
monarchy.writeAsync {
val sync = SyncEntity(token)
it.insertOrUpdate(sync)
}
realm.close()
}