Pause/Resume sync thread when app goes background/foreground

This commit is contained in:
ganfra 2018-10-17 15:20:26 +02:00
parent d9f1d3fc85
commit e7f9bf86c6
7 changed files with 74 additions and 12 deletions

View File

@ -45,7 +45,6 @@ dependencies {
def support_version = '28.0.0' def support_version = '28.0.0'
def moshi_version = '1.7.0' def moshi_version = '1.7.0'
def lifecycle_version = "1.1.1" def lifecycle_version = "1.1.1"
def work_version = "1.0.0-alpha10"
implementation fileTree(dir: 'libs', include: ['*.aar']) implementation fileTree(dir: 'libs', include: ['*.aar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
@ -65,7 +64,6 @@ dependencies {
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
implementation 'com.squareup.okio:okio:1.15.0' implementation 'com.squareup.okio:okio:1.15.0'
implementation 'com.novoda:merlin:1.1.6' implementation 'com.novoda:merlin:1.1.6'
implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.code.gson:gson:2.8.5'
implementation "com.squareup.moshi:moshi-adapters:$moshi_version" implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
@ -73,10 +71,7 @@ dependencies {
// Paging // Paging
implementation "android.arch.paging:runtime:1.0.1" implementation "android.arch.paging:runtime:1.0.1"
// Worker // FP
implementation "android.arch.work:work-runtime-ktx:$work_version"
implementation 'com.evernote:android-job:1.2.6'
implementation "io.arrow-kt:arrow-core:$arrow_version" implementation "io.arrow-kt:arrow-core:$arrow_version"
// DI // DI

View File

@ -1,12 +1,13 @@
package im.vector.matrix.android.api package im.vector.matrix.android.api
import android.arch.lifecycle.ProcessLifecycleOwner
import android.content.Context import android.content.Context
import com.evernote.android.job.JobManager
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.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
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
import io.realm.Realm import io.realm.Realm
import org.koin.standalone.KoinComponent import org.koin.standalone.KoinComponent
import org.koin.standalone.StandAloneContext.loadKoinModules import org.koin.standalone.StandAloneContext.loadKoinModules
@ -16,16 +17,17 @@ import org.koin.standalone.inject
class Matrix(matrixOptions: MatrixOptions) : KoinComponent { class Matrix(matrixOptions: MatrixOptions) : KoinComponent {
private val authenticator by inject<Authenticator>() private val authenticator by inject<Authenticator>()
private val backgroundDetectionObserver by inject<BackgroundDetectionObserver>()
var currentSession: Session? = null var currentSession: Session? = null
init { init {
Realm.init(matrixOptions.context) Realm.init(matrixOptions.context)
JobManager.create(matrixOptions.context)
val matrixModule = MatrixModule(matrixOptions) val matrixModule = MatrixModule(matrixOptions)
val networkModule = NetworkModule() val networkModule = NetworkModule()
val authModule = AuthModule() val authModule = AuthModule()
loadKoinModules(listOf(matrixModule, networkModule, authModule)) loadKoinModules(listOf(matrixModule, networkModule, authModule))
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
} }
fun authenticator(): Authenticator { fun authenticator(): Authenticator {

View File

@ -2,6 +2,7 @@ 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.api.thread.MainThreadExecutor
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
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
@ -22,5 +23,10 @@ class MatrixModule(private val options: MatrixOptions) : Module {
single { single {
MatrixCoroutineDispatchers(io = Dispatchers.IO, computation = Dispatchers.IO, main = MainThreadExecutor().asCoroutineDispatcher()) MatrixCoroutineDispatchers(io = Dispatchers.IO, computation = Dispatchers.IO, main = MainThreadExecutor().asCoroutineDispatcher())
} }
single {
BackgroundDetectionObserver()
}
}.invoke() }.invoke()
} }

View File

@ -34,7 +34,7 @@ class SyncModule : Module {
} }
scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
SyncThread(get(), get(), get()) SyncThread(get(), get(), get(), get())
} }

View File

@ -7,6 +7,7 @@ import im.vector.matrix.android.internal.events.sync.SyncRequest
import im.vector.matrix.android.internal.events.sync.SyncTokenStore import im.vector.matrix.android.internal.events.sync.SyncTokenStore
import im.vector.matrix.android.internal.events.sync.data.SyncResponse import im.vector.matrix.android.internal.events.sync.data.SyncResponse
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
@ -14,8 +15,9 @@ private const val RETRY_WAIT_TIME_MS = 10_000L
class SyncThread(private val syncRequest: SyncRequest, class SyncThread(private val syncRequest: SyncRequest,
private val networkConnectivityChecker: NetworkConnectivityChecker, private val networkConnectivityChecker: NetworkConnectivityChecker,
private val syncTokenStore: SyncTokenStore private val syncTokenStore: SyncTokenStore,
) : Thread(), NetworkConnectivityChecker.Listener { private val backgroundDetectionObserver: BackgroundDetectionObserver
) : Thread(), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
enum class State { enum class State {
IDLE, IDLE,
@ -63,8 +65,9 @@ class SyncThread(private val syncRequest: SyncRequest,
override fun run() { override fun run() {
Timber.v("Start syncing...") Timber.v("Start syncing...")
state = State.RUNNING
networkConnectivityChecker.register(this) networkConnectivityChecker.register(this)
backgroundDetectionObserver.register(this)
state = State.RUNNING
while (state != State.KILLING) { while (state != State.KILLING) {
if (!networkConnectivityChecker.isConnected() || state == State.PAUSED) { if (!networkConnectivityChecker.isConnected() || state == State.PAUSED) {
Timber.v("Waiting...") Timber.v("Waiting...")
@ -94,6 +97,7 @@ class SyncThread(private val syncRequest: SyncRequest,
} }
Timber.v("Sync killed") Timber.v("Sync killed")
state = State.KILLED state = State.KILLED
backgroundDetectionObserver.unregister(this)
networkConnectivityChecker.unregister(this) networkConnectivityChecker.unregister(this)
} }
@ -103,6 +107,15 @@ class SyncThread(private val syncRequest: SyncRequest,
} }
} }
override fun onMoveToForeground() {
restart()
}
override fun onMoveToBackground() {
pause()
}
} }

View File

@ -0,0 +1,46 @@
package im.vector.matrix.android.internal.util
import android.arch.lifecycle.Lifecycle
import android.arch.lifecycle.LifecycleObserver
import android.arch.lifecycle.OnLifecycleEvent
import timber.log.Timber
/**
* To be attached to ProcessLifecycleOwner lifecycle
*/
class BackgroundDetectionObserver : LifecycleObserver {
var isIsBackground: Boolean = false
private set
private
val listeners = ArrayList<Listener>()
fun register(listener: Listener) {
listeners.add(listener)
}
fun unregister(listener: Listener) {
listeners.remove(listener)
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() {
Timber.d("App returning to foreground…")
isIsBackground = false
listeners.forEach { it.onMoveToForeground() }
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onMoveToBackground() {
Timber.d("App going to background…")
isIsBackground = true
listeners.forEach { it.onMoveToBackground() }
}
interface Listener {
fun onMoveToForeground()
fun onMoveToBackground()
}
}