ConnectivityManager.CONNECTIVITY_ACTION ist veraltet

93

In Android N wird auf der offiziellen Website erwähnt, dass "Apps für Android N keine CONNECTIVITY_ACTION-Sendungen empfangen". Und es wird auch erwähnt, dass JobSchedulerals Alternative verwendet werden kann. Aber das JobSchedulerbietet nicht genau das gleiche Verhalten wie CONNECTIVITY_ACTIONBroadcast.

In meiner Android-Anwendung habe ich diese Übertragung verwendet, um den Netzwerkstatus des Geräts zu ermitteln. Ich wollte wissen, ob dieser Zustand war CONNECTINGoder CONNECTEDmit Hilfe von CONNECTIVITY_ACTIONRundfunk und es war am besten für meine Anforderung geeignet.

Kann mir jetzt, da es veraltet ist, jemand einen alternativen Ansatz vorschlagen, um den aktuellen Netzwerkstatus zu erhalten?

Raghuram db
quelle
10
Und was ist, wenn das OP eines Tages ein Verhalten wünscht, das eine Erhöhung targetSdkVersionauf N oder später erfordert ?
Michael
1
Nun, ich weiß auch, dass wenn ich meine Anwendung nicht auf Android NI ausrichte, die Sendung empfangen wird. Meine Anwendung muss jedoch Android N unterstützen. Wie kann ich in Android N das gleiche Übertragungsverhalten erzielen? Gibt es einen anderen Ansatz, den ich ausprobieren kann? @ DavidWasser
Raghuram db
Manchmal denke ich, dass es sinnvoller ist, sich in Zukunft Sorgen um die Zukunft zu machen. Dies ist ein rein pragmatischer Ansatz für die Programmierung. Natürlich können Sie immer versuchen, sicherzustellen, dass Ihr Code keine veralteten Funktionen verwendet. Auf der anderen Seite bleiben veraltete Funktionen normalerweise lange bestehen, und es kann sein, dass Ihre App nicht mehr funktioniert, bevor die veralteten Funktionen verschwinden. Android N ist so neu, dass ich nicht viel Zeit damit verbringen würde, mir darüber Sorgen zu machen. Noch. Nur meine 2 Cent. Bitte beachten Sie, dass ich einen Kommentar zu der Frage geschrieben habe und nicht vorgeschlagen habe, dass "Mach das nicht" eine gültige Antwort ist.
David Wasser
2
@Raghuramdb Ihre App kann auf Android N ausgeführt werden, auch wenn Sie Ihre App nicht auf Android N ausrichten. Sie müssen nur auf Android N abzielen, wenn Sie Funktionen verwenden möchten, die nur in Android N verfügbar sind.
David Wasser
2
Sie können BroadcastReceiverden android.net.conn.CONNECTIVITY_CHANGEFilter mit Absicht auch dann verwenden, wenn Sie auf API29 abzielen. Sie müssen ihn nur registrieren Application.OnCreate. Sie erhalten nur keine Updates, wenn die App geschlossen wird.
Pierre

Antworten:

96

Was veraltet ist, ist die Fähigkeit einer Hintergrundanwendung, Änderungen des Netzwerkverbindungsstatus zu empfangen.

Wie David Wasser sagte, können Sie immer noch über Konnektivitätsänderungen benachrichtigt werden, wenn die App-Komponente instanziiert (nicht zerstört) wird und Sie Ihren Empfänger programmgesteuert mit seinem Kontext registriert haben , anstatt dies im Manifest zu tun.

Oder Sie können stattdessen NetworkCallback verwenden. Insbesondere müssen Sie onAvailable für Änderungen des Verbindungsstatus überschreiben .

Lassen Sie mich schnell einen Ausschnitt entwerfen:

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
quelle
2
Da diese Technik nur funktionieren kann, wenn die App im Vordergrund läuft. Bedeutet das, dass wir das Verbindungsereignis nicht mehr abhören können, wenn die App nicht im Vordergrund ausgeführt wird? <Action android: name = "android.net.conn.CONNECTIVITY_CHANGE" /> in manifest.xml zu haben, hat in Android N keine Auswirkung mehr.
Cheok Yan Cheng
2
@CheokYanCheng AFAIK das ist richtig. Sie benötigen einen Prozess, der im Vordergrund ausgeführt wird, um auf Konnektivitätsereignisse zu warten. Es scheint, dass die Android-Framework-Ingenieure davon ausgegangen sind, dass das Abhören von Konnektivitätsereignissen hauptsächlich durchgeführt wurde, um zu wissen, wann mit der Synchronisierung von Daten zwischen Client und Server begonnen werden soll. Daher ist JobScheduler die empfohlene Methode für diesen Anwendungsfall.
Amokrane Chentir
23
lol was zum Teufel, weitere 10 Android-Updates und alles, was wir schreiben können, ist eine Hallo-Welt-App
DennisVA
1
Muss ich die Registrierung von NetworkCallback aufheben (z. B. in der onDestroy-Methode der Aktivität)?
Ruslan Berozov
2
@ Ruslan ja natürlich, oder Sie werden alles lecken, was registriert ist
DennisVA
32

Ich werde die Sayem'sAntwort auf Probleme mit festen Flusen aktualisieren , die mir angezeigt werden.

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

Und gleiche Verwendung:

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

Übrigens, danke sayem für deine Lösung.

Kebab Krabby
quelle
2
Erstaunliche Lösung!
Box
2
Sehr gute Lösung, um Live-Daten zu verwenden und ältere Versionen zu unterstützen
Prakash Shukla
Dies ist die beste im Internet verfügbare Lösung.
Karan Sharma
Sehr gute Lösung! ABER es gibt ein "nicht" - es ist eine falsche Methode, die onAvailable-Methode (Netzwerk: Netzwerk?) Zu verwenden, da sie selbst das Internet aufruft und nicht verfügbar ist. Es ist besser, onCapabilitiesChanged (Netzwerk: Netzwerk, Netzwerkkapazitäten: Netzwerkkapazitäten) zu verwenden und networkCapabilities.hasCapability (NET_CAPABILITY_INTERNET) und networkCapabilities.hasCapability (NET_CAPABILITY_VALIDATED) zu überprüfen.
DmitryKanunnikoff
1
@DmitryKanunnikoff Ich habe den Code aktualisiert.
Kebab Krabby
28

In der Dokumentation für Android N heißt es:

Apps, die auf Android N ausgerichtet sind, empfangen keine CONNECTIVITY_ACTION-Broadcasts, selbst wenn sie über Manifesteinträge verfügen, um eine Benachrichtigung über diese Ereignisse anzufordern. Apps, die im Vordergrund ausgeführt werden, können weiterhin auf CONNECTIVITY_CHANGE in ihrem Hauptthread warten, wenn sie eine Benachrichtigung mit einem BroadcastReceiver anfordern.

Dies bedeutet, dass Sie weiterhin eine registrieren können, BroadcastReceiverwenn Ihre App im Vordergrund ausgeführt wird, um Änderungen in der Netzwerkkonnektivität zu erkennen.

David Wasser
quelle
Schöner subtiler Fang :)
Amokrane Chentir
Bedeutet dies, dass die App keine Sendungen mehr empfängt, sobald sie nicht im Vordergrund steht? (
Also
1
Ich weiß es nicht genau, ich müsste es testen, um sicher zu sein. Beim Lesen der Dokumentation wird jedoch angezeigt, dass Sie die Sendung nicht erhalten würden, wenn Ihre App nicht im Vordergrund steht Intent.
David Wasser
1
Das Erkennen von Konnektivitätsänderungen im Hintergrund ist jedoch für alle sip-Apps (VoIP) obligatorisch. Diese Apps werden normalerweise tagelang im Hintergrund ausgeführt und springen nur dann in den Vordergrund, wenn ein Anruf eingeht (genau wie bei Ihrem Dialer des Telefons). Diese Apps müssen sich im Hintergrund automatisch wieder verbinden. Dadurch werden alle Apps (die keinen eigenen Push-Server haben) von der Android-Plattform entfernt, da sie offline sind. immer.
Grisgram
Verwenden Sie einfach den Firebase-Push-Service.
Pierre
20

Bitte überprüfen Sie zuerst die Antwort von @Amokrane Chentir auf Android N-Unterstützung.

Für diejenigen, die in allen API-Levels unterstützen und es in der Benutzeroberfläche beobachten möchten, überprüfen Sie bitte den folgenden Code.

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

Beobachten Sie in der Benutzeroberfläche (Aktivität / Fragment):

val connectionLiveData = ConnectionLiveData(context)
    connectionLiveData.observe(this, Observer { 
       // do whatever you want with network connectivity change 
})
Sayem
quelle
Übrigens müssen Sie nicht IntentFilterexplizit definieren . Wie so:var intentFilter = IntentFilter(CONNECTIVITY_ACTION)
Ryan Amaral
danke für Ihren Vorschlag. Ich wollte nicht jedes Mal ein Objekt in onActive erstellen.
Sayem
Ich meine , dass die 2 globalen Variablen / Eigenschaften ( intentFilterund connectivityManager) nicht explizit benötigen , um ihre Art zu definieren ( IntentFilterund ConnectivityManagerjeweils).
Ryan Amaral
7

Ich bin vor ein paar Tagen auf dasselbe Problem gestoßen und habe mich für diese Bibliothek Android-Job entschieden

Diese Bibliothek verwendet JobSchedular, GcmNetworkManagerund BroadcastReceiverje nachdem , welche Android - Version der App läuft.

Einen Job zu beginnen ist ziemlich einfach

new JobRequest.Builder(DemoSyncJob.TAG)
            .setRequiresCharging(true)
            .setRequiresDeviceIdle(false)
            .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) // this is what gets the job done
            .build()
            .schedule();
Noman Rafique
quelle
1
Ich habe den gleichen Planer ausprobiert und eine Ausnahme wie diese erhalten. Sie versuchen, einen Job ohne Einschränkungen zu erstellen. Dies ist nicht zulässig. Können Sie uns bitte helfen, dieses Problem zu lösen?
Sanket Kachhela
Die Verwendung von Android-Job für diesen Zweck ist wirklich keine sehr gute Lösung. Es ist dazu gedacht, Dinge zu einer bestimmten Zeit entweder einmal oder in regelmäßigen Abständen auszuführen. Es soll Retro-Kompatibilitätsunterstützung für Alarme und dergleichen bieten. Dies widerspricht der ganzen Idee, warum sich die API geändert hat, und lautet: developer.android.com/training/monitoring-device-state/… Sie können schnell ein Gefühl dafür bekommen, warum.
Pedronveloso
Das einzige Problem ist, dass es in Android N nur für mindestens 15 Minuten in der Zukunft geplant werden kann
Fire Crow
4

Ich habe eine Kotlin-Implementierung geschrieben, die auf Sayams Antwort basiert , aber ohne LiveData. Ich habe beschlossen, die (zu diesem Zeitpunkt) neueste API-Methode ( ConnectivityManager#registerDefaultNetworkCallback) aufzurufen, die auf Android Nougat abzielt.

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

}

Verwendung:

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

oder:

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

Vergessen Sie nicht, die ACCESS_NETWORK_STATEBerechtigung in Ihre AndroidManifest.xml aufzunehmen :

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

Ich freue mich darauf, hilfreiche Kommentare und Verbesserungen von Ihnen zu lesen.

JJD
quelle
1
Ich musste etwas ändern, damit Rückrufe Ansichten in einer Aktivität (Kontext) im Hauptthread "berühren" konnten: (context as AppCompatActivity).runOnUiThread(object: Runnable{ override fun run() { onConnectionAvailable.invoke() } })statt onConnectionAvailable.invoke(). Das gleiche gilt für onConnectionLost.invoke().
17ндрей Воробьев
Ja, abhängig von Ihrem Anwendungsfall müssen Sie möglicherweise die Threads wechseln. Ich würde es nicht zu einem Teil der Klasse machen, sondern den Verbraucher der Klasse sich darum kümmern lassen. Aber danke für den Hinweis.
JJD
1

Ich stimme der Antwort von @rds zu.

Beachten Sie, dass CONNECTIVITY_ACTION in API-Level 28 veraltet ist.

Wenn Sie die Anforderung haben, dass der WLAN-Status (Verbinden / Trennen) erkannt werden soll, obwohl die App beendet wurde, und Sie auf die neueste Version abzielen möchten, haben Sie keine große Auswahl.

Sie müssen verwenden connectivityManager.registerNetworkCallback(networkRequest, networkCallback)

Die Frage ist, dass Sie BroadcastReceiver nicht verwenden können. Wie also?

Sie können entweder JobScheduler oder besser WorkManager (Periodic Request) verwenden. Warum periodisch, denn wenn es sich um eine OneTimeRequest handelt, kann sie nur einmal ausgeführt werden und weiterhören, während sich Ihre App im Vordergrund befindet.

Die Dokumentation sagt:

Die Rückrufe werden so lange aufgerufen, bis entweder die Anwendung beendet wird oder der Link #unregisterNetworkCallback (NetworkCallback)} aufgerufen wird.

Sobald die App beendet oder aus der Liste der zuletzt verwendeten Apps entfernt wurde, kann networkCallback nicht mehr zuhören.

Sie benötigen also solche regelmäßigen Jobs, damit die App kontinuierlich zuhört. Wie lang sollte die Dauer sein? Das liegt bei Ihnen und hängt von Fall zu Fall ab.

Ich weiß, dass es ein bisschen hässlich ist, aber so ist es. Eine Herausforderung könnte sein, dass sich Ihr Auftrag verzögert, wenn sich das Gerät des Benutzers im Doze-Modus befindet oder sich die App im Standby-Status befindet.

Wahib Ul Haq
quelle
Beachten Sie auch, dass der MIUI Android OS workManager (regelmäßige Aufgaben) auf einigen stark angepassten EMUI nicht immer korrekt funktionieren muss.
Kebab Krabby
1

Wenn wir einen Netzwerkrückruf mit der registerNetworkCallbackMethode registrieren, wird er manchmal nicht und manchmal falsch positiv ausgelöst:

  1. Wenn wir eine App mit Internetverbindung starten, wird die onAvailableMethode ausgelöst.
  2. Wenn jedoch beim Starten einer App keine Internetverbindung auf dem Gerät besteht, wird nichts von dem NetworkCallbackaufgerufen (dies ist aufgrund von S. 1 sehr seltsam).
  3. Wenn wir WiFi-Verbindung haben, aber ohne Internetverbindung onAvailableMethode auslöst. Und ich denke, es ist ein falsch positives Verhalten, weil wir erwarten, dass die Internetverbindung beobachtet wird.

Wie Sie unten sehen, ist standardmäßig eine Internetverbindung verfügbar, die nur ausgelöst wird, wenn sie sich ändert. Keine falsch positiven Auslöser.

Fassen Sie einfach diese und diese Antworten zusammen (aber nur für 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)
    }
}

So stellen Sie die Abhängigkeiten bereit

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

Und wie man es benutzt:

@Inject
lateinit var connectionManager: ConnectionManager

//....

viewLifecycleOwner.observe(connectionManager) { isInternetAvailable ->
    // TODO 
}
Bitvale
quelle
0

Basierend auf der Antwort von @ 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)
        }
    }
}

Und verwenden Sie es fast genauso wie in der ursprünglichen Antwort (wenn Sie zum Beispiel von einer Aktivität aus beobachten):

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