Tobi
07/26/2019, 1:58 PMNetworkInfo#isConnectedOrConnecting
and ConnectivityManager.CONNECTIVITY_ACTION
in API 28 I wrote the following class.
https://developer.android.com/reference/android/net/NetworkInfo#isConnectedOrConnecting()
https://developer.android.com/reference/android/net/ConnectivityManager#CONNECTIVITY_ACTION
/**
* Observes network connectivity by consulting the [ConnectivityManager].
* Observing can run infinitely or automatically be stopped after the first response is received.
*/
class ConnectivityObserver @JvmOverloads constructor(
val context: Context,
val onConnectionAvailable: () -> Unit,
val onConnectionLost: () -> Unit = {},
val shouldStopAfterFirstResponse: Boolean = false
) {
private val connectivityManager
get() = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
@Suppress("DEPRECATION")
private val intentFilter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
private val broadCastReceiver = object : BroadcastReceiver() {
@Suppress("DEPRECATION")
override fun onReceive(context: Context?, intent: Intent?) {
if (ConnectivityManager.CONNECTIVITY_ACTION != intent?.action) {
return
}
val networkInfo = connectivityManager.activeNetworkInfo
if (networkInfo != null && networkInfo.isConnectedOrConnecting) {
onConnectionAvailable.invoke()
} else {
onConnectionLost.invoke()
}
if (shouldStopAfterFirstResponse) {
stop()
}
}
}
private lateinit var networkCallback: ConnectivityManager.NetworkCallback
init {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
onConnectionAvailable.invoke()
if (shouldStopAfterFirstResponse) {
stop()
}
}
override fun onLost(network: Network?) {
super.onLost(network)
onConnectionLost.invoke()
if (shouldStopAfterFirstResponse) {
stop()
}
}
}
}
}
fun start() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
// Decouple from component lifecycle, use application context.
// See: <https://developer.android.com/reference/android/content/Context.html#getApplicationContext()>
context.applicationContext.registerReceiver(broadCastReceiver, intentFilter)
} else {
connectivityManager.registerDefaultNetworkCallback(networkCallback)
}
}
fun stop() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
context.applicationContext.unregisterReceiver(broadCastReceiver)
} else {
connectivityManager.unregisterNetworkCallback(networkCallback)
}
}
}
It can be used this way:
val onConnectionAvailable = TODO()
val connectivityObserver = ConnectivityObserver(context, onConnectionAvailable)
connectivityObserver.start()
connectivityObserver.stop()
or this way:
val onConnectionAvailable = TODO()
val onConnectionLost = TODO()
ConnectivityObserver(context,
onConnectionAvailable,
onConnectionLost,
shouldStopAfterFirstResponse = true
).start()
What do you think about my implementation?If you use Activity context to instantiate it, it will behave differently pre and post N when activity is closed - pre N it will stop updating and after N it will keep doing it@Marko Mitic Can you please explain the difference?
Marko Mitic
07/26/2019, 2:07 PMTobi
07/26/2019, 2:10 PMcontext
-> context.applicationContext
WifiManager
requires to use the applicationContext
.
https://developer.android.com/reference/android/content/Context.html#WIFI_SERVICE
I could not find any indicator that this is true for the ConnectivityManager
. Do you know?
Your topic: Where did you read that the BroadcastReceiver
must be registered via applicationContext
for < Android Nougat?Marko Mitic
07/27/2019, 11:10 AMTobi
07/27/2019, 11:12 AMMarko Mitic
07/27/2019, 11:13 AMTobi
07/27/2019, 11:45 AMdave08
08/15/2019, 8:09 AMcallbackFlow
fun Context.fromBroadcast(filter: IntentFilter) = callbackFlow<Intent> {
Log.i(TAG, "Registering receiver for $filter")
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.i(TAG, "Got intent: $intent")
this@callbackFlow.sendBlocking(intent)
}
}
this@fromBroadcast.registerReceiver(receiver, filter)
awaitClose { this@fromBroadcast.unregisterReceiver(receiver) }
}