ConnectivityManager.CONNECTIVITY_ACTION tidak digunakan lagi

95

Di Android N, disebutkan di situs resminya bahwa "Aplikasi yang menargetkan Android N tidak menerima siaran CONNECTIVITY_ACTION". Dan disebutkan juga bahwa JobSchedulerdapat digunakan sebagai alternatif. Tetapi JobSchedulertidak memberikan perilaku yang persis sama seperti CONNECTIVITY_ACTIONsiaran.

Di aplikasi Android saya, saya menggunakan siaran ini untuk mengetahui status jaringan perangkat. Saya ingin tahu apakah status ini CONNECTINGatau CONNECTEDdengan bantuan CONNECTIVITY_ACTIONpenyiaran dan paling sesuai untuk kebutuhan saya.

Sekarang sudah tidak digunakan lagi, dapatkah seseorang menyarankan saya pendekatan alternatif untuk mendapatkan status jaringan saat ini?

Raghuram db
sumber
10
Dan bagaimana jika OP suatu hari nanti menginginkan beberapa perilaku yang memerlukan peningkatan targetSdkVersionke N atau nanti?
Michael
1
Yah, saya juga tahu bahwa jika saya tidak menargetkan aplikasi saya ke Android NI akan menerima siaran. Tetapi aplikasi saya harus mendukung Android N. Bagaimana saya bisa mendapatkan perilaku siaran yang sama di Android N? Apakah ada pendekatan lain yang bisa saya coba? @DavidWasser
Raghuram db
Kadang-kadang saya pikir lebih masuk akal jika khawatir tentang masa depan di masa depan. Ini murni pendekatan pragmatis untuk pemrograman. Tentu saja, Anda selalu dapat mencoba memastikan bahwa kode Anda tidak menggunakan fitur yang sudah usang. Di sisi lain, fitur yang tidak digunakan lagi biasanya bertahan untuk waktu yang lama dan mungkin aplikasi Anda akan berakhir sebelum fitur yang tidak digunakan lagi hilang. Android N sangat baru sehingga saya tidak akan menghabiskan banyak waktu untuk mengkhawatirkannya. Namun. Hanya 2 sen saya. Harap dicatat bahwa saya menulis komentar untuk pertanyaan dan tidak menyarankan bahwa "jangan lakukan itu" adalah jawaban yang valid.
David Wasser
2
@Raghuramdb Aplikasi Anda dapat berjalan di Android N meskipun Anda tidak menargetkan aplikasi ke Android N. Anda hanya perlu menargetkan Android N jika ingin menggunakan fitur yang hanya tersedia di Android N.
David Wasser
2
Anda masih bisa menggunakan BroadcastReceiverdengan android.net.conn.CONNECTIVITY_CHANGEfilter maksud bahkan saat menargetkan API29, Anda hanya perlu mendaftarkannya Application.OnCreate. Anda tidak akan mendapatkan pembaruan apa pun saat aplikasi ditutup.
Pierre

Jawaban:

101

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
   }
}
Amokrane Chentir
sumber
2
Karena teknik ini hanya akan berfungsi, jika aplikasi berjalan di latar depan. Apakah itu berarti, kita tidak lagi memiliki kemampuan untuk mendengarkan peristiwa koneksi, saat aplikasi tidak berjalan di latar depan? Memiliki <action android: name = "android.net.conn.CONNECTIVITY_CHANGE" /> di manifest.xml tidak lagi berpengaruh di Android N.
Cheok Yan Cheng
2
@CheokYanCheng AFAIK itu benar. Anda harus memiliki proses yang berjalan di latar depan untuk mendengarkan peristiwa konektivitas. Tampaknya asumsi yang dibuat oleh para insinyur kerangka Android adalah mendengarkan peristiwa konektivitas sebagian besar dilakukan untuk mengetahui kapan harus mulai menyinkronkan data antara klien & server. Oleh karena itu, JobScheduler adalah cara yang disarankan untuk kasus penggunaan tersebut.
Amokrane Chentir
28
lol apa-apaan, 10 pembaruan android lainnya dan yang bisa kami tulis hanyalah aplikasi hello world
DennisVA
1
Apakah saya perlu membatalkan registrasi NetworkCallback (misalnya, dalam metode onDestroy aktivitas)?
Ruslan Berozov
2
@Ruslan ya tentu saja, atau Anda akan membocorkan apa pun yang terdaftar
DennisVA
35

Saya akan memperbarui Sayem'sjawaban 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.

Kebab Krabby
sumber
2
Solusi luar biasa!
kotak
2
Solusi yang sangat baik untuk menggunakan data langsung dan mendukung versi lama
Prakash Shukla
Ini adalah solusi terbaik yang tersedia di internet.
Karan Sharma
Solusi yang sangat bagus! TAPI ada satu yang "tidak" - ini adalah cara yang salah untuk menggunakan metode onAvailable (jaringan: Jaringan?), Karena ia memanggil bahkan Internet tidak tersedia. Lebih baik menggunakan onCapabilitiesChanged (network: Network, networkCapabilities: NetworkCapabilities) dan memeriksa networkCapabilities.hasCapability (NET_CAPABILITY_INTERNET) dan networkCapabilities.hasCapability (NET_CAPABILITY_VALIDATED).
DmitryKanunnikoff
bagaimana cara mendapatkan ip dan tipe jaringan dalam basis kode ini?
A_rmas
29

Dokumentasi untuk Android N menyatakan:

Aplikasi yang menargetkan Android N tidak menerima siaran CONNECTIVITY_ACTION, meskipun memiliki entri manifes untuk meminta notifikasi peristiwa ini. Aplikasi yang berjalan di latar depan masih bisa mendengarkan CONNECTIVITY_CHANGE di utas utamanya jika mereka meminta notifikasi dengan BroadcastReceiver.

Artinya, Anda masih bisa mendaftarkan BroadcastReceiverjika aplikasi Anda berjalan di latar depan, untuk mendeteksi perubahan dalam konektivitas jaringan.

David Wasser
sumber
Tangkapan halus yang bagus :)
Amokrane Chentir
apakah ini berarti aplikasi akan berhenti menerima siaran setelah tidak berada di latar depan? (jadi saya tidak bisa mendengarkannya dalam layanan misalnya?)
sundie
1
Saya tidak tahu pasti, saya perlu mengujinya untuk memastikan. Namun, membaca dokumentasi akan terlihat bahwa jika aplikasi Anda tidak berada di latar depan, Anda tidak akan mendapatkan siarannya Intent.
David Wasser
2
Tetapi untuk mendeteksi perubahan konektivitas di latar belakang adalah wajib untuk setiap aplikasi sip (VoIP) ... aplikasi tersebut biasanya berjalan di latar belakang selama berhari-hari dan melompat ke latar depan hanya jika ada panggilan masuk (seperti dialer telepon Anda) .. Aplikasi tersebut perlu terhubung kembali secara otomatis di latar belakang. Ini membunuh semua aplikasi (yang tidak memiliki server push sendiri) dari platform android karena akan offline. selalu.
Grisgram
cukup gunakan layanan firebase push.
Pierre
21

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 
})
Sayem
sumber
btw, Anda tidak perlu mendefinisikan IntentFiltersecara eksplisit. Seperti:var intentFilter = IntentFilter(CONNECTIVITY_ACTION)
Ryan Amaral
terima kasih atas saran Anda. Saya tidak ingin membuat objek setiap saat di onActive.
Sayem
Maksud saya, 2 variabel / properti global ( intentFilterdan connectivityManager) Anda tidak perlu secara eksplisit menentukan jenisnya ( IntentFilterdan ConnectivityManagermasing - masing).
Ryan Amaral
Sebagian besar barang (sayangnya) usang ..
Andrew
7

Saya mengalami masalah yang sama beberapa hari yang lalu dan saya memutuskan untuk menggunakan perpustakaan ini Android-Job

Library ini menggunakan JobSchedular, GcmNetworkManagerdan BroadcastReceiverbergantung 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();
Noman Rafique
sumber
1
saya telah mencoba penjadwal yang sama dan mendapatkan pengecualian seperti ini Anda mencoba untuk membangun pekerjaan tanpa kendala, ini tidak diperbolehkan. bisakah Anda membantu kami menyelesaikan ini ??
Sanket Kachhela
Menggunakan Android-Job untuk tujuan ini sebenarnya bukan solusi yang sangat bagus. Ini dimaksudkan untuk menjalankan sesuatu pada waktu tertentu, baik sekali atau secara berkala. Ini dimaksudkan untuk menghadirkan dukungan kompatibilitas retro untuk Alarm dan semacamnya. Ini bertentangan dengan keseluruhan ide mengapa API berubah, dan membaca: developer.android.com/training/monitoring-device-state/… Anda dapat dengan cepat memahami alasannya.
pedronveloso
satu-satunya masalah adalah bahwa di Android N itu hanya dapat dijadwalkan minimal 15 menit di masa depan
Fire Crow
4

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_STATEizin di AndroidManifest.xml Anda :

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

Saya menantikan untuk membaca komentar dan peningkatan yang bermanfaat dari Anda.

JJD
sumber
1
Saya harus mengubah sesuatu agar callback dapat "menyentuh tampilan" dalam Aktivitas (konteks) di utas utama: (context as AppCompatActivity).runOnUiThread(object: Runnable{ override fun run() { onConnectionAvailable.invoke() } })bukan onConnectionAvailable.invoke(). Sama untuk onConnectionLost.invoke().
Андрей Воробьев
Ya, tergantung pada kasus penggunaan Anda, Anda mungkin perlu mengganti utas. Saya tidak akan menjadikannya bagian dari kelas, tetapi membiarkan konsumen kelas mengurus ini. Tapi terima kasih atas petunjuknya.
JJD
4

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"}")
})
DmitryKanunnikoff
sumber
2

Aplikasi yang menargetkan Android N (Nougat) tidak menerima CONNECTIVITY_ACTIONsiaran yang ditentukan dalam manifes (lihat Svelte ).

Solusi yang memungkinkan:

Lihat juga Android O - Mendeteksi perubahan konektivitas di latar belakang

rds
sumber
1

Saya setuju dengan jawaban yang disarankan oleh @rds.

Perlu diingat bahwa CONNECTIVITY_ACTION sudah tidak digunakan lagi di API level 28.

Jika Anda memiliki persyaratan bahwa status Wifi (sambungkan / putuskan) harus terdeteksi meskipun aplikasi dimatikan dan Anda ingin menargetkan versi terbaru, maka Anda tidak punya banyak pilihan.

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:

Callback akan terus dipanggil hingga aplikasi keluar atau link #unregisterNetworkCallback (NetworkCallback)} dipanggil.

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.

Wahib Ul Haq
sumber
Juga perlu diingat bahwa pada beberapa EMUI yang sangat disesuaikan, pengelola kerja OS Android MIUI (tugas berkala) tidak harus selalu berfungsi dengan benar.
Kebab Krabby
1

Saat kami mendaftarkan callback jaringan menggunakan registerNetworkCallbackmetode ini, terkadang tidak terpicu dan terkadang memicu positif palsu:

  1. Jika kita memulai aplikasi dengan koneksi internet, onAvailablemetode tersebut akan dipicu.
  2. Tetapi jika tidak ada koneksi internet pada perangkat saat kita memulai aplikasi tidak ada yang NetworkCallbackdisebut (sangat aneh karena hal. 1)
  3. Jika kita memiliki koneksi wifi tetapi tanpa onAvailablepemicu 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 
}
bitvale
sumber