ConnectivityManager.CONNECTIVITY_ACTION obsolète

94

Dans Android N, il est mentionné sur le site officiel que "les applications ciblant Android N ne reçoivent pas les diffusions CONNECTIVITY_ACTION". Et il est également mentionné qu'il JobSchedulerpeut être utilisé comme alternative. Mais le JobSchedulerne fournit pas exactement le même comportement que la CONNECTIVITY_ACTIONdiffusion.

Dans mon application Android, j'utilisais cette diffusion pour connaître l'état du réseau de l'appareil. Je voulais savoir si cet état était CONNECTINGou CONNECTEDavec l'aide de la CONNECTIVITY_ACTIONdiffusion et il était le mieux adapté à mon besoin.

Maintenant qu'il est obsolète, quelqu'un peut-il me suggérer une approche alternative pour obtenir l'état actuel du réseau?

Raghuram db
la source
10
Et si l'OP souhaite un jour un comportement qui nécessite de passer targetSdkVersionà N ou plus tard?
Michael
1
Eh bien, je sais aussi que si je ne cible pas mon application sur Android, NI recevra la diffusion. Mais mon application doit prendre en charge Android N. Comment puis-je obtenir le même comportement de diffusion sous Android N? Y a-t-il une autre approche que je peux essayer? @DavidWasser
Raghuram db
Parfois, je pense qu'il est plus logique de s'inquiéter de l'avenir à l'avenir. Il s'agit d'une approche purement pragmatique de la programmation. Bien sûr, vous pouvez toujours essayer de vous assurer que votre code n'utilise aucune fonctionnalité obsolète. D'un autre côté, les fonctionnalités obsolètes restent généralement longtemps et il se peut que votre application soit en fin de vie avant que les fonctionnalités obsolètes disparaissent. Android N est si nouveau que je ne passerais pas beaucoup de temps à m'inquiéter à ce sujet. Encore. Juste mes 2 cents. Veuillez noter que j'ai écrit un commentaire à la question et que je n'ai pas suggéré que «ne faites pas cela» était une réponse valide.
David Wasser
2
@Raghuramdb Votre application peut fonctionner sur Android N même si vous ne ciblez pas votre application sur Android N.Vous devez uniquement cibler Android N si vous souhaitez utiliser des fonctionnalités qui ne sont disponibles que dans Android N.
David Wasser
2
Vous pouvez toujours utiliser le BroadcastReceiveravec le android.net.conn.CONNECTIVITY_CHANGEfiltre d'intention même lorsque vous ciblez l'API29, il vous suffit de l'enregistrer Application.OnCreate. Vous n'obtiendrez tout simplement aucune mise à jour lorsque l'application sera fermée.
Pierre

Réponses:

97

Ce qui sera obsolète, c'est la possibilité pour une application en arrière-plan de recevoir des changements d'état de connexion réseau.

Comme l'a dit David Wasser , vous pouvez toujours être informé des changements de connectivité si le composant d'application est instancié (non détruit) et que vous avez enregistré votre récepteur par programme avec son contexte, au lieu de le faire dans le manifeste.

Ou vous pouvez utiliser NetworkCallback à la place. En particulier, vous devrez remplacer onAvailable pour les changements d'état connecté.

Permettez-moi de rédiger rapidement un extrait:

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
la source
2
Étant donné que cette technique ne pourra fonctionner que si l'application s'exécute au premier plan. Cela signifie-t-il que nous n'avons plus la possibilité d'écouter l'événement de connexion, lorsque l'application ne s'exécute pas au premier plan? Avoir <action android: name = "android.net.conn.CONNECTIVITY_CHANGE" /> dans manifest.xml n'a plus d'effet dans Android N.
Cheok Yan Cheng
2
@CheokYanCheng AFAIK c'est correct. Vous devez disposer d'un processus qui s'exécute au premier plan pour écouter les événements de connectivité. Il semble que l'hypothèse émise par les ingénieurs du framework Android était que l'écoute des événements de connectivité était principalement effectuée pour savoir quand commencer la synchronisation des données entre le client et le serveur. Par conséquent, JobScheduler est la méthode recommandée pour ce cas d'utilisation.
Amokrane Chentir
25
lol que diable, 10 autres mises à jour Android et tout ce que nous pourrons écrire est une application Hello World
DennisVA
1
Dois-je désinscrire NetworkCallback (par exemple, dans la méthode onDestroy de l'activité)?
Ruslan Berozov
2
@Ruslan oui bien sûr, ou vous
fuirez
34

Je mettrai à jour la Sayem'sréponse pour résoudre les problèmes de charpie qu'il me montre.

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

Et même usage:

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

Btw merci sayem pour votre solution.

Kebab Krabby
la source
2
Solution incroyable!
boîte du
2
Très bonne solution pour utiliser des données en direct et prendre en charge une version plus ancienne
Prakash Shukla
C'est la meilleure solution disponible sur Internet.
Karan Sharma le
Très bonne solution! MAIS il y a un "non" - c'est une mauvaise façon d'utiliser la méthode onAvailable (réseau: Réseau?), Car elle invoque même Internet n'est pas disponible. Il est préférable d'utiliser onCapabilitiesChanged (network: Network, networkCapabilities: NetworkCapabilities) et de vérifier networkCapabilities.hasCapability (NET_CAPABILITY_INTERNET) et networkCapabilities.hasCapability (NET_CAPABILITY_VALIDATED).
DmitryKanunnikoff
comment obtenir l'ip et le type de réseau dans cette base de code?
A_rmas
28

La documentation pour Android N indique:

Les applications ciblant Android N ne reçoivent pas les diffusions CONNECTIVITY_ACTION, même si elles ont des entrées manifestes pour demander une notification de ces événements. Les applications exécutées au premier plan peuvent toujours écouter CONNECTIVITY_CHANGE sur leur thread principal si elles demandent une notification avec un BroadcastReceiver.

Cela signifie que vous pouvez toujours enregistrer un BroadcastReceiversi votre application s'exécute au premier plan, afin de détecter les changements dans la connectivité réseau.

David Wasser
la source
Nice subtle catch :)
Amokrane Chentir
cela signifie-t-il que l'application cessera de recevoir des diffusions une fois qu'elle n'est pas au premier plan? (donc je ne peux pas l'écouter dans un service par exemple?)
dimanche
1
Je ne sais pas avec certitude, j'aurais besoin de le tester pour être sûr. Cependant, la lecture de la documentation semblerait que si votre application n'est pas au premier plan, vous n'obtiendrez pas la diffusion Intent.
David Wasser
2
Mais pour détecter un changement de connectivité en arrière-plan est obligatoire pour toutes les applications sip (VoIP) ... ces applications fonctionnent normalement en arrière-plan pendant des jours et passent au premier plan uniquement si un appel arrive (tout comme votre numéroteur du téléphone). . Ces applications doivent se reconnecter automatiquement en arrière-plan. Cela tue toutes ces applications (qui n'ont pas leur propre serveur push) de la plate-forme Android car elles seront hors ligne. toujours.
Grisgram
utilisez simplement le service push firebase.
Pierre
21

Veuillez d'abord vérifier la réponse @Amokrane Chentir pour la prise en charge d'Android N.

Pour ceux qui souhaitent prendre en charge tous les niveaux d'API et l'observer dans l'interface utilisateur, veuillez vérifier le code ci-dessous.

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

observer dans l'interface utilisateur (activité / fragment):

val connectionLiveData = ConnectionLiveData(context)
    connectionLiveData.observe(this, Observer { 
       // do whatever you want with network connectivity change 
})
Sayem
la source
btw, vous n'avez pas besoin de définir IntentFilterexplicitement. Comme ça:var intentFilter = IntentFilter(CONNECTIVITY_ACTION)
Ryan Amaral
merci pour votre suggestion. Je ne voulais pas créer d'objet à chaque fois dans onActive.
Sayem le
Je veux dire que les 2 variables / propriétés globales ( intentFilteret connectivityManager) vous n'avez pas besoin de définir explicitement leur type ( IntentFilteret ConnectivityManagerrespectivement).
Ryan Amaral
7

J'ai rencontré le même problème il y a quelques jours et j'ai décidé d'utiliser cette bibliothèque Android-Job

Cette utilisation bibliothèque JobSchedular, GcmNetworkManageret BroadcastReceiverselon la version Android de l'application est en cours d' exécution sur.

Commencer un travail est assez facile

new JobRequest.Builder(DemoSyncJob.TAG)
            .setRequiresCharging(true)
            .setRequiresDeviceIdle(false)
            .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) // this is what gets the job done
            .build()
            .schedule();
Noman Rafique
la source
1
J'ai essayé le même ordonnanceur et j'ai obtenu une exception comme celle-ci. Vous essayez de créer un travail sans contraintes, ce n'est pas autorisé. pouvez-vous nous aider à résoudre ce problème?
Sanket Kachhela
Utiliser Android-Job à cette fin n'est vraiment pas une très bonne solution. Il est destiné à exécuter les choses à une heure spécifiée, une fois ou périodiquement. Il est destiné à apporter un support de rétro-compatibilité pour les alarmes et autres. Cela va à l'encontre de l'idée même de savoir pourquoi l'API a changé, et en lisant: developer.android.com/training/monitoring-device-state/ ... Vous pouvez rapidement comprendre pourquoi.
pedronveloso le
Le seul problème est que dans Android N, il ne peut être programmé que pour un minimum de 15 minutes à l'avenir
Fire Crow
4

J'ai écrit une implémentation de Kotlin qui est basée sur la réponse de Sayam mais sans LiveData. J'ai décidé d'invoquer la dernière méthode API (à ce stade ConnectivityManager#registerDefaultNetworkCallback) qui cible 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)
        }
    }

}

Usage:

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

ou:

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

N'oubliez pas d'ajouter l' ACCESS_NETWORK_STATEautorisation dans votre AndroidManifest.xml :

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

J'ai hâte de lire vos commentaires et améliorations utiles.

JJD
la source
1
J'ai dû changer quelque chose pour que les rappels puissent "toucher les vues" dans une activité (contexte) sur le fil principal: (context as AppCompatActivity).runOnUiThread(object: Runnable{ override fun run() { onConnectionAvailable.invoke() } })au lieu de onConnectionAvailable.invoke(). La même chose pour onConnectionLost.invoke().
Андрей Воробьев
Oui, selon votre cas d'utilisation, vous devrez peut-être changer de thread. Je ne ferais pas partie de la classe mais laisserais plutôt le consommateur de la classe s'en occuper. Mais merci pour l'indice.
JJD
1

Je suis d'accord avec la réponse suggérée par @rds.

Gardez à l'esprit que CONNECTIVITY_ACTION est obsolète au niveau de l'API 28.

Si vous avez l'exigence que l'état du Wifi (connexion / déconnexion) soit détecté malgré la suppression de l'application et que vous souhaitez cibler la dernière version, vous n'avez pas beaucoup de choix.

Vous devez utiliser connectivityManager.registerNetworkCallback(networkRequest, networkCallback)

La question est que vous ne pouvez pas utiliser BroadcastReceiver alors comment?

Vous pouvez utiliser JobScheduler ou mieux si WorkManager (demande périodique). Pourquoi Periodic car s'il s'agit d'un OneTimeRequest, il ne pourra s'exécuter qu'une seule fois et continuer à écouter pendant que votre application est au premier plan.

La documentation dit:

Les rappels continueront à être appelés jusqu'à ce que l'application se termine ou que le lien #unregisterNetworkCallback (NetworkCallback)} soit appelé.

Une fois que l'application est supprimée ou supprimée de la liste des applications récentes, networkCallback ne pourra plus l'écouter.

Donc, vous avez besoin de tels emplois périodiques pour que l'application écoute en permanence. Quelle devrait être la durée? Cela dépend de vous et dépend du cas par cas.

Je sais que c'est un peu moche mais c'est comme ça. Un défi pourrait être que si l'appareil de l'utilisateur est en mode Veille ou que l'application est en état de veille, votre travail peut être retardé.

Wahib Ul Haq
la source
Gardez également à l'esprit que sur certains EMUI fortement personnalisés, MIUI Android OS workManager (tâches périodiques) ne doit pas toujours fonctionner correctement.
Kebab Krabby
1

Lorsque nous enregistrons un rappel réseau à l'aide de la registerNetworkCallbackméthode, il ne se déclenche parfois pas et parfois il déclenche des faux positifs:

  1. Si nous démarrons une application avec une connexion Internet, la onAvailableméthode se déclenche.
  2. Mais s'il n'y a pas de connexion Internet sur l'appareil lorsque nous démarrons une application, rien du tout NetworkCallbackn'est appelé (c'est très étrange à cause de la p. 1)
  3. Si nous avons une connexion wifi mais sans onAvailabledéclencheurs de méthode de connexion Internet . Et je pense que c'est un comportement faux positif parce que nous nous attendons à une observation de la connexion Internet.

Comme vous le voyez dans le code ci-dessous, la connexion Internet par défaut est disponible et ne se déclenche que si elle change. Aucun déclencheur faussement positif.

Résumez simplement ceci et cela (mais uniquement pour l'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)
    }
}

Comment fournir les dépendances

@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)

Et comment utiliser:

@Inject
lateinit var connectionManager: ConnectionManager

//....

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

Basé sur la réponse 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)
        }
    }
}

Et en l'utilisant presque de la même manière que dans la réponse originale (si vous observez à partir d'une activité, par exemple):

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