Skip to content

Commit b470387

Browse files
committed
only register network callback once in reachability monitor
1 parent 5802410 commit b470387

File tree

2 files changed

+60
-31
lines changed

2 files changed

+60
-31
lines changed

aws-datastore/src/main/java/com/amplifyframework/datastore/syncengine/ReachabilityMonitor.kt

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ import android.net.Network
2222
import androidx.annotation.VisibleForTesting
2323
import com.amplifyframework.datastore.DataStoreException
2424
import io.reactivex.rxjava3.core.Observable
25-
import io.reactivex.rxjava3.core.ObservableEmitter
26-
import io.reactivex.rxjava3.core.ObservableOnSubscribe
25+
import io.reactivex.rxjava3.subjects.BehaviorSubject
2726
import java.util.concurrent.TimeUnit
2827

2928
/**
@@ -54,43 +53,29 @@ public interface ReachabilityMonitor {
5453
}
5554

5655
private class ReachabilityMonitorImpl constructor(val schedulerProvider: SchedulerProvider) : ReachabilityMonitor {
57-
private var emitter: ObservableOnSubscribe<Boolean>? = null
58-
56+
private val subject = BehaviorSubject.create<Boolean>()
5957
override fun configure(context: Context) {
6058
return configure(context, DefaultConnectivityProvider())
6159
}
6260

6361
override fun configure(context: Context, connectivityProvider: ConnectivityProvider) {
64-
emitter = ObservableOnSubscribe { emitter ->
65-
val callback = getCallback(emitter)
66-
connectivityProvider.registerDefaultNetworkCallback(context, callback)
67-
// Provide the current network status upon subscription.
68-
emitter.onNext(connectivityProvider.hasActiveNetwork)
69-
}
70-
}
62+
connectivityProvider.registerDefaultNetworkCallback(
63+
context,
64+
object : NetworkCallback() {
65+
override fun onAvailable(network: Network) {
66+
subject.onNext(true)
67+
}
7168

72-
override fun getObservable(): Observable<Boolean> {
73-
emitter?.let { emitter ->
74-
return Observable.create(emitter)
75-
.subscribeOn(schedulerProvider.io())
76-
.debounce(250, TimeUnit.MILLISECONDS, schedulerProvider.computation())
77-
} ?: run {
78-
throw DataStoreException(
79-
"ReachabilityMonitor has not been configured.",
80-
"Call ReachabilityMonitor.configure() before calling ReachabilityMonitor.getObservable()"
81-
)
82-
}
69+
override fun onLost(network: Network) {
70+
subject.onNext(false)
71+
}
72+
}
73+
)
8374
}
8475

85-
private fun getCallback(emitter: ObservableEmitter<Boolean>): NetworkCallback {
86-
return object : NetworkCallback() {
87-
override fun onAvailable(network: Network) {
88-
emitter.onNext(true)
89-
}
90-
override fun onLost(network: Network) {
91-
emitter.onNext(false)
92-
}
93-
}
76+
override fun getObservable(): Observable<Boolean> {
77+
return subject.subscribeOn(schedulerProvider.io())
78+
.debounce(250, TimeUnit.MILLISECONDS, schedulerProvider.computation())
9479
}
9580
}
9681

aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/ReachabilityMonitorTest.kt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import io.reactivex.rxjava3.core.BackpressureStrategy
88
import io.reactivex.rxjava3.schedulers.TestScheduler
99
import io.reactivex.rxjava3.subscribers.TestSubscriber
1010
import java.util.concurrent.TimeUnit
11+
import org.junit.Assert.assertEquals
1112
import org.junit.Test
1213
import org.mockito.Mockito.mock
1314

@@ -69,4 +70,47 @@ class ReachabilityMonitorTest {
6970

7071
testSubscriber.assertValues(true, false, true, true)
7172
}
73+
74+
/**
75+
* Test that calling getObservable() multiple times only results in the network
76+
* callback being registered once.
77+
*/
78+
@Test
79+
fun testNetworkCallbackRegisteredOnce() {
80+
var networkCallback: ConnectivityManager.NetworkCallback? = null
81+
var numCallbacksRegistered = 0
82+
83+
val connectivityProvider = object : ConnectivityProvider {
84+
override val hasActiveNetwork: Boolean
85+
get() = run {
86+
return true
87+
}
88+
override fun registerDefaultNetworkCallback(
89+
context: Context,
90+
callback: ConnectivityManager.NetworkCallback
91+
) {
92+
networkCallback = callback
93+
numCallbacksRegistered += 1
94+
}
95+
}
96+
97+
// TestScheduler allows the virtual time to be advanced by exact amounts, to allow for repeatable tests
98+
val testScheduler = TestScheduler()
99+
val reachabilityMonitor = ReachabilityMonitor.createForTesting(TestSchedulerProvider(testScheduler))
100+
val mockContext = mock(Context::class.java)
101+
reachabilityMonitor.configure(mockContext, connectivityProvider)
102+
103+
reachabilityMonitor.getObservable().subscribe()
104+
val network = mock(Network::class.java)
105+
// Should provide initial network state (true) upon subscription (after debounce)
106+
testScheduler.advanceTimeBy(251, TimeUnit.MILLISECONDS)
107+
networkCallback!!.onAvailable(network)
108+
109+
reachabilityMonitor.getObservable().subscribe()
110+
testScheduler.advanceTimeBy(251, TimeUnit.MILLISECONDS)
111+
networkCallback!!.onAvailable(network)
112+
113+
// Only 1 network callback should be registered
114+
assertEquals(1, numCallbacksRegistered)
115+
}
72116
}

0 commit comments

Comments
 (0)