Realm: avoid using monarchy thread for custom work

This commit is contained in:
ganfra 2019-07-03 14:48:45 +02:00
parent eefd09d022
commit 93ce0cc5e9
8 changed files with 75 additions and 35 deletions

View File

@ -17,7 +17,10 @@
package im.vector.matrix.android.internal.database package im.vector.matrix.android.internal.database


import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.internal.util.createBackgroundHandler
import io.realm.OrderedCollectionChangeSet import io.realm.OrderedCollectionChangeSet
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.RealmResults import io.realm.RealmResults
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
@ -29,17 +32,24 @@ internal interface LiveEntityObserver {
fun isStarted(): Boolean fun isStarted(): Boolean
} }


internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val monarchy: Monarchy) internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val realmConfiguration: RealmConfiguration)
: LiveEntityObserver { : LiveEntityObserver {


private companion object {
val BACKGROUND_HANDLER = createBackgroundHandler("LIVE_ENTITY_BACKGROUND")
}

protected abstract val query: Monarchy.Query<T> protected abstract val query: Monarchy.Query<T>
private val isStarted = AtomicBoolean(false) private val isStarted = AtomicBoolean(false)
private val backgroundRealm = AtomicReference<Realm>()
private lateinit var results: AtomicReference<RealmResults<T>> private lateinit var results: AtomicReference<RealmResults<T>>


override fun start() { override fun start() {
if (isStarted.compareAndSet(false, true)) { if (isStarted.compareAndSet(false, true)) {
monarchy.postToMonarchyThread { BACKGROUND_HANDLER.post {
val queryResults = query.createQuery(it).findAll() val realm = Realm.getInstance(realmConfiguration)
backgroundRealm.set(realm)
val queryResults = query.createQuery(realm).findAll()
queryResults.addChangeListener { t, changeSet -> queryResults.addChangeListener { t, changeSet ->
onChanged(t, changeSet) onChanged(t, changeSet)
} }
@ -50,8 +60,11 @@ internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val m


override fun dispose() { override fun dispose() {
if (isStarted.compareAndSet(true, false)) { if (isStarted.compareAndSet(true, false)) {
monarchy.postToMonarchyThread { BACKGROUND_HANDLER.post {
results.getAndSet(null).removeAllChangeListeners() results.getAndSet(null).removeAllChangeListeners()
backgroundRealm.getAndSet(null).also {
it.close()
}
} }
} }
} }

View File

@ -50,7 +50,6 @@ import javax.inject.Inject
internal class DefaultSession @Inject constructor(override val sessionParams: SessionParams, internal class DefaultSession @Inject constructor(override val sessionParams: SessionParams,
private val context: Context, private val context: Context,
private val liveEntityObservers: Set<@JvmSuppressWildcards LiveEntityObserver>, private val liveEntityObservers: Set<@JvmSuppressWildcards LiveEntityObserver>,
private val monarchy: Monarchy,
private val sessionListeners: SessionListeners, private val sessionListeners: SessionListeners,
private val roomService: RoomService, private val roomService: RoomService,
private val roomDirectoryService: RoomDirectoryService, private val roomDirectoryService: RoomDirectoryService,
@ -85,9 +84,6 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
assertMainThread() assertMainThread()
assert(!isOpen) assert(!isOpen)
isOpen = true isOpen = true
if (!monarchy.isMonarchyThreadOpen) {
monarchy.openManually()
}
liveEntityObservers.forEach { it.start() } liveEntityObservers.forEach { it.start() }
} }


@ -123,9 +119,6 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
stopSync() stopSync()
liveEntityObservers.forEach { it.dispose() } liveEntityObservers.forEach { it.dispose() }
cryptoService.close() cryptoService.close()
if (monarchy.isMonarchyThreadOpen) {
monarchy.closeManually()
}
isOpen = false isOpen = false
} }



View File

@ -27,14 +27,16 @@ import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
import im.vector.matrix.android.internal.database.model.GroupEntity import im.vector.matrix.android.internal.database.model.GroupEntity
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.SessionDatabase
import im.vector.matrix.android.internal.worker.WorkerParamsFactory import im.vector.matrix.android.internal.worker.WorkerParamsFactory
import io.realm.RealmConfiguration
import javax.inject.Inject import javax.inject.Inject


private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER" private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER"


internal class GroupSummaryUpdater @Inject constructor(private val context: Context, internal class GroupSummaryUpdater @Inject constructor(private val context: Context,
private val credentials: Credentials, private val credentials: Credentials,
monarchy: Monarchy) : RealmLiveEntityObserver<GroupEntity>(monarchy) { @SessionDatabase realmConfiguration: RealmConfiguration) : RealmLiveEntityObserver<GroupEntity>(realmConfiguration) {


override val query = Monarchy.Query<GroupEntity> { GroupEntity.where(it) } override val query = Monarchy.Query<GroupEntity> { GroupEntity.where(it) }



View File

@ -22,8 +22,10 @@ import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
import im.vector.matrix.android.internal.database.mapper.asDomain 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.EventEntity
import im.vector.matrix.android.internal.database.query.types import im.vector.matrix.android.internal.database.query.types
import im.vector.matrix.android.internal.di.SessionDatabase
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.task.configureWith
import io.realm.RealmConfiguration
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject


@ -33,11 +35,11 @@ import javax.inject.Inject
* The summaries can then be extracted and added (as a decoration) to a TimelineEvent for final display. * The summaries can then be extracted and added (as a decoration) to a TimelineEvent for final display.
*/ */


internal class EventRelationsAggregationUpdater @Inject constructor(monarchy: Monarchy, internal class EventRelationsAggregationUpdater @Inject constructor(@SessionDatabase realmConfiguration: RealmConfiguration,
private val credentials: Credentials, private val credentials: Credentials,
private val task: EventRelationsAggregationTask, private val task: EventRelationsAggregationTask,
private val taskExecutor: TaskExecutor) : private val taskExecutor: TaskExecutor) :
RealmLiveEntityObserver<EventEntity>(monarchy) { RealmLiveEntityObserver<EventEntity>(realmConfiguration) {


override val query = Monarchy.Query<EventEntity> { override val query = Monarchy.Query<EventEntity> {
EventEntity.types(it, listOf( EventEntity.types(it, listOf(

View File

@ -23,9 +23,11 @@ import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
import im.vector.matrix.android.internal.database.mapper.asDomain 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.EventEntity
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.SessionDatabase
import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.SessionScope
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.task.configureWith
import io.realm.RealmConfiguration
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject


@ -33,11 +35,11 @@ import javax.inject.Inject
* Listens to the database for the insertion of any redaction event. * Listens to the database for the insertion of any redaction event.
* As it will actually delete the content, it should be called last in the list of listener. * As it will actually delete the content, it should be called last in the list of listener.
*/ */
internal class EventsPruner @Inject constructor(monarchy: Monarchy, internal class EventsPruner @Inject constructor(@SessionDatabase realmConfiguration: RealmConfiguration,
private val credentials: Credentials, private val credentials: Credentials,
private val pruneEventTask: PruneEventTask, private val pruneEventTask: PruneEventTask,
private val taskExecutor: TaskExecutor) : private val taskExecutor: TaskExecutor) :
RealmLiveEntityObserver<EventEntity>(monarchy) { RealmLiveEntityObserver<EventEntity>(realmConfiguration) {


override val query = Monarchy.Query<EventEntity> { EventEntity.where(it, type = EventType.REDACTION) } override val query = Monarchy.Query<EventEntity> { EventEntity.where(it, type = EventType.REDACTION) }



View File

@ -16,9 +16,6 @@


package im.vector.matrix.android.internal.session.room.timeline package im.vector.matrix.android.internal.session.room.timeline


import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
@ -43,6 +40,8 @@ import im.vector.matrix.android.internal.database.query.whereInRoom
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.util.Debouncer import im.vector.matrix.android.internal.util.Debouncer
import im.vector.matrix.android.internal.util.createBackgroundHandler
import im.vector.matrix.android.internal.util.createUIHandler
import io.realm.OrderedCollectionChangeSet import io.realm.OrderedCollectionChangeSet
import io.realm.OrderedRealmCollectionChangeListener import io.realm.OrderedRealmCollectionChangeListener
import io.realm.Realm import io.realm.Realm
@ -75,9 +74,7 @@ internal class DefaultTimeline(
) : Timeline { ) : Timeline {


private companion object { private companion object {
val BACKGROUND_HANDLER = Handler( val BACKGROUND_HANDLER = createBackgroundHandler("TIMELINE_DB_THREAD")
HandlerThread("TIMELINE_DB_THREAD").apply { start() }.looper
)
} }


override var listener: Timeline.Listener? = null override var listener: Timeline.Listener? = null
@ -90,7 +87,7 @@ internal class DefaultTimeline(


private val isStarted = AtomicBoolean(false) private val isStarted = AtomicBoolean(false)
private val isReady = AtomicBoolean(false) private val isReady = AtomicBoolean(false)
private val mainHandler = Handler(Looper.getMainLooper()) private val mainHandler = createUIHandler()
private val backgroundRealm = AtomicReference<Realm>() private val backgroundRealm = AtomicReference<Realm>()
private val cancelableBag = CancelableBag() private val cancelableBag = CancelableBag()
private val debouncer = Debouncer(mainHandler) private val debouncer = Debouncer(mainHandler)

View File

@ -22,17 +22,19 @@ import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.EventEntityFields import im.vector.matrix.android.internal.database.model.EventEntityFields
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.SessionDatabase
import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.SessionScope
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.TaskThread import im.vector.matrix.android.internal.task.TaskThread
import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.task.configureWith
import io.realm.RealmConfiguration
import io.realm.Sort import io.realm.Sort
import javax.inject.Inject import javax.inject.Inject


internal class UserEntityUpdater @Inject constructor(monarchy: Monarchy, internal class UserEntityUpdater @Inject constructor(@SessionDatabase realmConfiguration: RealmConfiguration,
private val updateUserTask: UpdateUserTask, private val updateUserTask: UpdateUserTask,
private val taskExecutor: TaskExecutor) private val taskExecutor: TaskExecutor)
: RealmLiveEntityObserver<EventEntity>(monarchy) { : RealmLiveEntityObserver<EventEntity>(realmConfiguration) {


override val query = Monarchy.Query<EventEntity> { override val query = Monarchy.Query<EventEntity> {
EventEntity EventEntity

View File

@ -0,0 +1,29 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package im.vector.matrix.android.internal.util

import android.os.Handler
import android.os.HandlerThread
import android.os.Looper

fun createBackgroundHandler(name: String): Handler = Handler(
HandlerThread(name).apply { start() }.looper
)

fun createUIHandler(): Handler = Handler(
Looper.getMainLooper()
)