Di Android N, disebutkan di situs resminya bahwa "Aplikasi yang menargetkan Android N tidak menerima siaran CONNECTIVITY_ACTION". Dan disebutkan juga bahwa JobScheduler
dapat digunakan sebagai alternatif. Tetapi JobScheduler
tidak memberikan perilaku yang persis sama seperti CONNECTIVITY_ACTION
siaran.
Di aplikasi Android saya, saya menggunakan siaran ini untuk mengetahui status jaringan perangkat. Saya ingin tahu apakah status ini CONNECTING
atau CONNECTED
dengan bantuan CONNECTIVITY_ACTION
penyiaran dan paling sesuai untuk kebutuhan saya.
Sekarang sudah tidak digunakan lagi, dapatkah seseorang menyarankan saya pendekatan alternatif untuk mendapatkan status jaringan saat ini?
targetSdkVersion
ke N atau nanti?BroadcastReceiver
denganandroid.net.conn.CONNECTIVITY_CHANGE
filter maksud bahkan saat menargetkan API29, Anda hanya perlu mendaftarkannyaApplication.OnCreate
. Anda tidak akan mendapatkan pembaruan apa pun saat aplikasi ditutup.Jawaban:
Yang tidak akan digunakan lagi adalah kemampuan aplikasi latar belakang untuk menerima perubahan status koneksi jaringan.
Seperti yang dikatakan David Wasser , Anda masih bisa mendapatkan pemberitahuan tentang perubahan konektivitas jika komponen aplikasi dibuatkan (tidak dimusnahkan) dan Anda telah mendaftarkan penerima secara terprogram dengan konteksnya, alih-alih melakukannya dalam manifes.
Atau Anda dapat menggunakan NetworkCallback sebagai gantinya. Secara khusus, Anda perlu mengganti onAvailable untuk perubahan status terhubung.
Izinkan saya membuat draf cuplikan dengan cepat:
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 } }
sumber
Saya akan memperbarui
Sayem's
jawaban untuk memperbaiki masalah lint yang ditampilkan kepada saya.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) } }
Dan penggunaan yang sama:
val connectionLiveData = ConnectionLiveData(context) connectionLiveData.observe(this, Observer { isConnected -> isConnected?.let { // do job } })
Btw terima kasih sayem atas solusinya.
sumber
Dokumentasi untuk Android N menyatakan:
Artinya, Anda masih bisa mendaftarkan
BroadcastReceiver
jika aplikasi Anda berjalan di latar depan, untuk mendeteksi perubahan dalam konektivitas jaringan.sumber
Intent
.Silakan periksa dulu jawaban @Amokrane Chentir untuk dukungan Android N.
Bagi mereka yang ingin mendukung di semua level api & mengamatinya di ui, silakan periksa kode di bawah ini.
LiveData dari 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) } } }
amati di UI (Aktivitas / Fragmen):
val connectionLiveData = ConnectionLiveData(context) connectionLiveData.observe(this, Observer { // do whatever you want with network connectivity change })
sumber
IntentFilter
secara eksplisit. Seperti:var intentFilter = IntentFilter(CONNECTIVITY_ACTION)
intentFilter
danconnectivityManager
) Anda tidak perlu secara eksplisit menentukan jenisnya (IntentFilter
danConnectivityManager
masing - masing).Saya mengalami masalah yang sama beberapa hari yang lalu dan saya memutuskan untuk menggunakan perpustakaan ini Android-Job
Library ini menggunakan
JobSchedular
,GcmNetworkManager
danBroadcastReceiver
bergantung pada versi Android aplikasi yang dijalankan.Memulai pekerjaan cukup mudah
new JobRequest.Builder(DemoSyncJob.TAG) .setRequiresCharging(true) .setRequiresDeviceIdle(false) .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) // this is what gets the job done .build() .schedule();
sumber
Saya menulis implementasi Kotlin yang didasarkan pada jawaban Sayam tetapi tanpa
LiveData
. Saya memutuskan untuk menjalankan (pada saat ini) metode API terbaru (ConnectivityManager#registerDefaultNetworkCallback
) yang menargetkan 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) } } }
Pemakaian:
val onConnectionAvailable = TODO() val connectivityObserver = ConnectivityObserver(context, onConnectionAvailable) connectivityObserver.start() connectivityObserver.stop()
atau:
val onConnectionAvailable = TODO() val onConnectionLost = TODO() ConnectivityObserver(context, onConnectionAvailable, onConnectionLost, shouldStopAfterFirstResponse = true ).start()
Jangan lupa untuk menambahkan
ACCESS_NETWORK_STATE
izin di AndroidManifest.xml Anda :<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Saya menantikan untuk membaca komentar dan peningkatan yang bermanfaat dari Anda.
sumber
(context as AppCompatActivity).runOnUiThread(object: Runnable{ override fun run() { onConnectionAvailable.invoke() } })
bukanonConnectionAvailable.invoke()
. Sama untukonConnectionLost.invoke()
.Berdasarkan jawaban @ 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) } } }
Dan penggunaannya hampir sama dengan jawaban aslinya (jika diamati dari suatu Kegiatan, misalnya):
ConnectivityWatcher(this).observe(this, Observer { Log.i("*-*-*", "is internet available? - ${if (it) "Yes" else "No"}") })
sumber
Aplikasi yang menargetkan Android N (Nougat) tidak menerima
CONNECTIVITY_ACTION
siaran yang ditentukan dalam manifes (lihat Svelte ).Solusi yang memungkinkan:
ConnectivityManager.registernetworkCallback()
setelah aplikasi berjalan.JobScheduler
, dan tentukan jaringan tanpa meteran melaluisetRequiredNetworkType()
.Lihat juga Android O - Mendeteksi perubahan konektivitas di latar belakang
sumber
Saya setuju dengan jawaban yang disarankan oleh @rds.
Perlu diingat bahwa CONNECTIVITY_ACTION sudah tidak digunakan lagi di API level 28.
Anda perlu menggunakan
connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
Pertanyaannya adalah Anda tidak dapat menggunakan BroadcastReceiver, lalu bagaimana?
Anda dapat menggunakan JobScheduler atau lebih baik jika WorkManager (Permintaan Berkala). Mengapa Berkala karena jika itu adalah OneTimeRequest maka itu hanya akan dapat dijalankan sekali dan terus mendengarkan saat aplikasi Anda berada di latar depan.
Dokumentasi mengatakan:
Setelah aplikasi dimatikan atau dihapus dari daftar aplikasi terbaru, networkCallback tidak akan dapat mendengarkan.
Jadi, Anda memerlukan pekerjaan berkala seperti itu agar aplikasi terus mendengarkan. Berapa durasinya? Itu terserah Anda dan tergantung kasus ke kasus.
Saya tahu ini cara yang agak jelek tapi begitulah adanya. Salah satu tantangannya adalah jika perangkat pengguna dalam mode Istirahatkan atau aplikasi dalam Status Siaga, pekerjaan Anda mungkin tertunda.
sumber
Saat kami mendaftarkan callback jaringan menggunakan
registerNetworkCallback
metode ini, terkadang tidak terpicu dan terkadang memicu positif palsu:onAvailable
metode tersebut akan dipicu.NetworkCallback
disebut (sangat aneh karena hal. 1)onAvailable
pemicu metode koneksi internet . Dan saya pikir itu adalah perilaku positif palsu karena kami mengharapkan pengamatan koneksi internet.Seperti yang Anda lihat pada kode di bawah ini secara default koneksi internet tersedia dan hanya terpicu jika berubah. Tidak ada pemicu positif palsu.
Rangkum saja ini dan jawaban ini (tetapi hanya untuk 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) } }
Bagaimana menyediakan dependensi
@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)
Dan cara menggunakan:
@Inject lateinit var connectionManager: ConnectionManager //.... viewLifecycleOwner.observe(connectionManager) { isInternetAvailable -> // TODO }
sumber