连接管理器.CONNECTIVITY_ACTION已被弃用

118
在Android N中,官方网站提到“面向Android N的应用程序不会接收CONNECTIVITY_ACTION广播”。并且还提到可以使用JobScheduler作为替代方法。但是JobScheduler并不能完全提供与CONNECTIVITY_ACTION广播相同的行为。
在我的Android应用程序中,我使用此广播来了解设备的网络状态。我想通过CONNECTIVITY_ACTION广播知道这个状态是CONNECTING还是CONNECTED,它最适合我的需求。
现在它被弃用了,有人能建议我获取当前网络状态的替代方法吗?

10
如果有一天原始发布者想要实现某些需要将“targetSdkVersion”提升到N或更高版本的行为,该怎么办? - Michael
1
我也知道如果我不将我的应用程序针对Android N进行定位,那么我将无法接收广播。但是我的应用程序需要支持Android N。我该如何在Android N中获得相同的广播行为?还有其他方法可以尝试吗? @ DavidWasser - Raghuram db
2
@Raghuramdb即使你的应用程序不是针对Android N开发,它仍然可以在Android N上运行。只有当你想使用仅适用于Android N的功能时,才需要将应用程序定向到Android N。 - David Wasser
2
即使针对API29,您仍然可以使用BroadcastReceiverandroid.net.conn.CONNECTIVITY_CHANGE意图过滤器,只需在Application.OnCreate中注册即可。当应用程序关闭时,您将不会收到任何更新。 - Pierre
1
@SimpleGuy 实现一个应用程序类 public class MainApplication extends Application implements Application.ActivityLifecycleCallbacks {...},重写 public void onCreate() { 方法,在其中调用 IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); registerReceiver(new MyBroadcastReceiver(), intentFilter);。别忘了在你的 AndroidManifest.xml 中添加 android:name 到应用程序 <application android:name=".MainApplication" ...> - Pierre
显示剩余2条评论
15个回答

116

即将被废弃的是后台应用程序接收网络连接状态更改的功能。

正如David Wasser所说,如果满足以下条件仍然可以收到连接更改的通知:

  • 应用程序或服务处于“前台”状态(例如应用程序视图处于打开状态,或前台服务的通知已弹出)。
  • 并且您已经使用其context在程序中注册了接收器,而不是在清单文件中进行注册,例如:
    IntentFilter filter = new IntentFilter();
    @SuppressWarnings({"deprecation", "RedundantSuppression"})
    String action = ConnectivityManager.CONNECTIVITY_ACTION;
    filter.addAction(action);
    myContext.registerReceiver(myReceiver, filter);
    

替代方案

如果您不喜欢@SuppressWarnings部分,或者认为甚至编程注册接收器可能在某些未来的Android版本中停止工作,则继续阅读:

您可以改用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,
       // or instead, override `onUnavailable()` or `onLost()`,
       // (to detect absense of network).
   }
}

3
由于这种技术只能在应用程序在前台运行时才能工作,这是否意味着当应用程序不在前台运行时,我们不再能够监听连接事件?在manifest.xml中添加<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />在Android N中不再起作用。 - Cheok Yan Cheng
3
据我所知,这是正确的。您需要运行在前台的进程来监听连接事件。Android框架工程师做出的假设似乎是,监听连接事件主要是为了知道何时开始在客户端和服务器之间同步数据。因此,JobScheduler是该用例的推荐方式。 - Amokrane Chentir
55
笑死,再来10个安卓更新,我们只能写一个hello world的应用程序了。 - DennisVA
2
我需要注销NetworkCallback吗(例如在Activity的onDestroy方法中)? - Ruslan Berozov
2
@Ruslan 当然可以,否则你将泄露所有已注册的内容。 - DennisVA
显示剩余3条评论

56

我会更新 Sayem 的回答,以解决它向我展示的 lint 问题。

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 提供的解决方案。


3
惊人的解决方案! - box
3
非常好的解决方案是使用实时数据并支持旧版本。 - Prakash Shukla
2
这是互联网上最好的解决方案。 - Karan Sharma
3
非常好的解决方案!但是有一个“不”——错误地使用onAvailable(network:Network?)方法,因为即使Internet不可用也会调用它。更好的方法是使用onCapabilitiesChanged(network:Network,networkCapabilities:NetworkCapabilities),并检查networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)和networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)。 - DmitryKanunnikoff
1
@SimpleGuy 是的,你可以在IDE中将它从Kotlin反编译为Java。 默认快捷键:CMD + OPTION + SHIFT + D - Kebab Krabby
显示剩余9条评论

30

Android N的文档说明如下:

针对Android N的应用程序即使具有请求接收网络连接状态改变通知的清单条目,也不会接收到CONNECTIVITY_ACTION广播。仍在前台运行的应用程序可以通过BroadcastReceiver在主线程上监听CONNECTIVITY_CHANGE以接收通知。

这意味着,如果你的应用程序在前台运行,仍然可以注册一个BroadcastReceiver来检测网络连接状态的更改。


不错的细微发现 :) - Amokrane Chentir
这是否意味着一旦应用程序不在前台,它将停止接收广播?(因此我不能在服务中监听它?) - sundie
1
我不确定,需要测试才能确定。然而,阅读文档似乎表明,如果您的应用程序不在前台,则不会收到广播“Intent”。 - David Wasser
2
但是在后台检测连接变化对于任何SIP应用程序(VoIP)都是必要的...这些应用程序通常在后台运行数天,只有在有电话呼入时才会跳转到前台(就像您手机的拨号器一样)...这些应用程序需要在后台自动重新连接。这将使所有那些没有自己的推送服务器的应用程序从Android平台上下线。 - Grisgram
天啊,这个引用太令人困惑了!我花了几个小时搜索如何监听 "CONNECTIVITY_CHANGE" 而不是过时的方法,但最终发现那只是 "CONNECTIVITY_ACTION" 变量的值。我希望人们停止复制/粘贴文档,如果那真的有用,我们就不会在这里了。 - Top-Master
显示剩余2条评论

25

请先查看@Amokrane Chentir的Android N支持答案。

对于那些想要在所有API级别上提供支持并在用户界面中观察它的人,请检查下面的代码。

NetworkConnection的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中观察(Activity/Fragment):

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

顺便提一下,您不需要显式定义IntentFilter。像这样:var intentFilter = IntentFilter(CONNECTIVITY_ACTION) - Ryan Amaral
感谢您的建议。我不想在onActive中每次都创建对象。 - Sayem
我的意思是,你不需要显式地定义intentFilterconnectivityManager这两个全局变量/属性的类型(分别为IntentFilterConnectivityManager)。 - Ryan Amaral

9

基于 @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"}")
})

2
到目前为止最有帮助的答案。这个解决方案唯一的问题是,使用ConnectivityManager.CONNECTIVITY_ACTION和Broadcast Receiver时,当应用程序在飞行模式下启动或移动数据/WIFI关闭时,可能会发出错误值。问题在于,在这种情况下,ConnectivityManager.NetworkCallback()不会触发。为了克服这个问题,您可以将ConnectivityWatcher的默认值设置为“false”,或者观察并将此值传递给ViewModel中的MutableLiveData,并使用协程在一定延迟(比如1000ms)后将该值设置为false,如果它为空。 - Alex Shevchyshen
@AlexShevchyshen非常感谢您的评论! - DmitryKanunnikoff

7
我几天前遇到了同样的问题,我决定使用这个库Android-Job
该库根据应用程序运行的Android版本使用JobSchedularGcmNetworkManagerBroadcastReceiver
启动作业非常容易。
new JobRequest.Builder(DemoSyncJob.TAG)
            .setRequiresCharging(true)
            .setRequiresDeviceIdle(false)
            .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) // this is what gets the job done
            .build()
            .schedule();

1
我已经尝试了同样的调度器,但出现了以下异常: 您正在尝试构建无约束条件的作业,这是不允许的。 请问您能帮我们解决吗? - Sanket Kachhela
使用Android-Job来实现这个目的并不是一个很好的解决方案。它旨在在指定的时间运行一次或定期运行。它旨在为警报等提供向后兼容性支持。这与API更改的整个理念背道而驰,阅读以下内容:https://developer.android.com/training/monitoring-device-state/connectivity-monitoring.html您可以快速了解原因。 - pedronveloso
唯一的问题是在Android N中,它只能被安排在未来最少15分钟。 - Fire Crow

4
我编写了一个基于Sayam的答案的Kotlin实现,但没有使用LiveData。我决定调用(此时)最新的API方法({{link2: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()

不要忘记在您的AndroidManifest.xml中添加ACCESS_NETWORK_STATE权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

我期待着从您那里阅读到有帮助的评论和改进意见。


1
我必须更改一些内容,以便回调能够在主线程上“触摸视图”在Activity(上下文)中:(context as AppCompatActivity).runOnUiThread(object: Runnable{ override fun run() { onConnectionAvailable.invoke() } }) 而不是 onConnectionAvailable.invoke()。对于 onConnectionLost.invoke() 也是如此。 - Андрей Воробьев
是的,根据您的用例,您可能需要切换线程。我不会将其作为类的一部分,而是让类的使用者来处理它。但是感谢您的提示。 - JJD

3

我决定检查这里的解决方案here,并进行了一些改进和示例。我还使其避免了多次设置相同的值,因此观察者不会一次又一次地获取相同的值:

ConnectionLiveData.kt

class ConnectionLiveData(private val context: Context) : LiveData<Boolean>() {
    private val handler = Handler(Looper.getMainLooper())
    private lateinit var connectivityManagerCallback: ConnectivityManager.NetworkCallback
    private lateinit var networkReceiver: BroadcastReceiver

    init {
        when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> {
                connectivityManagerCallback = object : ConnectivityManager.NetworkCallback() {
                    @AnyThread
                    override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
                            networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
                            setNewValue(true)
                        }
                    }

                    override fun onAvailable(network: Network) {
                        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1)
                            setNewValue(true)
                    }

                    override fun onLost(network: Network) {
                        setNewValue(false)
                    }
                }
            }
            else -> {
                networkReceiver = object : BroadcastReceiver() {
                    override fun onReceive(context: Context, intent: Intent) {
                        updateConnection()
                    }
                }
            }
        }
    }

    /**this prevents observers to get multiple times the same value*/
    @AnyThread
    private fun setNewValue(isConnected: Boolean) {
        handler.removeCallbacksAndMessages(null)
        if (isUiThread()) {
            if (value != isConnected)
                @SuppressLint("WrongThread")
                value = isConnected
            return
        }
        handler.post {
            if (value != isConnected)
                value = isConnected
        }
    }

    @UiThread
    override fun onActive() {
        super.onActive()
        val connectivityManager: ConnectivityManager = context.getSystemService()!!
        updateConnection()
        when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> connectivityManager.registerDefaultNetworkCallback(connectivityManagerCallback, handler)
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> connectivityManager.registerDefaultNetworkCallback(connectivityManagerCallback)
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {
                val networkRequest =
                    NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build()
                connectivityManager.registerNetworkCallback(networkRequest, connectivityManagerCallback)
            }
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> {
                val networkRequest =
                    NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build()
                connectivityManager.registerNetworkCallback(networkRequest, connectivityManagerCallback)
            }
            else -> {
                @Suppress("DEPRECATION") context.registerReceiver(networkReceiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
            }
        }
    }

    override fun onInactive() {
        super.onInactive()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            val connectivityManager: ConnectivityManager = context.getSystemService()!!
            connectivityManager.unregisterNetworkCallback(connectivityManagerCallback)
        } else {
            context.unregisterReceiver(networkReceiver)
        }
    }


    @Suppress("DEPRECATION")
    private fun updateConnection() {
        setNewValue(isConnectedToInternet(context))
    }

    companion object {
        @JvmStatic
        fun isUiThread(): Boolean =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) Looper.getMainLooper().isCurrentThread else Thread.currentThread() === Looper.getMainLooper().thread

        private fun isConnectedToInternet(context: Context): Boolean {
            val connectivityManager: ConnectivityManager = context.getSystemService()!!
            val activeNetwork: NetworkInfo? = connectivityManager.activeNetworkInfo
            return activeNetwork?.isConnectedOrConnecting == true
        }
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val textView = findViewById<TextView>(R.id.textView)
        ConnectionLiveData(this).observe(this) {
            textView.text = if (it) "connected" else "disconnected"
            Log.d("AppLog", "connected?$it")
        }
        val internetSettings = findViewById<View>(R.id.internetSettings)
        internetSettings.isVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
        internetSettings.setOnClickListener {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
                startActivity(Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY))
        }
        findViewById<View>(R.id.wifiSettings).setOnClickListener {
            startActivity(Intent(Settings.ACTION_WIFI_SETTINGS))
        }
        findViewById<View>(R.id.mobileDataSettings).setOnClickListener {
            startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
        }
    }
}

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"
    android:orientation="vertical" tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" />

    <Button
        android:id="@+id/internetSettings" android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="internet settings" />

    <Button
        android:id="@+id/wifiSettings" android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="wifi settings" />

    <Button
        android:id="@+id/mobileDataSettings" android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="mobile-data settings" />
</LinearLayout>

清单需要:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

请注意,当飞行模式被打开时,它会出现一个罕见的问题,暂时认为没有连接,然后相反,但随后又回到没有连接的状态。有关此问题的报告在这里

迄今为止我在网上找到的完美答案。+1。 - Shihab Uddin
我不确定为什么这个答案没有更多的赞,因为它非常有效。我在开启/关闭WiFi和飞行模式的情况下进行了测试,它在所有情况下都完美运行。谢谢你!!!如果可以的话,我会点赞两次。 - Lance Samaria

2

1

这是我的Java解决方案!适用于从安卓LOLLIPOP到安卓S及以上版本

@RequiresApi (api = Build.VERSION_CODES.LOLLIPOP)
public final class NetworkWatcher extends LiveData<Boolean> {
//    Variables
private final Context context;

private final ConnectivityManager connectivityManager;
private ConnectivityManager.NetworkCallback networkCallback;

private NetworkRequest networkRequest;

private NetworkWatcher.NetworkStateWatcherReceiver networkStateWatcherReceiver;


//    Constructors
public NetworkWatcher(@NonNull Context context){
    this.context = context;

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
        this.connectivityManager = context.getSystemService(ConnectivityManager.class);

        this.networkCallback = new ConnectivityManager.NetworkCallback(){
            @Override
            public void onLost(@NonNull Network network) {
                NetworkWatcher.super.postValue(false);
            }

            @Override
            public void onCapabilitiesChanged(@NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
                NetworkWatcher.super.postValue(
                        networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
                                networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
                );
            }
        };
        this.networkRequest = new NetworkRequest.Builder()
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
                .build();
    } else {
        this.networkStateWatcherReceiver = new NetworkStateWatcherReceiver();
        this.networkStateWatcherReceiver.setOnNetworkChangedListener(NetworkWatcher.super::postValue);
    }
}


//    Override methods
@Override
protected void onActive() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
            this.connectivityManager.registerBestMatchingNetworkCallback(this.networkRequest, this.networkCallback, null);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
            this.connectivityManager.registerDefaultNetworkCallback(this.networkCallback);
        } else {
            this.connectivityManager.registerNetworkCallback(this.networkRequest, this.networkCallback);
        }
    } else {
        this.context.registerReceiver(
                this.networkStateWatcherReceiver,
                new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        );
    }
}

@Override
protected void onInactive() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
        this.connectivityManager.unregisterNetworkCallback(this.networkCallback);
    } else {
        this.context.unregisterReceiver(this.networkStateWatcherReceiver);
    }
}



//    Inner method classes
@TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
private static final class NetworkStateWatcherReceiver extends BroadcastReceiver {
    //        Variables
    private NetworkStateWatcherReceiver.OnNetworkChangedListener onNetworkChangedListener;


    //        Constructors
    public NetworkStateWatcherReceiver() {
    }


    //        Override methods
@Override
    public void onReceive(@NonNull Context context, @NonNull Intent intent) {
        if (this.onNetworkChangedListener != null) {
            boolean isConnected = this.isConnected(context);
            this.onNetworkChangedListener.onNetworkChangedListener(isConnected);
        }
    }


    //        Methods
    private boolean isConnected(@NonNull Context context){
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

        return networkInfo != null && networkInfo.isConnected();
    }
    public void setOnNetworkChangedListener(@Nullable OnNetworkChangedListener onNetworkChangedListener) {
        this.onNetworkChangedListener = onNetworkChangedListener;
    }



    //        Inner interfaces
private interface OnNetworkChangedListener{
        void onNetworkChangedListener(boolean isConnected);
    }
}
}

在你的主活动中的onCreate方法中使用它

new NetworkWatcher(this).observe(this, aBoolean -> {
        if (aBoolean){
            // Internet connection available
        } else {
           // No internet connection
        }
    });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接