ConnectivityManager.CONNECTIVITY_ACTION kullanımdan kaldırıldı


96

Android N'de, resmi web sitesinde "Android N'yi hedefleyen uygulamaların CONNECTIVITY_ACTION yayınlarını almadığı" belirtilmektedir. Ve JobScheduleralternatif olarak kullanılabileceğinden de bahsediliyor . Ancak, yayınla JobSchedulertam olarak aynı davranışı sağlamaz CONNECTIVITY_ACTION.

Android uygulamamda, cihazın ağ durumunu öğrenmek için bu yayını kullanıyordum. Bu durum olup olmadığını bilmek istedi CONNECTINGya CONNECTEDyardımıyla CONNECTIVITY_ACTIONyayın ve en iyi benim gereksinimi için uygun.

Artık kullanımdan kaldırıldığına göre, herhangi biri bana mevcut ağ durumunu almak için alternatif bir yaklaşım önerebilir mi?


10
Ya OP bir gün targetSdkVersionN'ye veya daha sonrasına yükseltmeyi gerektiren bazı davranışlar isterse ?
Michael

1
Uygulamamı Android NI'ye hedeflemezsem yayını alacağını da biliyorum. Ancak uygulamamın Android N'yi desteklemesi gerekiyor. Android N'de aynı yayın davranışını nasıl elde edebilirim? Deneyebileceğim başka bir yaklaşım var mı? @DavidWasser
Raghuram db

2
@Raghuramdb Uygulamanızı Android N'ye hedeflemeseniz bile uygulamanız Android N'de çalışabilir. Yalnızca Android N'de bulunan özellikleri kullanmak istiyorsanız yalnızca Android N'yi hedeflemeniz gerekir.
David Wasser

2
API29'u hedeflerken bile niyet filtresiyle BroadcastReceiverbirlikte kullanmaya devam edebilirsiniz android.net.conn.CONNECTIVITY_CHANGE, sadece kaydetmeniz gerekir Application.OnCreate. Uygulama kapatıldığında herhangi bir güncelleme almayacaksınız.
Pierre

1
@SimpleGuy Bir Uygulama sınıfı public class MainApplication extends Application implements Application.ActivityLifecycleCallbacks {...}geçersiz kılma uygulayın public void onCreate() {ve orada uygulamaya eklemek için IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); registerReceiver(new MyBroadcastReceiver(), intentFilter);Dont Forgot'ı arayınAndroidManifest.xmlandroid:name<application android:name=".MainApplication" ...>
Pierre

Yanıtlar:


104

Kullanımdan kaldırılacak olan şey, arka planda çalışan bir uygulamanın ağ bağlantı durumu değişikliklerini alma yeteneğidir.

David Wasser'in dediği gibi , uygulama bileşeni somutlaştırılmışsa (yok edilmemişse) ve alıcınızı bildirimde yapmak yerine programatik olarak bağlamıyla kaydetmişseniz bağlantı değişikliklerinden yine de haberdar olabilirsiniz .

Veya bunun yerine NetworkCallback'i kullanabilirsiniz . Özellikle, bağlı durum değişiklikleri için onAvailable özelliğini geçersiz kılmanız gerekecektir .

Hemen bir pasaj taslağı hazırlayayım:

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
Bu teknik sadece uygulama ön planda çalışıyorsa işe yarayacağından. Bu, uygulama ön planda çalışmadığında artık bağlantı olayını dinleme yeteneğimiz olmadığı anlamına mı geliyor? Manifest.xml'de <action android: name = "android.net.conn.CONNECTIVITY_CHANGE" /> seçeneğinin artık Android N'de hiçbir etkisi yoktur.
Cheok Yan Cheng

2
@CheokYanCheng AFAIK bu doğru. Bağlantı olaylarını dinlemek için ön planda çalışan bir işleme sahip olmanız gerekir. Görünüşe göre, Android çerçeve mühendisleri tarafından yapılan varsayım, bağlantı olaylarını dinlemenin çoğunlukla istemci ve sunucu arasında verileri ne zaman senkronize etmeye başlayacağını bilmek için yapıldığıydı. Bu nedenle, JobScheduler bu kullanım durumu için önerilen yoldur.
Amokrane Chentir

30
lol what the be,
other

1
NetworkCallback kaydını silmem gerekir mi (örneğin, etkinliğin onDestroy yönteminde)?
Ruslan Berozov

2
@Ruslan evet, yoksa kayıtlı
olanı sızdırırsın

35

Sayem'sBana gösterdiği tiftik sorunlarını düzeltmek için cevabı güncelleyeceğim .

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

Ve aynı kullanım:

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

Btw, çözümünüz için teşekkürler sayem.


2
İnanılmaz çözüm!
kutu

2
Canlı verileri kullanmak ve eski sürümü desteklemek için çok iyi bir çözüm
Prakash Shukla

İnternette bulunan en iyi çözüm budur.
Karan Sharma

1
Çok güzel bir çözüm! ANCAK bir "değil" vardır - onAvailable (ağ: Ağ?) Yöntemini kullanmanın yanlış bir yolu, çünkü bu, İnternet'in bile kullanılamamasına neden oluyor. OnCapabilitiesChanged (ağ: Network, networkCapabilities: NetworkCapabilities) kullanmak ve networkCapabilities.hasCapability (NET_CAPABILITY_INTERNET) ve networkCapabilities.hasCapability'yi (NET_CAPABILITY_VALIDATED) kontrol etmek daha iyidir.
DmitryKanunnikoff

1
@SimpleGuy Evet, bunu IDE'de kotlin'den java'ya dönüştürebilirsiniz. Varsayılan kısayol: CMD + OPTION + SHIFT + D
Kebab Krabby

29

Android N durumları için belgeler:

Android N'yi hedefleyen uygulamalar, bu olayların bildirimini talep etmek için manifest girişleri olsa bile CONNECTIVITY_ACTION yayınlarını almaz. Ön planda çalışan uygulamalar, bir BroadcastReceiver ile bildirim isterlerse ana ileti dizilerinde hala CONNECTIVITY_CHANGE dinleyebilir.

Bu BroadcastReceiver, ağ bağlantısındaki değişiklikleri algılamak için, uygulamanız ön planda çalışıyorsa hala bir kayıt yapabileceğiniz anlamına gelir .


Güzel ince yakalama :)
Amokrane Chentir

Bu, uygulamanın ön planda olmadığında yayın almayı durduracağı anlamına mı geliyor? (bu yüzden örneğin bir serviste
dinleyemiyorum

1
Emin değilim, emin olmak için test etmem gerekecek. Bununla birlikte, belgeleri okurken, uygulamanız ön planda değilse yayını alamayacağınız anlaşılır Intent.
David Wasser

2
Ancak arka planda bağlantı değişikliğini tespit etmek tüm sip uygulamaları (VoIP) için zorunludur ... bu uygulamalar normalde arka planda günlerce çalışır ve yalnızca bir çağrı geldiğinde ön plana atlar (tıpkı telefon çeviriciniz gibi). . Bu uygulamaların arka planda otomatik olarak yeniden bağlanması gerekir . Bu, çevrimdışı olacakları için android platformundan tüm bu uygulamaları (kendi push sunucusu olmayan) öldürür. her zaman.
Grisgram

sadece firebase push hizmetini kullanın.
Pierre

21

Lütfen android N desteği için önce @ Amokrane Chentir yanıtını kontrol edin.

Tüm api seviyesinde desteklemek ve ui'de gözlemlemek isteyenler için lütfen aşağıdaki kodu kontrol edin.

Ağ Bağlantısının LiveData:

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

UI'de gözlemleyin (Etkinlik / Parça):

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

btw, IntentFilteraçıkça tanımlamanıza gerek yoktur . Öyle:var intentFilter = IntentFilter(CONNECTIVITY_ACTION)
Ryan Amaral

önerin için teşekkürler. OnActive'da her seferinde nesne oluşturmak istemedim.
Sayem

Demek istediğim, 2 global değişken / özellik ( intentFilterve connectivityManager) türlerini ( IntentFilterve ConnectivityManagersırasıyla) açıkça tanımlamanıza gerek yoktur .
Ryan Amaral

Malzemelerin çoğu (ne yazık ki) kullanımdan kaldırıldı ..
Andrew

7

Birkaç gün önce aynı problemle karşılaştım ve bu kütüphaneyi Android-Job kullanmaya karar verdim

Bu kütüphane kullanımları JobSchedular, GcmNetworkManagerve BroadcastReceiverhangi Android sürümü bağlı uygulaması üzerinde çalışıyor.

Bir işe başlamak oldukça kolaydır

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

1
Aynı zamanlayıcıyı denedim ve bunun gibi istisna alıyorum Kısıtlamasız bir iş oluşturmaya çalışıyorsunuz, buna izin verilmiyor. Lütfen bunu çözmemize yardım eder misiniz?
Sanket Kachhela

Android-Job'u bu amaçla kullanmak gerçekten çok iyi bir çözüm değil. İşleri belirli bir zamanda, bir kez veya periyodik olarak yürütmek içindir. Alarmlar ve benzeri için geriye dönük uyumluluk desteği getirmesi amaçlanmıştır. Bu, API'nin neden değiştiğine dair tüm fikre aykırıdır ve şunu okuyun: developer.android.com/training/monitoring-device-state/… Nedenini hızlı bir şekilde anlayabilirsiniz.
pedronveloso

tek sorun, Android N'de yalnızca gelecekte en az 15 dakika için planlanabilmesidir
Fire Crow

5

@ KebabKrabby'nin cevabına göre:

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

Ve onu orijinal cevapla neredeyse aynı şekilde kullanmak (örneğin bir Faaliyetten gözlemlerseniz):

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

1
Şimdiye kadarki en yararlı cevap. Bu çözümle ilgili tek sorun, ConnectivityManager.CONNECTIVITY_ACTION ve Broadcast Receiver ile, uygulama bir uçak modu açıkken veya mobil veri / WIFI kapalıyken başlatıldığında yanlış bir değer yaymanın mümkün olmasıdır. Sorun, ConnectivityManager.NetworkCallback () bu durumda tetiklenmemesidir. Bunun üstesinden gelmek için, ConnectivityWatcher'ın varsayılan değerini "false" olarak ayarlayabilir veya bu değeri gözlemleyip ViewModel'de MutableLiveData'ya iletebilir ve coroutines'i kullanarak belirli bir gecikmeden sonra değeri false olarak ayarlayabilirsiniz (örneğin 1000ms) boşsa
Alex Shevchyshen

@AlexShevchyshen Yorumunuz için çok teşekkür ederim!
DmitryKanunnikoff

4

Sayam'ın cevabına dayanan ancak içermeyen bir Kotlin uygulaması yazdım LiveData. ConnectivityManager#registerDefaultNetworkCallbackAndroid Nougat'ı hedefleyen (bu noktada) en son API yöntemini ( ) kullanmaya karar verdim .

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

}

Kullanım:

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

veya:

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

ACCESS_NETWORK_STATEİzni AndroidManifest.xml dosyanıza eklemeyi unutmayın :

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

Sizden faydalı yorum ve iyileştirmeleri okumak için sabırsızlanıyorum.


1
Geri aramaların (context as AppCompatActivity).runOnUiThread(object: Runnable{ override fun run() { onConnectionAvailable.invoke() } })yerine ana iş parçacığı üzerindeki bir Aktivitede (bağlam) "görünümlere dokunabilmek" için bir şeyler değiştirmem gerekiyordu onConnectionAvailable.invoke(). Aynısı için onConnectionLost.invoke().
Андрей Воробьев

Evet, kullanım durumunuza bağlı olarak ileti dizileri arasında geçiş yapmanız gerekebilir. Onu sınıfın bir parçası yapmaz, bunun yerine sınıfın tüketicisinin ilgilenmesine izin veririm. Ama ipucu için teşekkürler.
JJD


1

Ben katılıyorum cevap @rds tarafından önerdi.

CONNECTIVITY_ACTION'ın API seviyesi 28'de kullanımdan kaldırıldığını unutmayın .

Uygulamanın kapatılmasına rağmen Wifi durumunun (bağlan / bağlantıyı kes) tespit edilmesi şartına sahipseniz ve en son sürümü hedeflemek istiyorsanız, fazla seçeneğiniz yoktur.

Kullanmanız gerekir connectivityManager.registerNetworkCallback(networkRequest, networkCallback)

Soru şu ki, BroadcastReceiver'ı kullanamazsınız, peki o zaman nasıl?

Ya JobScheduler'ı ya da WorkManager (Periyodik İstek) ise daha iyisini kullanabilirsiniz. Neden Periyodik, çünkü bir OneTimeRequest ise, yalnızca bir kez çalışabilir ve uygulamanız ön plandayken dinlemeye devam edebilir.

Belgeler diyor ki:

Geri aramalar, uygulama çıkıncaya veya #unregisterNetworkCallback (NetworkCallback)} bağlantısı çağrılana kadar çağrılmaya devam edecektir.

Uygulama son uygulamalar listesinden kaldırıldığında veya kaldırıldığında, networkCallback dinleyemez.

Bu nedenle, uygulamanın sürekli dinlemesi için bu tür periyodik işlere ihtiyacınız var. Süre ne kadar olmalıdır? Bu size bağlıdır ve duruma göre değişir.

Bunun biraz çirkin olduğunu biliyorum ama bu böyle. Zorluklardan biri, kullanıcının cihazı Doz modundaysa veya uygulama Bekleme Durumundaysa, işinizin gecikebileceği olabilir.


Ayrıca, yoğun şekilde özelleştirilmiş bazı EMUI'lerde, MIUI Android OS workManager'ın (periyodik görevler) her zaman doğru şekilde çalışması gerekmediğini unutmayın.
Kebab Krabby

1

registerNetworkCallbackYöntemi kullanarak bir ağ geri araması kaydettiğimizde bazen tetiklemez ve bazen yanlış pozitif tetikler:

  1. İnternet bağlantısı olan bir uygulama onAvailablebaşlatırsak , yöntem tetiklenir.
  2. Ancak bir uygulamayı başlattığımızda cihazda internet bağlantısı yoksa hiçbir şey NetworkCallbackçağrılmaz (s. 1 nedeniyle çok tuhaf)
  3. Wifi bağlantımız varsa ancak internet bağlantısı onAvailableyöntemi yoksa tetikler. Ve bunun yanlış pozitif bir davranış olduğunu düşünüyorum çünkü internet bağlantısının gözlemlenmesini bekliyoruz.

Aşağıdaki kodda gördüğünüz gibi varsayılan olarak internet bağlantısı mevcuttur ve sadece değiştiğinde tetiklenir. Yanlış pozitif tetikleyici yok.

Sadece bunu ve bu cevapları özetleyin (ancak yalnızca API> = 21 için):

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

Bağımlılıklar nasıl sağlanır

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

Ve nasıl kullanılır:

@Inject
lateinit var connectionManager: ConnectionManager

//....

viewLifecycleOwner.observe(connectionManager) { isInternetAvailable ->
    // TODO 
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.