В Android N на официальном сайте упоминается, что «Приложения, ориентированные на Android N, не получают широковещательные сообщения CONNECTIVITY_ACTION». И также упоминается, что JobScheduler
можно использовать как альтернативу. Но JobScheduler
поведение не совсем такое, как у CONNECTIVITY_ACTION
трансляции.
В моем приложении для Android я использовал эту трансляцию, чтобы узнать состояние сети устройства. Я хотел знать, было ли это состояние CONNECTING
или CONNECTED
с помощью CONNECTIVITY_ACTION
трансляции, и это лучше всего соответствовало моим требованиям.
Теперь, когда он устарел, может ли кто-нибудь предложить мне альтернативный подход для получения текущего состояния сети?
targetSdkVersion
до N или более поздней версии?BroadcastReceiver
сandroid.net.conn.CONNECTIVITY_CHANGE
намерением даже при таргетинге на API29, вам просто нужно зарегистрировать его вApplication.OnCreate
. Вы просто не получите никаких обновлений, когда приложение будет закрыто.Ответы:
Что будет устаревшим, так это возможность фонового приложения получать изменения состояния сетевого подключения.
Как сказал Дэвид Вассер, вы все еще можете получать уведомления об изменениях подключения, если компонент приложения создан (не уничтожен) и вы программно зарегистрировали свой приемник с его контекстом, а не в манифесте.
Или вы можете использовать вместо этого NetworkCallback . В частности, вам нужно будет переопределить onAvailable для изменений состояния подключения.
Позвольте мне быстро набросать фрагмент:
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 } }
источник
Я обновлю
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 за ваше решение.
источник
В документации для Android N указано:
Это означает, что вы все еще можете зарегистрировать,
BroadcastReceiver
если ваше приложение работает на переднем плане, чтобы обнаруживать изменения в подключении к сети.источник
Intent
.Пожалуйста, проверьте первый ответ @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)
intentFilter
иconnectivityManager
) вам не нужно явно определять их тип (IntentFilter
иConnectivityManager
соответственно).Несколько дней назад я столкнулся с той же проблемой и решил использовать эту библиотеку 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();
источник
Я написал реализацию 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" />
Я с нетерпением жду ваших полезных комментариев и улучшений.
источник
(context as AppCompatActivity).runOnUiThread(object: Runnable{ override fun run() { onConnectionAvailable.invoke() } })
вместоonConnectionAvailable.invoke()
. То же самое дляonConnectionLost.invoke()
.На основе ответа @ 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) } } }
И использование его почти так же, как в исходном ответе (например, если наблюдать из Activity):
ConnectivityWatcher(this).observe(this, Observer { Log.i("*-*-*", "is internet available? - ${if (it) "Yes" else "No"}") })
источник
Приложения, ориентированные на Android N (Nougat), не получают
CONNECTIVITY_ACTION
широковещательные сообщения, определенные в манифесте (см. Svelte ).Возможные решения:
ConnectivityManager.registernetworkCallback()
после запуска приложения.JobScheduler
и укажите безлимитную сеть черезsetRequiredNetworkType()
.См. Также Android O - Обнаружение изменения подключения в фоновом режиме
источник
Я согласен с ответом, предложенным @rds.
Имейте в виду, что CONNECTIVITY_ACTION устарело на уровне API 28.
Вам нужно использовать
connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
Вопрос в том, что вы не можете использовать BroadcastReceiver, так как же тогда?
Вы можете использовать JobScheduler или лучше WorkManager (периодический запрос). Почему Periodic, потому что если это OneTimeRequest, то он сможет запускаться только один раз и продолжать прослушивание, пока ваше приложение находится на переднем плане.
В документации говорится:
После того, как приложение будет убито или удалено из списка последних приложений, networkCallback не сможет его прослушивать.
Итак, вам нужны такие периодические задания, чтобы приложение постоянно слушало. Какой должна быть продолжительность? Это зависит от вас и зависит от конкретного случая.
Я знаю, что это немного некрасиво, но так оно и есть. Одна из проблем может заключаться в том, что если устройство пользователя находится в режиме ожидания или приложение находится в состоянии ожидания, ваша работа может быть отложена.
источник
Когда мы регистрируем сетевой обратный вызов с помощью этого
registerNetworkCallback
метода, иногда он не срабатывает, а иногда вызывает ложное срабатывание:onAvailable
метод сработает.NetworkCallback
вызывается (это очень странно из-за п. 1)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 }
источник