睿诚科技协会

Android如何准确判断网络连接状态?

你应该用哪个方法?

Android 版本 推荐方法 优点 缺点
API 23 (Android 6.0) 及以上 ConnectivityManager.NetworkCallback 最佳实践、异步、实时、精确 实现稍复杂
API 21 (Android 5.0) - API 22 ConnectivityManager + NetworkRequest 异步、实时 API 21-22 的回调不如 API 23+ 稳定
API 1 - API 20 ConnectivityManager + getNetworkInfo() 简单直接、同步、代码少 已废弃、不精确、无法实时更新

使用 NetworkCallback (推荐,API 23+)

这是目前最推荐、最现代的方法,它采用异步回调的方式,可以让你精确地监听网络连接、丢失和状态变化,非常高效且不会阻塞主线程。

Android如何准确判断网络连接状态?-图1
(图片来源网络,侵删)

添加网络权限

AndroidManifest.xml 中,确保你有访问网络的权限。

<!-- 允许应用完全访问网络 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 允许应用查看网络状态 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

代码实现

创建一个管理类来封装网络监听逻辑。

import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Build
class NetworkManager(private val context: Context) {
    // 定义网络状态变化的回调接口
    interface NetworkStateListener {
        fun onAvailable()
        fun onLost()
    }
    private var listener: NetworkStateListener? = null
    private val connectivityManager =
        context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    // 用于注册网络请求的回调
    private val networkCallback = object : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network) {
            super.onAvailable(network)
            // 网络可用时调用
            listener?.onAvailable()
        }
        override fun onLost(network: Network) {
            super.onLost(network)
            // 网络丢失时调用
            listener?.onLost()
        }
    }
    /**
     * 注册网络监听
     */
    fun register(listener: NetworkStateListener) {
        this.listener = listener
        val request = NetworkRequest.Builder()
            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
            .build()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // API 24+ 推荐 registerDefaultNetworkCallback
            connectivityManager.registerDefaultNetworkCallback(networkCallback)
        } else {
            // API 23 使用 registerNetworkCallback
            connectivityManager.registerNetworkCallback(request, networkCallback)
        }
    }
    /**
     * 取消网络监听
     */
    fun unregister() {
        connectivityManager.unregisterNetworkCallback(networkCallback)
        listener = null
    }
}

在 Activity 或 ViewModel 中使用

import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity(), NetworkManager.NetworkStateListener {
    private lateinit var networkManager: NetworkManager
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        networkManager = NetworkManager(this)
        networkManager.register(this)
    }
    override fun onAvailable() {
        // 在主线程中执行UI更新
        runOnUiThread {
            Toast.makeText(this, "网络已连接", Toast.LENGTH_SHORT).show()
            // 可以在这里开始加载网络数据
        }
    }
    override fun onLost() {
        runOnUiThread {
            Toast.makeText(this, "网络已断开", Toast.LENGTH_SHORT).show()
            // 可以在这里停止加载,显示错误提示
        }
    }
    override fun onDestroy() {
        super.onDestroy()
        // 一定要在 Activity 销毁时取消注册,否则会导致内存泄漏
        networkManager.unregister()
    }
}

同步检查当前网络状态 (适用于所有版本,但旧方法)

如果你只是想在某个操作(如点击按钮)前快速检查一下当前是否有网络,可以使用同步方法,注意,旧方法 (getNetworkInfo) 已被废弃,新方法 (getNetworkCapabilities) 更精确。

检查是否有任何类型的网络连接

import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
fun isNetworkConnected(context: Context): Boolean {
    val connectivityManager =
        context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        // API 23+ 的方法
        val activeNetwork = connectivityManager.activeNetwork
        val caps = connectivityManager.getNetworkCapabilities(activeNetwork)
        return caps != null && (
                caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
                caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
                caps.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) ||
                caps.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)
            )
    } else {
        // API 22 及以下的方法 (已废弃)
        @Suppress("DEPRECATION")
        val networkInfo = connectivityManager.activeNetworkInfo
        return networkInfo != null && networkInfo.isConnected
    }
}

检查是否是 Wi-Fi 或移动数据

import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
fun isWifiConnected(context: Context): Boolean {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        val connectivityManager =
            context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val caps = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
        return caps != null && caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
    } else {
        @Suppress("DEPRECATION")
        val networkInfo = (context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager)
            .getNetworkInfo(ConnectivityManager.TYPE_WIFI)
        return networkInfo != null && networkInfo.isConnected
    }
}
fun isMobileDataConnected(context: Context): Boolean {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        val connectivityManager =
            context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val caps = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
        return caps != null && caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
    } else {
        @Suppress("DEPRECATION")
        val networkInfo = (context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager)
            .getNetworkInfo(ConnectivityManager.TYPE_MOBILE)
        return networkInfo != null && networkInfo.isConnected
    }
}

重要注意事项

  1. 网络 ≠ 可以上网

    Android如何准确判断网络连接状态?-图2
    (图片来源网络,侵删)
    • isNetworkConnected() 只能判断设备是否连接到了一个网络(如 Wi-Fi 或移动数据),它不能保证这个网络可以访问互联网。
    • 你可能连上了一个需要认证的酒店 Wi-Fi,但没有登录,isNetworkConnected() 返回 true,但你无法上网。
    • 要真正判断是否能上网,最可靠的方法是尝试发起一个网络请求(如访问一个可靠的轻量级服务器)。
  2. 后台网络限制

    • 从 Android 6.0 (API 23) 开始,系统对后台应用的网络访问有严格限制,如果你的应用在后台,NetworkCallback 的回调可能会被延迟或不会触发,前台服务是解决此问题的一种方式。
  3. Wi-Fi 状态广播

    • 除了上述方法,你还可以监听系统广播 ConnectivityManager.CONNECTIVITY_ACTION 来获取网络状态变化,但这种方法不如 NetworkCallback 精准和高效,因为它只告诉你“网络状态变了”,但不知道具体变成了什么。
  4. 权限声明

    • 对于 Android 10 (API 29) 及更高版本,如果需要查询 Wi-Fi 状态,你还需要在 AndroidManifest.xml 中声明:
      <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

最佳实践建议

  • 对于需要实时响应网络变化的场景(如下载管理器、聊天应用):使用 NetworkCallback,在 Application 类或 ViewModel 中进行管理,确保在组件销毁时正确取消注册。
  • 对于只需要在执行特定操作前检查的场景(如点击“上传”按钮):使用同步检查方法 (isNetworkConnected),并给用户友好的提示(如“请检查网络连接后重试”)。
Android如何准确判断网络连接状态?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇