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,
@ -66,16 +65,16 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
private val contentUrlResolver: ContentUrlResolver, private val contentUrlResolver: ContentUrlResolver,
private val contentUploadProgressTracker: ContentUploadStateTracker) private val contentUploadProgressTracker: ContentUploadStateTracker)
: Session, : Session,
RoomService by roomService, RoomService by roomService,
RoomDirectoryService by roomDirectoryService, RoomDirectoryService by roomDirectoryService,
GroupService by groupService, GroupService by groupService,
UserService by userService, UserService by userService,
CryptoService by cryptoService, CryptoService by cryptoService,
CacheService by cacheService, CacheService by cacheService,
SignOutService by signOutService, SignOutService by signOutService,
FilterService by filterService, FilterService by filterService,
PushRuleService by pushRuleService, PushRuleService by pushRuleService,
PushersService by pushersService { PushersService by pushersService {


private var isOpen = false private var isOpen = false


@ -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()
)