Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offload connectivity monitor to a background thread #1368

Merged
merged 2 commits into from May 7, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -26,57 +26,71 @@ import android.net.NetworkRequest.Builder
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import androidx.core.content.getSystemService
import androidx.tracing.Trace
import androidx.tracing.trace
import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOn
import javax.inject.Inject

internal class ConnectivityManagerNetworkMonitor @Inject constructor(
@ApplicationContext private val context: Context,
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
) : NetworkMonitor {
override val isOnline: Flow<Boolean> = callbackFlow {
val connectivityManager = context.getSystemService<ConnectivityManager>()
if (connectivityManager == null) {
channel.trySend(false)
channel.close()
return@callbackFlow
}
trace("NetworkMonitor.callbackFlow") {
val connectivityManager = context.getSystemService<ConnectivityManager>()
if (connectivityManager == null) {
channel.trySend(false)
channel.close()
return@callbackFlow
}

/**
* The callback's methods are invoked on changes to *any* network matching the [NetworkRequest],
* not just the active network. So we can simply track the presence (or absence) of such [Network].
*/
val callback = object : NetworkCallback() {

/**
* The callback's methods are invoked on changes to *any* network matching the [NetworkRequest],
* not just the active network. So we can simply track the presence (or absence) of such [Network].
*/
val callback = object : NetworkCallback() {
private val networks = mutableSetOf<Network>()

private val networks = mutableSetOf<Network>()
override fun onAvailable(network: Network) {
networks += network
channel.trySend(true)
}

override fun onAvailable(network: Network) {
networks += network
channel.trySend(true)
override fun onLost(network: Network) {
mlykotom marked this conversation as resolved.
Show resolved Hide resolved
networks -= network
channel.trySend(networks.isNotEmpty())
}
}

override fun onLost(network: Network) {
networks -= network
channel.trySend(networks.isNotEmpty())
trace("NetworkMonitor.registerNetworkCallback") {
val request = Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build()
connectivityManager.registerNetworkCallback(request, callback)
}
}

val request = Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build()
connectivityManager.registerNetworkCallback(request, callback)
/**
* Sends the latest connectivity status to the underlying channel.
*/
channel.trySend(connectivityManager.isCurrentlyConnected())

/**
* Sends the latest connectivity status to the underlying channel.
*/
channel.trySend(connectivityManager.isCurrentlyConnected())
Trace.endSection()
mlykotom marked this conversation as resolved.
Show resolved Hide resolved

awaitClose {
connectivityManager.unregisterNetworkCallback(callback)
awaitClose {
connectivityManager.unregisterNetworkCallback(callback)
}
}
}
.flowOn(ioDispatcher)
.conflate()

@Suppress("DEPRECATION")
Expand Down