ConnectivityManager.CONNECTIVITY_ACTION застарілий


94

В Android N на офіційному веб-сайті згадується, що "Програми, націлені на Android N, не отримують трансляцій CONNECTIVITY_ACTION". І також згадується, що JobSchedulerможна використовувати як альтернативу. Але JobSchedulerце не забезпечує точно таку ж поведінку, як CONNECTIVITY_ACTIONтрансляція.

У моїй програмі для Android я використовував цю трансляцію, щоб знати стан мережі пристрою. Я хотів би знати , якщо ця держава CONNECTINGабо CONNECTEDза допомогою CONNECTIVITY_ACTIONтрансляції , і це було найкраще підходить для моєї вимоги.

Тепер, коли він застарілий, хтось може запропонувати мені альтернативний підхід для отримання поточного стану мережі?


10
А що, якщо ОП колись захоче якоїсь поведінки, яка вимагає підвищення targetSdkVersionN або пізнішої версії?
Майкл

1
Ну, я теж знаю, що якщо я не націлю свою програму на Android NI, я отримаю трансляцію. Але мій додаток повинен підтримувати Android N. Як я можу отримати однакову поведінку трансляції в Android N? Чи є якийсь інший підхід, який я можу спробувати? @DavidWasser
db

Іноді я думаю, що має сенс більше турбуватися про майбутнє в майбутньому. Це суто прагматичний підхід до програмування. Звичайно, ви завжди можете спробувати переконатися, що ваш код не використовує жодних застарілих функцій. З іншого боку, застарілі функції зазвичай залишаються довгий час, і можливо, ваш додаток закінчиться до того, як застарілі функції зникнуть. Android N настільки новий, що я б не витрачав на це багато часу, турбуючись про це. Тим не менше. Тільки мої 2 центи. Зверніть увагу, що я написав коментар до запитання і не припускав, що "не робіть цього" є правильною відповіддю.
Девід Вассер

2
@Raghuramdb Ваш додаток може працювати на Android N, навіть якщо ви не націлюєте його на Android N. Вам потрібно націлити Android N, лише якщо ви хочете використовувати функції, доступні лише в Android N.
Девід Вассер

2
Ви все ще можете використовувати фільтр BroadcastReceiverз android.net.conn.CONNECTIVITY_CHANGEнамірами навіть при націлюванні на API29, вам просто потрібно зареєструвати його в Application.OnCreate. Ви просто не отримаєте жодних оновлень, коли програму закрито.
П’єр,

Відповіді:


97

Що буде застарілим, так це можливість програми у фоновому режимі отримувати зміни стану мережевого підключення.

Як сказав Девід Вассер, ви все одно можете отримувати сповіщення про зміни підключення, якщо компонент програми екземпляром (не знищений), і ви зареєстрували ваш приймач програмно з його контекстом, замість того, щоб робити це в маніфесті.

Або ви можете використовувати NetworkCallback замість цього. Зокрема, вам доведеться замінити на Доступний для підключених змін стану.

Дозвольте мені швидко скласти фрагмент:

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

2
Оскільки цей прийом зможе працювати, лише якщо програма запущена на передньому плані. Чи означає це, що ми більше не маємо можливості слухати подію підключення, коли програма не працює на передньому плані? Наявність <action android: name = "android.net.conn.CONNECTIVITY_CHANGE" /> в manifest.xml більше не впливає на Android N.
Cheok Yan Cheng

2
@CheokYanCheng AFAIK це правильно. Вам потрібно мати процес, який виконується на передньому плані, щоб прослуховувати події зв’язку. Здається, припущення інженерів фреймворків Android полягало в тому, що прослуховування подій підключення здійснювалось переважно для того, щоб знати, коли починати синхронізацію даних між клієнтом та сервером. Тому JobScheduler - рекомендований спосіб для цього випадку використання.
Amokrane Chentir

25
хай що, біса, ще 10 оновлень для android, і все, що ми зможемо написати - це привіт, світова програма
DennisVA

1
Чи потрібно скасовувати реєстрацію NetworkCallback (наприклад, у методі активності onDestroy)?
Руслан Берозов

2
@Ruslan так, звичайно, інакше ви
випустите

34

Я оновлю Sayem'sвідповідь на виправлення неполадок, які мені показують.

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

І те саме використання:

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

До речі дякую sayem за ваше рішення.


2
Дивовижне рішення!
коробка

2
Дуже хороше рішення для використання даних в реальному часі та підтримки старішої версії
Пракаш Шукла,

Це найкраще рішення, доступне в Інтернеті.
Каран Шарма

Дуже хороше рішення! АЛЕ є одне "не" - це неправильний спосіб використання методу onAvailable (мережа: Мережа?), Оскільки він викликає навіть Інтернет, недоступний. Краще використовувати onCapabilitiesChanged (мережа: Network, networkCapabilities: NetworkCapabilities) і перевірити networkCapabilities.hasCapability (NET_CAPABILITY_INTERNET) і networkCapabilities.hasCapability (NET_CAPABILITY_VALIDATED).
Дмитро Канунніков,

як отримати ip та тип мережі в межах цієї бази коду?
A_rmas

28

У документації для Android N зазначено:

Додатки, орієнтовані на Android N, не отримують трансляцій CONNECTIVITY_ACTION, навіть якщо вони мають записи маніфесту, щоб вимагати сповіщення про ці події. Додатки, що працюють на передньому плані, все ще можуть прослуховувати CONNECTIVITY_CHANGE у своєму основному потоці, якщо вони запитують повідомлення за допомогою BroadcastReceiver.

Це означає, що ви все ще можете зареєструвати, BroadcastReceiverякщо ваша програма працює на передньому плані, щоб виявити зміни в мережному підключенні.


Хороший тонкий улов :)
Amokrane Chentir

чи означає це, що програма перестане отримувати трансляції, коли вона не буде на першому плані? (отже, я не можу слухати це в службі, наприклад?)
sundie

1
Я точно не знаю, мені потрібно було б це перевірити, щоб бути впевненим. Однак читання документації може здатися, що якщо ваша програма не на першому плані, ви не отримаєте трансляцію Intent.
Девід Вассер,

2
Але виявлення зміни з’єднання у фоновому режимі є обов’язковим для будь-яких програм sip-додатків (VoIP) ... ці програми зазвичай працюють у фоновому режимі протягом днів і переходять на перший план, лише якщо надходить дзвінок (як і ваш телефонний набір). Ці програми повинні автоматично підключатись у фоновому режимі. Це вбиває всі ті програми (які не мають власного push-сервера) з платформи android, оскільки вони будуть офлайн. завжди.
Grisgram

просто скористайтеся послугою push-сервісу firebase.
П’єр,

21

Будь ласка, перевірте спочатку відповідь @Amokrane Chentir щодо підтримки Android N.

Для тих, хто хоче підтримувати на будь-якому рівні api та спостерігати за цим на інтерфейсі, будь ласка, перевірте нижченаведений код.

LiveData від 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)
        }
    }
}

спостерігати в інтерфейсі користувача (Activity / Fragment):

val connectionLiveData = ConnectionLiveData(context)
    connectionLiveData.observe(this, Observer { 
       // do whatever you want with network connectivity change 
})

До речі, вам не потрібно IntentFilterчітко визначати . Приблизно так:var intentFilter = IntentFilter(CONNECTIVITY_ACTION)
Райан Амарал

дякую за вашу пропозицію. Я не хотів створювати об'єкт щоразу в onActive.
Саєм

Я маю на увазі, що 2 глобальним змінним / властивостям ( intentFilterі connectivityManager) вам не потрібно чітко визначати їх тип ( IntentFilterі ConnectivityManagerвідповідно).
Райан Амарал,

7

Я зіткнувся з тією ж проблемою кілька днів тому, і я вирішив використовувати цю бібліотеку Android-Job

Ця бібліотека використовує JobSchedular, GcmNetworkManagerі в BroadcastReceiverзалежності від Android версії додаток працює на.

Почати роботу досить просто

new JobRequest.Builder(DemoSyncJob.TAG)
            .setRequiresCharging(true)
            .setRequiresDeviceIdle(false)
            .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) // this is what gets the job done
            .build()
            .schedule();

1
я пробував такий самий планувальник і отримую подібні винятки. Ви намагаєтесь створити роботу без обмежень, це заборонено. Ви можете допомогти нам вирішити це питання ??
Sanket Kachhela

Використання Android-Job для цієї мети насправді є не дуже вдалим рішенням. Він призначений для запуску речей у визначений час, або один раз, або періодично. Це має на меті забезпечити підтримку ретро-сумісності для Сигналізації та подібних. Це суперечить цілій ідеї, чому API змінився, і читання: developer.android.com/training/monitoring-device-state/... Ви можете швидко зрозуміти, чому.
pedronveloso

єдина проблема полягає в тому, що в Android N це може бути заплановано лише на мінімум 15 хвилин у майбутньому
Fire Crow

4

Я написав реалізацію Kotlin, яка базується на відповіді Саяма, але без LiveData. Я вирішив застосувати (на даний момент часу) найновіший метод API ( ConnectivityManager#registerDefaultNetworkCallback), який націлений на 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)
        }
    }

}

Використання:

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

або:

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

Не забудьте додати ACCESS_NETWORK_STATEдозвіл у своєму AndroidManifest.xml :

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

Я з нетерпінням чекаю корисних коментарів та вдосконалень від вас.


1
Мені довелося щось змінити для зворотних викликів, щоб мати можливість "торкатися поглядів" в Activity (контексті) в основному потоці: (context as AppCompatActivity).runOnUiThread(object: Runnable{ override fun run() { onConnectionAvailable.invoke() } })замість onConnectionAvailable.invoke(). Те саме для onConnectionLost.invoke().
Андрей Воробьев

Так, залежно від випадку використання, вам може знадобитися переключити потоки. Я не став би це частиною класу, але натомість дозволив споживачеві класу подбати про це. Але дякую за підказку.
JJD

2

Додатки, націлені на Android N (Нуга), не отримують CONNECTIVITY_ACTIONтрансляцій, визначених у маніфесті (див. Svelte ).

Можливі рішення:

Див. Також Android O - Виявлення зміни з’єднання у фоновому режимі


1

Я згоден з відповіддю, запропонованою @rds.

Майте на увазі, що CONNECTIVITY_ACTION застаріло на рівні API 28.

Якщо у вас є вимога щодо визначення стану Wi-Fi (підключення / відключення), незважаючи на те, що програму вбито, і ви хочете націлити на останню версію, у вас немає великого вибору.

Вам потрібно використовувати connectivityManager.registerNetworkCallback(networkRequest, networkCallback)

Питання в тому, що ви не можете використовувати BroadcastReceiver, то як тоді?

Ви можете використовувати JobScheduler або краще, якщо WorkManager (періодичний запит). Чому періодично, тому що якщо це OneTimeRequest, він зможе запуститись лише один раз і продовжувати слухати, поки ваша програма знаходиться на передньому плані.

Документація говорить:

Зворотні дзвінки продовжуватимуть викликатись до виходу програми або посилання #unregisterNetworkCallback (NetworkCallback)}.

Як тільки додаток буде вбито або видалено зі списку останніх програм, networkCallback не зможе слухати.

Отже, вам потрібні такі періодичні завдання, щоб програма постійно слухала. Скільки має бути тривалість? Це залежить від вас і залежить від конкретного випадку.

Я знаю, що це трохи потворно, але це так. Однією із проблем може бути те, що якщо пристрій користувача перебуває в режимі Doze або програма перебуває в режимі очікування, ваша робота може затриматися.


Також майте на увазі, що на деяких сильно налаштованих EMUI ОС MIUI Android OS workManager (періодичні завдання) не завжди повинна працювати коректно.
Кебаб

1

Коли ми реєструємо зворотний виклик з використанням registerNetworkCallbackметоду, іноді він не запускається, а іноді спрацьовує помилково позитивний:

  1. Якщо ми запускаємо програму з підключенням до Інтернету, onAvailableметод запускається.
  2. Але якщо на пристрої немає підключення до Інтернету, коли ми запускаємо програму, нічого з цього NetworkCallbackне викликається (це дуже дивно через с. 1)
  3. Якщо у нас є з’єднання Wi-Fi, але не onAvailableспрацьовує спосіб підключення до Інтернету . І я думаю, що це помилково позитивна поведінка, тому що ми очікуємо спостереження за підключенням до Інтернету.

Як ви бачите в нижченаведеному коді, за замовчуванням доступне підключення до Інтернету, яке спрацьовує, лише якщо воно змінюється. Немає хибнопозитивних тригерів.

Просто підсумувати це і це відповіді (але тільки для 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)
    }
}

Як надати залежності

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

І як користуватися:

@Inject
lateinit var connectionManager: ConnectionManager

//....

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

1

На основі відповіді @ 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)
        }
    }
}

І використовуючи його майже так само, як і в оригінальній відповіді (якщо спостерігати з Акції, наприклад):

ConnectivityWatcher(this).observe(this, Observer {
    Log.i("*-*-*", "is internet available? - ${if (it) "Yes" else "No"}")
})
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.