Better coroutines management

This commit is contained in:
ganfra 2018-10-13 11:18:49 +02:00
parent 1a967b6326
commit 95fd7190e4
12 changed files with 81 additions and 64 deletions

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View File

@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.3.0-rc-116' ext.kotlin_version = '1.3.0-rc-146'
ext.koin_version = '1.0.1' ext.koin_version = '1.0.1'
repositories { repositories {
google() google()
@ -9,7 +9,7 @@ buildscript {
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.2.0' classpath 'com.android.tools.build:gradle:3.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong

View File

@ -4,7 +4,6 @@ import android.content.Context
import im.vector.matrix.android.BuildConfig import im.vector.matrix.android.BuildConfig
import im.vector.matrix.android.api.auth.Authenticator import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.thread.MainThreadExecutor
import im.vector.matrix.android.internal.auth.AuthModule import im.vector.matrix.android.internal.auth.AuthModule
import im.vector.matrix.android.internal.di.MatrixModule import im.vector.matrix.android.internal.di.MatrixModule
import im.vector.matrix.android.internal.di.NetworkModule import im.vector.matrix.android.internal.di.NetworkModule
@ -13,7 +12,6 @@ import org.koin.standalone.StandAloneContext.loadKoinModules
import org.koin.standalone.inject import org.koin.standalone.inject
import timber.log.Timber import timber.log.Timber
import timber.log.Timber.DebugTree import timber.log.Timber.DebugTree
import java.util.concurrent.Executor
class Matrix(matrixOptions: MatrixOptions) : KoinComponent { class Matrix(matrixOptions: MatrixOptions) : KoinComponent {
@ -38,5 +36,4 @@ class Matrix(matrixOptions: MatrixOptions) : KoinComponent {
} }
data class MatrixOptions(val context: Context, data class MatrixOptions(val context: Context)
val mainExecutor: Executor = MainThreadExecutor())

View File

@ -18,7 +18,7 @@ class AuthModule(private val context: Context) : Module {
override fun invoke(): ModuleDefinition = module { override fun invoke(): ModuleDefinition = module {
single { single {
DefaultAuthenticator(get(), get(), get(), get()) as Authenticator DefaultAuthenticator(get(), get(), get()) as Authenticator
} }
single(name = AUTH_BOX_STORE) { single(name = AUTH_BOX_STORE) {

View File

@ -1,7 +1,7 @@
package im.vector.matrix.android.internal.auth package im.vector.matrix.android.internal.auth
import arrow.core.Either
import arrow.core.leftIfNull import arrow.core.leftIfNull
import com.squareup.moshi.Moshi
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.Authenticator import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
@ -17,10 +17,10 @@ import im.vector.matrix.android.internal.util.CancelableCoroutine
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import retrofit2.Retrofit import retrofit2.Retrofit
class DefaultAuthenticator(private val retrofitBuilder: Retrofit.Builder, class DefaultAuthenticator(private val retrofitBuilder: Retrofit.Builder,
private val jsonMapper: Moshi,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val sessionParamsStore: SessionParamsStore) : Authenticator { private val sessionParamsStore: SessionParamsStore) : Authenticator {
@ -35,28 +35,37 @@ class DefaultAuthenticator(private val retrofitBuilder: Retrofit.Builder,
} }
} }
override fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig, login: String, password: String, callback: MatrixCallback<Session>): Cancelable { override fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig,
val authAPI = buildAuthAPI(homeServerConnectionConfig) login: String,
password: String,
callback: MatrixCallback<Session>): Cancelable {
val job = GlobalScope.launch(coroutineDispatchers.main) { val job = GlobalScope.launch(coroutineDispatchers.main) {
val loginParams = PasswordLoginParams.userIdentifier(login, password, "Mobile") val sessionOrFailure = authenticate(homeServerConnectionConfig, login, password)
executeRequest<Credentials> { sessionOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(it) })
apiCall = authAPI.login(loginParams)
moshi = jsonMapper
dispatcher = coroutineDispatchers.io
}.leftIfNull {
Failure.Unknown(IllegalArgumentException("Credentials shouldn't not be null"))
}.map {
val sessionParams = SessionParams(it, homeServerConnectionConfig)
sessionParamsStore.save(sessionParams)
sessionParams
}.map {
DefaultSession(it)
}.bimap(
{ callback.onFailure(it) }, { callback.onSuccess(it) }
)
} }
return CancelableCoroutine(job) return CancelableCoroutine(job)
}
private suspend fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig,
login: String,
password: String): Either<Failure, Session> = withContext(coroutineDispatchers.io) {
val authAPI = buildAuthAPI(homeServerConnectionConfig)
val loginParams = PasswordLoginParams.userIdentifier(login, password, "Mobile")
executeRequest<Credentials> {
apiCall = authAPI.login(loginParams)
}.leftIfNull {
Failure.Unknown(IllegalArgumentException("Credentials shouldn't not be null"))
}.map {
val sessionParams = SessionParams(it, homeServerConnectionConfig)
sessionParamsStore.save(sessionParams)
sessionParams
}.map {
DefaultSession(it)
}
} }
private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI { private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {

View File

@ -1,6 +1,7 @@
package im.vector.matrix.android.internal.di package im.vector.matrix.android.internal.di
import im.vector.matrix.android.api.MatrixOptions import im.vector.matrix.android.api.MatrixOptions
import im.vector.matrix.android.api.thread.MainThreadExecutor
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO import kotlinx.coroutines.IO
@ -19,7 +20,7 @@ class MatrixModule(private val options: MatrixOptions) : Module {
} }
single { single {
MatrixCoroutineDispatchers(io = Dispatchers.IO, computation = Dispatchers.IO, main = options.mainExecutor.asCoroutineDispatcher()) MatrixCoroutineDispatchers(io = Dispatchers.IO, computation = Dispatchers.IO, main = MainThreadExecutor().asCoroutineDispatcher())
} }
}.invoke() }.invoke()
} }

View File

@ -1,5 +1,6 @@
package im.vector.matrix.android.internal.events.sync package im.vector.matrix.android.internal.events.sync
import im.vector.matrix.android.api.events.Event
import im.vector.matrix.android.internal.database.mapper.EventMapper import im.vector.matrix.android.internal.database.mapper.EventMapper
import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.EventEntity
@ -23,8 +24,8 @@ class RoomSyncHandler(
return return
} }
val roomEntities = ArrayList<RoomEntity>() val roomEntities = ArrayList<RoomEntity>()
roomSyncByRoom.forEach { roomId, roomSync -> roomSyncByRoom.forEach {
val roomEntity = handleJoinedRoom(roomId, roomSync) val roomEntity = handleJoinedRoom(it.key, it.value)
roomEntities.add(roomEntity) roomEntities.add(roomEntity)
} }
roomBox.put(roomEntities) roomBox.put(roomEntities)
@ -40,22 +41,27 @@ class RoomSyncHandler(
} }
roomEntity.membership = RoomEntity.Membership.JOINED roomEntity.membership = RoomEntity.Membership.JOINED
if (roomSync.timeline != null) { if (roomSync.timeline != null) {
val chunkEntity = ChunkEntity() val chunkEntity = eventListToChunk(roomSync.timeline.events, roomSync.timeline.prevBatch)
chunkEntity.prevToken = roomSync.timeline.prevBatch
roomSync.timeline.events
.map { event -> EventMapper.map(event) }
.forEach { chunkEntity.events.add(it) }
roomEntity.chunks.add(chunkEntity) roomEntity.chunks.add(chunkEntity)
} }
if (roomSync.state != null) { if (roomSync.state != null) {
val chunkEntity = ChunkEntity() val chunkEntity = eventListToChunk(roomSync.state.events)
roomSync.state.events
.map { event -> EventMapper.map(event) }
.forEach { chunkEntity.events.add(it) }
roomEntity.chunks.add(chunkEntity) roomEntity.chunks.add(chunkEntity)
} }
return roomEntity return roomEntity
} }
private fun eventListToChunk(eventList: List<Event>,
prevToken: String? = null,
nextToken: String? = null): ChunkEntity {
val chunkEntity = ChunkEntity()
chunkEntity.prevToken = prevToken
chunkEntity.nextToken = nextToken
eventList
.map { event -> EventMapper.map(event) }
.forEach { chunkEntity.events.add(it) }
return chunkEntity
}
} }

View File

@ -24,7 +24,7 @@ class SyncModule : Module {
} }
scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
Synchronizer(get(), get(), get(), get()) Synchronizer(get(), get(), get())
} }
}.invoke() }.invoke()

View File

@ -1,6 +1,5 @@
package im.vector.matrix.android.internal.events.sync package im.vector.matrix.android.internal.events.sync
import com.squareup.moshi.Moshi
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.events.sync.data.SyncResponse import im.vector.matrix.android.internal.events.sync.data.SyncResponse
@ -11,30 +10,32 @@ import im.vector.matrix.android.internal.util.CancelableCoroutine
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class Synchronizer(private val syncAPI: SyncAPI, class Synchronizer(private val syncAPI: SyncAPI,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val jsonMapper: Moshi,
private val syncResponseHandler: SyncResponseHandler) { private val syncResponseHandler: SyncResponseHandler) {
fun synchronize(callback: MatrixCallback<SyncResponse>): Cancelable { fun synchronize(callback: MatrixCallback<SyncResponse>): Cancelable {
val job = GlobalScope.launch(coroutineDispatchers.main) { val job = GlobalScope.launch(coroutineDispatchers.main) {
val params = HashMap<String, String>() val syncOrFailure = synchronize()
val filterBody = FilterBody() syncOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(it) })
FilterUtil.enableLazyLoading(filterBody, true)
params["timeout"] = "0"
params["filter"] = filterBody.toJSONString()
val syncResponse = executeRequest<SyncResponse> {
apiCall = syncAPI.sync(params)
moshi = jsonMapper
dispatcher = coroutineDispatchers.io
}.map {
syncResponseHandler.handleResponse(it, null, false)
it
}
syncResponse.bimap({ callback.onFailure(it) }, { callback.onSuccess(it) })
} }
return CancelableCoroutine(job) return CancelableCoroutine(job)
} }
private suspend fun synchronize() = withContext(coroutineDispatchers.io) {
val params = HashMap<String, String>()
val filterBody = FilterBody()
FilterUtil.enableLazyLoading(filterBody, true)
params["timeout"] = "0"
params["filter"] = filterBody.toJSONString()
executeRequest<SyncResponse> {
apiCall = syncAPI.sync(params)
}.map {
syncResponseHandler.handleResponse(it, null, false)
it
}
}
} }

View File

@ -4,9 +4,9 @@ import arrow.core.Either
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.failure.MatrixError import im.vector.matrix.android.api.failure.MatrixError
import kotlinx.coroutines.CoroutineDispatcher import im.vector.matrix.android.internal.di.MoshiProvider
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.withContext import kotlinx.coroutines.coroutineScope
import okhttp3.ResponseBody import okhttp3.ResponseBody
import retrofit2.Response import retrofit2.Response
import java.io.IOException import java.io.IOException
@ -15,13 +15,11 @@ suspend inline fun <DATA> executeRequest(block: Request<DATA>.() -> Unit) = Requ
class Request<DATA> { class Request<DATA> {
var moshi: Moshi = MoshiProvider.providesMoshi()
lateinit var apiCall: Deferred<Response<DATA>> lateinit var apiCall: Deferred<Response<DATA>>
lateinit var moshi: Moshi
lateinit var dispatcher: CoroutineDispatcher
suspend fun execute(): Either<Failure, DATA?> = withContext(dispatcher) {
return@withContext try {
suspend fun execute(): Either<Failure, DATA?> = coroutineScope {
try {
val response = apiCall.await() val response = apiCall.await()
if (response.isSuccessful) { if (response.isSuccessful) {
val result = response.body() val result = response.body()