ConnectivityManager.CONNECTIVITY_ACTION obsoleto

94

En Android N, se menciona en el sitio web oficial que "las aplicaciones destinadas a Android N no reciben transmisiones de CONNECTIVITY_ACTION". Y también se menciona que JobSchedulerse puede utilizar como alternativa. Pero JobSchedulerno proporciona exactamente el mismo comportamiento que la CONNECTIVITY_ACTIONtransmisión.

En mi aplicación de Android, estaba usando esta transmisión para conocer el estado de la red del dispositivo. Quería saber si este estado era CONNECTINGo CONNECTEDcon la ayuda de la CONNECTIVITY_ACTIONtransmisión y era el más adecuado para mis necesidades.

Ahora que está en desuso, ¿alguien puede sugerirme el enfoque alternativo para obtener el estado actual de la red?

Raghuram db
fuente
10
¿Y si algún día el OP quiere algún comportamiento que requiera subir el targetSdkVersiona N o más tarde?
Michael
1
Bueno, yo también sé que si no dirijo mi aplicación a Android, NI recibirá la transmisión. Pero mi aplicación debe ser compatible con Android N. ¿Cómo puedo obtener el mismo comportamiento de transmisión en Android N? ¿Hay algún otro enfoque que pueda probar? @DavidWasser
Raghuram db
A veces pienso que tiene más sentido preocuparse por el futuro en el futuro. Este es un enfoque puramente pragmático de la programación. Por supuesto, siempre puede intentar asegurarse de que su código no utilice funciones obsoletas. Por otro lado, las funciones obsoletas suelen permanecer durante mucho tiempo y es posible que su aplicación finalice su ciclo de vida antes de que desaparezcan las funciones obsoletas. Android N es tan nuevo que no pasaría mucho tiempo preocupándome por él. Todavía. Solo mis 2 centavos. Tenga en cuenta que escribí un comentario a la pregunta y no sugerí que "no hagas eso" fuera una respuesta válida.
David Wasser
2
@Raghuramdb Su aplicación se puede ejecutar en Android N incluso si no la orienta a Android N. Solo tiene que orientar su aplicación a Android N si desea utilizar funciones que solo están disponibles en Android N.
David Wasser
2
Aún puede usar el BroadcastReceivercon el android.net.conn.CONNECTIVITY_CHANGEfiltro de intención incluso cuando apunte a API29, solo necesita registrarlo Application.OnCreate. Simplemente no recibirá ninguna actualización cuando la aplicación esté cerrada.
Pierre

Respuestas:

97

Lo que quedará obsoleto es la capacidad de una aplicación en segundo plano para recibir cambios de estado de conexión de red.

Como dijo David Wasser , aún puede recibir notificaciones de los cambios de conectividad si se crea una instancia del componente de la aplicación (no se destruye) y ha registrado su receptor mediante programación con su contexto, en lugar de hacerlo en el manifiesto.

O puede utilizar NetworkCallback en su lugar. En particular, deberá anular onDisponible para cambios de estado conectado.

Déjame redactar un fragmento rápidamente:

public class ConnectionStateMonitor extends NetworkCallback {

   final NetworkRequest networkRequest;

   public ConnectionStateMonitor() {
       networkRequest = new NetworkRequest.Builder()
           .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
           .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
           .build();
   }

   public void enable(Context context) {
       ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
       connectivityManager.registerNetworkCallback(networkRequest, this);
   }

   // Likewise, you can have a disable method that simply calls ConnectivityManager.unregisterNetworkCallback(NetworkCallback) too.

   @Override
   public void onAvailable(Network network) {
       // Do what you need to do here
   }
}
Amokrane Chentir
fuente
2
Dado que esta técnica solo funcionará si la aplicación se ejecuta en primer plano. ¿Eso significa que ya no podemos escuchar el evento de conexión cuando la aplicación no se está ejecutando en primer plano? Tener <action android: name = "android.net.conn.CONNECTIVITY_CHANGE" /> en manifest.xml ya no tiene ningún efecto en Android N.
Cheok Yan Cheng
2
@CheokYanCheng AFAIK eso es correcto. Debe tener un proceso que se ejecute en primer plano para escuchar eventos de conectividad. Parece que la suposición que hicieron los ingenieros del marco de Android fue que escuchar los eventos de conectividad se hacía principalmente para saber cuándo comenzar a sincronizar los datos entre el cliente y el servidor. Por lo tanto, JobScheduler es la forma recomendada para ese caso de uso.
Amokrane Chentir
25
jajaja, qué demonios, otras 10 actualizaciones de Android y todo lo que podremos escribir es una aplicación de hola mundo
DennisVA
1
¿Necesito anular el registro de NetworkCallback (por ejemplo, en el método onDestroy de la actividad)?
Ruslan Berozov
2
@Ruslan sí, por supuesto, o filtrará lo que esté registrado
DennisVA
34

Actualizaré la Sayem'srespuesta para solucionar los problemas de pelusa que se me muestran.

class ConnectionLiveData(val context: Context) : LiveData<Boolean>() {

    private var connectivityManager: ConnectivityManager = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager

    private lateinit var connectivityManagerCallback: ConnectivityManager.NetworkCallback

    private val networkRequestBuilder: NetworkRequest.Builder = NetworkRequest.Builder()
        .addTransportType(android.net.NetworkCapabilities.TRANSPORT_CELLULAR)
        .addTransportType(android.net.NetworkCapabilities.TRANSPORT_WIFI)

    override fun onActive() {
        super.onActive()
        updateConnection()
        when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> connectivityManager.registerDefaultNetworkCallback(getConnectivityMarshmallowManagerCallback())
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> marshmallowNetworkAvailableRequest()
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> lollipopNetworkAvailableRequest()
            else -> {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                    context.registerReceiver(networkReceiver, IntentFilter("android.net.conn.CONNECTIVITY_CHANGE")) // android.net.ConnectivityManager.CONNECTIVITY_ACTION
                }
            }
        }
    }

    override fun onInactive() {
        super.onInactive()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            connectivityManager.unregisterNetworkCallback(connectivityManagerCallback)
        } else {
            context.unregisterReceiver(networkReceiver)
        }
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private fun lollipopNetworkAvailableRequest() {
        connectivityManager.registerNetworkCallback(networkRequestBuilder.build(), getConnectivityLollipopManagerCallback())
    }

    @TargetApi(Build.VERSION_CODES.M)
    private fun marshmallowNetworkAvailableRequest() {
    connectivityManager.registerNetworkCallback(networkRequestBuilder.build(), getConnectivityMarshmallowManagerCallback())
    }

    private fun getConnectivityLollipopManagerCallback(): ConnectivityManager.NetworkCallback {
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
           connectivityManagerCallback = object : ConnectivityManager.NetworkCallback() {
               override fun onAvailable(network: Network?) {
                   postValue(true)
               }

               override fun onLost(network: Network?) {
                   postValue(false)
               }
           }
           return connectivityManagerCallback
       } else {
           throw IllegalAccessError("Accessing wrong API version")
       }
    }

    private fun getConnectivityMarshmallowManagerCallback(): ConnectivityManager.NetworkCallback {
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
          connectivityManagerCallback = object : ConnectivityManager.NetworkCallback() {
            override fun onCapabilitiesChanged(network: Network?, networkCapabilities: NetworkCapabilities?) {
                networkCapabilities?.let { capabilities ->
                    if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
                        postValue(true)
                    }
                }
            }
            override fun onLost(network: Network?) {
                postValue(false)
            }
        }
        return connectivityManagerCallback
    } else {
        throw IllegalAccessError("Accessing wrong API version")
    }

    private val networkReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            updateConnection()
        }
    }

    private fun updateConnection() {
        val activeNetwork: NetworkInfo? = connectivityManager.activeNetworkInfo
        postValue(activeNetwork?.isConnected == true)
    }
}

Y mismo uso:

    val connectionLiveData = ConnectionLiveData(context)
        connectionLiveData.observe(this, Observer { isConnected ->
           isConnected?.let {
             // do job
           }
    })

Por cierto, gracias por tu solución.

Kebab Krabby
fuente
2
¡Solución asombrosa!
casilla
2
Muy buena solución para usar datos en vivo y soportar versiones anteriores
Prakash Shukla
Esta es la mejor solución disponible en Internet.
Karan Sharma
Muy buena solucion! PERO hay un "no": es una forma incorrecta de utilizar el método onAvailable (red: ¿Red?), Porque invoca incluso que Internet no está disponible. Es mejor usar onCapabilitiesChanged (network: Network, networkCapabilities: NetworkCapabilities) y verificar networkCapabilities.hasCapability (NET_CAPABILITY_INTERNET) y networkCapabilities.hasCapability (NET_CAPABILITY_VALIDATED).
DmitryKanunnikoff
¿Cómo obtener la IP y el tipo de red dentro de esta base de código?
A_rmas
28

La documentación para Android N dice:

Las aplicaciones destinadas a Android N no reciben transmisiones de CONNECTIVITY_ACTION, incluso si tienen entradas de manifiesto para solicitar la notificación de estos eventos. Las aplicaciones que se ejecutan en primer plano aún pueden escuchar CONNECTIVITY_CHANGE en su hilo principal si solicitan una notificación con un BroadcastReceiver.

Esto significa que aún puede registrar un BroadcastReceiversi su aplicación se está ejecutando en primer plano, para detectar cambios en la conectividad de la red.

David Wasser
fuente
Bonita captura sutil :)
Amokrane Chentir
¿Significa esto que la aplicación dejará de recibir transmisiones una vez que no esté en primer plano? (¿
Entonces
1
No lo sé con seguridad, tendría que probarlo para estar seguro. Sin embargo, al leer la documentación, parecería que si su aplicación no está en primer plano, no obtendrá la transmisión Intent.
David Wasser
2
Pero para detectar cambios de conectividad en segundo plano es obligatorio para cualquier aplicación sip (VoIP) ... esas aplicaciones normalmente se ejecutan en segundo plano durante días y saltan al primer plano solo si entra una llamada (como el marcador del teléfono). Esas aplicaciones deben volver a conectarse automáticamente en segundo plano. Esto mata a todas esas aplicaciones (que no tienen su propio servidor push) de la plataforma Android, ya que estarán fuera de línea. siempre.
Grisgram
solo usa el servicio push de firebase.
Pierre
21

Verifique la primera respuesta de @Amokrane Chentir para obtener soporte para Android N.

Para aquellos que quieran brindar soporte en todos los niveles de API y observarlo en la interfaz de usuario, verifique el código a continuación.

LiveData de NetworkConnection:

class ConnectionLiveData(val context: Context) : LiveData<Boolean>(){

    var  intentFilter = IntentFilter(CONNECTIVITY_ACTION)
    private var  connectivityManager = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
    private lateinit var networkCallback : NetworkCallback

    init {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            networkCallback = NetworkCallback(this)
        }
    }

    override fun onActive() {
        super.onActive()
        updateConnection()
        when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> connectivityManager.registerDefaultNetworkCallback(networkCallback)
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> {
                val builder = NetworkRequest.Builder().addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI)
                connectivityManager.registerNetworkCallback(builder.build(), networkCallback)
            }
            else -> {
                context.registerReceiver(networkReceiver, intentFilter)
            }
        }
    }

    override fun onInactive() {
        super.onInactive()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            connectivityManager.unregisterNetworkCallback(networkCallback)
        } else{
            context.unregisterReceiver(networkReceiver)
        }
    }


    private val networkReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            updateConnection()
        }
    }

    fun updateConnection() {
        val activeNetwork: NetworkInfo? = connectivityManager.activeNetworkInfo
        postValue(activeNetwork?.isConnectedOrConnecting == true)
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    class NetworkCallback(val liveData : ConnectionLiveData) : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network?) {
            liveData.postValue(true)
        }

        override fun onLost(network: Network?) {
            liveData.postValue(false)
        }
    }
}

observar en UI (Actividad / Fragmento):

val connectionLiveData = ConnectionLiveData(context)
    connectionLiveData.observe(this, Observer { 
       // do whatever you want with network connectivity change 
})
Sayem
fuente
por cierto, no es necesario definir IntentFilterexplícitamente. Así:var intentFilter = IntentFilter(CONNECTIVITY_ACTION)
Ryan Amaral
gracias por tu sugerencia. No quería crear un objeto cada vez en onActive.
Sayem
Quiero decir que las 2 variables / propiedades globales ( intentFiltery connectivityManager) no es necesario definir explícitamente su tipo ( IntentFiltery ConnectivityManagerrespectivamente).
Ryan Amaral
7

Me encontré con el mismo problema hace unos días y decidí usar esta biblioteca Android-Job

Esta biblioteca usa JobSchedular, GcmNetworkManagery BroadcastReceiverdependiendo de la versión de Android en la que se esté ejecutando la aplicación.

Comenzar un trabajo es bastante fácil

new JobRequest.Builder(DemoSyncJob.TAG)
            .setRequiresCharging(true)
            .setRequiresDeviceIdle(false)
            .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) // this is what gets the job done
            .build()
            .schedule();
Noman Rafique
fuente
1
Probé el mismo programador y obtuve una excepción como esta. Está tratando de crear un trabajo sin restricciones, esto no está permitido. ¿Puedes ayudarnos a resolver esto?
Sanket Kachhela
Usar Android-Job para este propósito no es realmente una muy buena solución. Está destinado a ejecutar cosas en un momento específico, ya sea una vez o periódicamente. Está destinado a brindar soporte de retrocompatibilidad para alarmas y demás. Esto va en contra de la idea de por qué cambió la API, y leyendo: developer.android.com/training/monitoring-device-state/… Puede tener una idea rápida del por qué.
pedronveloso
El único problema es que en Android N solo se puede programar para un mínimo de 15 minutos en el futuro
Fire Crow
4

Escribí una implementación de Kotlin que se basa en la respuesta de Sayam pero sin ella LiveData. Decidí invocar (en este momento) el último método de API ( ConnectivityManager#registerDefaultNetworkCallback) que apunta a Android Nougat.

/**
 * 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)
        }
    }

}

Uso:

val onConnectionAvailable = TODO()
val connectivityObserver = ConnectivityObserver(context, onConnectionAvailable)
connectivityObserver.start()
connectivityObserver.stop()

o:

val onConnectionAvailable = TODO()
val onConnectionLost = TODO()
ConnectivityObserver(context, 
    onConnectionAvailable, 
    onConnectionLost, 
    shouldStopAfterFirstResponse = true
).start()

No olvide agregar el ACCESS_NETWORK_STATEpermiso en su AndroidManifest.xml :

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Espero leer comentarios útiles y mejoras de su parte.

JJD
fuente
1
Tuve que cambiar algo para que las devoluciones de llamada pudieran "tocar vistas" en una Actividad (contexto) en el hilo principal: en (context as AppCompatActivity).runOnUiThread(object: Runnable{ override fun run() { onConnectionAvailable.invoke() } })lugar de onConnectionAvailable.invoke(). Lo mismo para onConnectionLost.invoke().
Андрей Воробьев
Sí, dependiendo de su caso de uso, es posible que deba cambiar de hilo. No lo haría parte de la clase, sino que dejaría que el consumidor de la clase se encargue de esto. Pero gracias por la pista.
JJD
2

Las aplicaciones destinadas a Android N (Nougat) no reciben CONNECTIVITY_ACTIONtransmisiones definidas en el manifiesto (consulte Svelte ).

Soluciones posibles:

Consulte también Android O: detectar cambios de conectividad en segundo plano

rds
fuente
1

Estoy de acuerdo con la respuesta sugerida por @rds.

Tenga en cuenta que CONNECTIVITY_ACTION está obsoleto en el nivel de API 28.

Si tiene el requisito de que se detecte el estado de Wifi (conectar / desconectar) a pesar de que se haya eliminado la aplicación y desea apuntar a la última versión, entonces no tiene muchas opciones.

Necesitas usar connectivityManager.registerNetworkCallback(networkRequest, networkCallback)

La pregunta es que no puede usar BroadcastReceiver, entonces, ¿cómo?

Puede usar JobScheduler o mejor si WorkManager (solicitud periódica). Why Periodic porque si es una OneTimeRequest, solo podrá ejecutarse una vez y seguir escuchando mientras su aplicación está en primer plano.

La documentación dice:

Se seguirán llamando a las devoluciones de llamada hasta que se cierre la aplicación o se llame al enlace #unregisterNetworkCallback (NetworkCallback)}.

Una vez que la aplicación se elimina o se elimina de la lista de aplicaciones recientes, networkCallback no podrá escuchar.

Por lo tanto, necesita trabajos periódicos para que la aplicación escuche continuamente. ¿Cuánto debe ser la duración? Eso depende de usted y depende de cada caso.

Sé que es un poco feo, pero así es. Un desafío podría ser que si el dispositivo del usuario está en modo Doze o la aplicación está en estado de espera, su trabajo podría retrasarse.

Wahib Ul Haq
fuente
También tenga en cuenta que en algunos EMUI altamente personalizados, el WorkManager (tareas periódicas) del sistema operativo Android MIUI no tiene que funcionar siempre correctamente.
Kebab Krabby
1

Cuando registramos una devolución de llamada de red usando el registerNetworkCallbackmétodo, a veces no se activa y, a veces, sí activa un falso positivo:

  1. Si iniciamos una aplicación con conexión a Internet, el onAvailablemétodo se activa.
  2. Pero si no hay conexión a Internet en el dispositivo cuando iniciamos una aplicación, no NetworkCallbackse llama nada (es muy extraño debido a la p. 1)
  3. Si tenemos conexión wifi pero sin conexión a internet onAvailablese activa el método. Y creo que es un comportamiento falso positivo porque esperamos que la conexión a Internet se observe.

Como puede ver en el siguiente código, la conexión a Internet predeterminada está disponible y se activa solo si cambia. Sin desencadenantes falsos positivos.

Simplemente resuma esto y esta respuesta (pero solo para API> = 21):

class ConnectionManager @Inject constructor(
    private val connectivityManager: ConnectivityManager,
    private val disposable: CompositeDisposable,
    private val singleTransformer: SingleTransformer<*, *>
) : LiveData<Boolean>() {

    private var isNetworkAvailable = true

    private val builder = NetworkRequest.Builder()
        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
        .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)

    private val callback = object : ConnectivityManager.NetworkCallback() {

        override fun onAvailable(network: Network) {
            ping()
        }

        override fun onLost(network: Network) {
            ping()
        }
    }

    private fun ping() {
        disposable.add(
            Single.fromCallable {
                try {
                    val timeoutMs = 1500
                    val socket = Socket()
                    val socketAddress = InetSocketAddress("8.8.8.8", 53)

                    socket.connect(socketAddress, timeoutMs)
                    socket.close()
                    true
                } catch (e: IOException) {
                    false
                }
            }
                .compose(singleTransformer as SingleTransformer<Boolean, Boolean>)
                .subscribeBy {
                    if (isNetworkAvailable != it){
                        value = it
                        isNetworkAvailable = it
                    }
                }
        )
    }

    override fun onActive() {
        ping()
        connectivityManager.registerNetworkCallback(builder.build(), callback)
    }

    override fun onInactive() {
        disposable.clear()
        connectivityManager.unregisterNetworkCallback(callback)
    }
}

Cómo proporcionar las dependencias

@Provides
fun provideTransformer(): SingleTransformer<Boolean, Boolean> {
    return SingleTransformer<Boolean, Boolean> { upstream: Single<Boolean> ->
        upstream.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
    }
}

@Singleton
@Provides
fun provideConnectivityManager(context: Context): ConnectivityManager =
        context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

@Singleton
@Provides
fun provideConnectionManager(connectivityManager: ConnectivityManager, singleTransformer: SingleTransformer<Boolean, Boolean>): ConnectionManager =
        ConnectionManager(connectivityManager, singleTransformer)

Y como usar:

@Inject
lateinit var connectionManager: ConnectionManager

//....

viewLifecycleOwner.observe(connectionManager) { isInternetAvailable ->
    // TODO 
}
bitvale
fuente
1

Basado en la respuesta de @ KebabKrabby:

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Context.CONNECTIVITY_SERVICE
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.net.ConnectivityManager.CONNECTIVITY_ACTION
import android.net.ConnectivityManager.EXTRA_NO_CONNECTIVITY
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.os.Build
import androidx.lifecycle.LiveData

class ConnectivityWatcher(
    private val context: Context
): LiveData<Boolean>() {

    private lateinit var networkCallback: ConnectivityManager.NetworkCallback
    private lateinit var broadcastReceiver: BroadcastReceiver

    override fun onActive() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            val cm = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
            networkCallback = createNetworkCallback()
            cm.registerDefaultNetworkCallback(networkCallback)
        } else {
            val intentFilter = IntentFilter(CONNECTIVITY_ACTION)
            broadcastReceiver = createBroadcastReceiver()
            context.registerReceiver(broadcastReceiver, intentFilter)
        }
    }

    override fun onInactive() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            val cm = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
            cm.unregisterNetworkCallback(networkCallback)
        } else {
            context.unregisterReceiver(broadcastReceiver)
        }
    }

    private fun createNetworkCallback() = object : ConnectivityManager.NetworkCallback() {

        override fun onCapabilitiesChanged(
            network: Network,
            networkCapabilities: NetworkCapabilities
        ) {
            val isInternet = networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)
            val isValidated = networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)
            postValue(isInternet && isValidated)
        }

        override fun onLost(network: Network) {
            postValue(false)
        }
    }

    private fun createBroadcastReceiver() = object : BroadcastReceiver() {

        override fun onReceive(context: Context?, intent: Intent?) {
            val isNoConnectivity = intent?.extras?.getBoolean(EXTRA_NO_CONNECTIVITY) ?: true
            postValue(!isNoConnectivity)
        }
    }
}

Y usarlo casi de la misma manera que en la respuesta original (si observa desde una Actividad, por ejemplo):

ConnectivityWatcher(this).observe(this, Observer {
    Log.i("*-*-*", "is internet available? - ${if (it) "Yes" else "No"}")
})
DmitryKanunnikoff
fuente