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.

buildscript {
ext.kotlin_version = '1.3.0-rc-116'
ext.kotlin_version = '1.3.0-rc-146'
ext.koin_version = '1.0.1'
repositories {
google()
@ -9,7 +9,7 @@ buildscript {
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
}
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"

// 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.api.auth.Authenticator
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.di.MatrixModule
import im.vector.matrix.android.internal.di.NetworkModule
@ -13,7 +12,6 @@ import org.koin.standalone.StandAloneContext.loadKoinModules
import org.koin.standalone.inject
import timber.log.Timber
import timber.log.Timber.DebugTree
import java.util.concurrent.Executor


class Matrix(matrixOptions: MatrixOptions) : KoinComponent {
@ -38,5 +36,4 @@ class Matrix(matrixOptions: MatrixOptions) : KoinComponent {

}

data class MatrixOptions(val context: Context,
val mainExecutor: Executor = MainThreadExecutor())
data class MatrixOptions(val context: Context)

View File

@ -18,7 +18,7 @@ class AuthModule(private val context: Context) : Module {
override fun invoke(): ModuleDefinition = module {

single {
DefaultAuthenticator(get(), get(), get(), get()) as Authenticator
DefaultAuthenticator(get(), get(), get()) as Authenticator
}

single(name = AUTH_BOX_STORE) {

View File

@ -1,7 +1,7 @@
package im.vector.matrix.android.internal.auth

import arrow.core.Either
import arrow.core.leftIfNull
import com.squareup.moshi.Moshi
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.Authenticator
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 kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import retrofit2.Retrofit

class DefaultAuthenticator(private val retrofitBuilder: Retrofit.Builder,
private val jsonMapper: Moshi,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
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 {
val authAPI = buildAuthAPI(homeServerConnectionConfig)
override fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig,
login: String,
password: String,
callback: MatrixCallback<Session>): Cancelable {

val job = GlobalScope.launch(coroutineDispatchers.main) {
val loginParams = PasswordLoginParams.userIdentifier(login, password, "Mobile")
executeRequest<Credentials> {
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) }
)
val sessionOrFailure = authenticate(homeServerConnectionConfig, login, password)
sessionOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(it) })
}
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 {

View File

@ -1,6 +1,7 @@
package im.vector.matrix.android.internal.di

import im.vector.matrix.android.api.MatrixOptions
import im.vector.matrix.android.api.thread.MainThreadExecutor
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
@ -19,7 +20,7 @@ class MatrixModule(private val options: MatrixOptions) : Module {
}

single {
MatrixCoroutineDispatchers(io = Dispatchers.IO, computation = Dispatchers.IO, main = options.mainExecutor.asCoroutineDispatcher())
MatrixCoroutineDispatchers(io = Dispatchers.IO, computation = Dispatchers.IO, main = MainThreadExecutor().asCoroutineDispatcher())
}
}.invoke()
}

View File

@ -11,4 +11,4 @@ object MoshiProvider {
return moshi
}

}
}

View File

@ -1,5 +1,6 @@
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.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.EventEntity
@ -23,8 +24,8 @@ class RoomSyncHandler(
return
}
val roomEntities = ArrayList<RoomEntity>()
roomSyncByRoom.forEach { roomId, roomSync ->
val roomEntity = handleJoinedRoom(roomId, roomSync)
roomSyncByRoom.forEach {
val roomEntity = handleJoinedRoom(it.key, it.value)
roomEntities.add(roomEntity)
}
roomBox.put(roomEntities)
@ -40,22 +41,27 @@ class RoomSyncHandler(
}
roomEntity.membership = RoomEntity.Membership.JOINED
if (roomSync.timeline != null) {
val chunkEntity = ChunkEntity()
chunkEntity.prevToken = roomSync.timeline.prevBatch
roomSync.timeline.events
.map { event -> EventMapper.map(event) }
.forEach { chunkEntity.events.add(it) }
val chunkEntity = eventListToChunk(roomSync.timeline.events, roomSync.timeline.prevBatch)
roomEntity.chunks.add(chunkEntity)
}

if (roomSync.state != null) {
val chunkEntity = ChunkEntity()
roomSync.state.events
.map { event -> EventMapper.map(event) }
.forEach { chunkEntity.events.add(it) }
val chunkEntity = eventListToChunk(roomSync.state.events)
roomEntity.chunks.add(chunkEntity)
}
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) {
Synchronizer(get(), get(), get(), get())
Synchronizer(get(), get(), get())
}

}.invoke()

View File

@ -1,6 +1,5 @@
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.util.Cancelable
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 kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class Synchronizer(private val syncAPI: SyncAPI,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val jsonMapper: Moshi,
private val syncResponseHandler: SyncResponseHandler) {

fun synchronize(callback: MatrixCallback<SyncResponse>): Cancelable {
val job = GlobalScope.launch(coroutineDispatchers.main) {
val params = HashMap<String, String>()
val filterBody = FilterBody()
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) })
val syncOrFailure = synchronize()
syncOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(it) })
}
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 im.vector.matrix.android.api.failure.Failure
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.withContext
import kotlinx.coroutines.coroutineScope
import okhttp3.ResponseBody
import retrofit2.Response
import java.io.IOException
@ -15,13 +15,11 @@ suspend inline fun <DATA> executeRequest(block: Request<DATA>.() -> Unit) = Requ

class Request<DATA> {

var moshi: Moshi = MoshiProvider.providesMoshi()
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()
if (response.isSuccessful) {
val result = response.body()