睿诚科技协会

Android如何实时监控网络状态变化?

核心概念

在开始之前,你需要了解 Android 网络相关的几个核心类和概念:

Android如何实时监控网络状态变化?-图1
(图片来源网络,侵删)
  1. ConnectivityManager: 这是系统级的服务,负责管理网络连接,所有关于网络状态的查询和请求都通过它来进行。
  2. NetworkCapabilities: 描述一个网络连接的能力,
    • NET_CAPABILITY_INTERNET: 是否能访问互联网。
    • NET_CAPABILITY_VALIDATED: 连接是否已验证(已成功解析 DNS)。
    • NET_CAPABILITY_NOT_METERED: 是否是非按流量计费的网络(如 Wi-Fi)。
    • NET_CAPABILITY_NOT_ROAMING: 是否是非漫游网络。
  3. NetworkRequest: 用于请求一个满足特定条件的网络,你可以用它来注册一个网络回调。
  4. NetworkCallback: 这是一个抽象类,你通过实现它来接收网络状态变化的通知,这是现代推荐的方式

使用 ConnectivityManager.NetworkCallback (推荐)

这是 Android 6.0 (API 23) 引入的、最灵活、最强大的方式,它允许你异步地、精确地监听网络状态的变化,而不会阻塞主线程。

优点

  • 异步非阻塞: 不会像广播那样可能造成 ANR (Application Not Responding)。
  • 精确控制: 你可以精确地定义你关心的网络类型(只关心 Wi-Fi 或移动数据)。
  • 高效: 只在你注册的条件下发生变化时才会收到回调,减少了不必要的唤醒和电量消耗。

实现步骤

  1. 获取 ConnectivityManager 实例
  2. 创建一个 NetworkRequest,指定你关心的网络条件。
  3. 创建 NetworkCallback 的子类,并重写 onAvailable(), onLost(), onUnavailable() 等方法。
  4. 注册和注销回调

代码示例

添加权限 (AndroidManifest.xml)

<!-- 检查网络状态所需 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 如果你的 App 需要 Internet 权限,最好也加上 -->
<uses-permission android:name="android.permission.INTERNET" />

创建一个 NetworkHelper 工具类

import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
public class NetworkHelper {
    private final ConnectivityManager connectivityManager;
    private final NetworkCallback networkCallback;
    public NetworkHelper(Context context) {
        this.connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        this.networkCallback = new NetworkCallback();
    }
    /**
     * 注册网络状态监听
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public void registerNetworkCallback() {
        if (connectivityManager != null) {
            // 创建一个网络请求,这里我们只关心能访问互联网的网络
            NetworkRequest request = new NetworkRequest.Builder()
                    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                    .build();
            // 注册回调
            connectivityManager.registerNetworkCallback(request, networkCallback);
        }
    }
    /**
     * 注销网络状态监听
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public void unregisterNetworkCallback() {
        if (connectivityManager != null) {
            connectivityManager.unregisterNetworkCallback(networkCallback);
        }
    }
    /**
     * 检查当前是否有网络连接
     */
    public boolean isNetworkAvailable() {
        if (connectivityManager == null) {
            return false;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Network network = connectivityManager.getActiveNetwork();
            if (network == null) {
                return false;
            }
            NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network);
            return capabilities != null && 
                   capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
                   capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
        } else {
            // 对于低于 Android 8.0 的版本,使用旧的方法
            android.net.NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
            return networkInfo != null && networkInfo.isConnected();
        }
    }
    /**
     * 获取当前网络类型
     */
    public String getNetworkType() {
        if (!isNetworkAvailable()) {
            return "No Connection";
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Network network = connectivityManager.getActiveNetwork();
            if (network == null) return "Unknown";
            NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network);
            if (capabilities == null) return "Unknown";
            if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                return "Wi-Fi";
            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                return "Cellular";
            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
                return "Ethernet";
            }
        } else {
            // 旧版本方法
            android.net.NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
            if (networkInfo != null) {
                int type = networkInfo.getType();
                if (type == ConnectivityManager.TYPE_WIFI) {
                    return "Wi-Fi";
                } else if (type == ConnectivityManager.TYPE_MOBILE) {
                    return "Cellular";
                } else if (type == ConnectivityManager.TYPE_ETHERNET) {
                    return "Ethernet";
                }
            }
        }
        return "Unknown";
    }
    private static class NetworkCallback extends ConnectivityManager.NetworkCallback {
        @Override
        public void onAvailable(@NonNull Network network) {
            super.onAvailable(network);
            // 网络可用时调用,Wi-Fi 或移动数据已连接
            // 注意:这里不能保证网络一定可以访问互联网,通常需要配合 onCapabilitiesChanged 判断
            System.out.println("NetworkCallback: Network is available.");
        }
        @Override
        public void onLost(@NonNull Network network) {
            super.onLost(network);
            // 网络丢失时调用,Wi-Fi 断开或移动数据断开
            System.out.println("NetworkCallback: Network is lost.");
        }
        @Override
        public void onUnavailable() {
            super.onUnavailable();
            // 没有可用的网络满足你的请求时调用
            System.out.println("NetworkCallback: Network is unavailable.");
        }
        @Override
        public void onCapabilitiesChanged(@NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
            super.onCapabilitiesChanged(network, networkCapabilities);
            // 网络能力变化时调用,例如从无网络到有网络,或者网络类型改变
            // 可以在这里精确判断网络是否真正可用
            boolean hasInternet = networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
            boolean isValidated = networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
            if (hasInternet && isValidated) {
                System.out.println("NetworkCallback: Network is validated and has internet.");
            }
        }
    }
}

在 Activity 或 Service 中使用

Android如何实时监控网络状态变化?-图2
(图片来源网络,侵删)
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
    private NetworkHelper networkHelper;
    private TextView networkStatusText;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        networkHelper = new NetworkHelper(this);
        networkStatusText = findViewById(R.id.network_status_text);
        Button checkButton = findViewById(R.id.check_button);
        checkButton.setOnClickListener(v -> {
            if (networkHelper.isNetworkAvailable()) {
                Toast.makeText(this, "网络已连接: " + networkHelper.getNetworkType(), Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "网络未连接", Toast.LENGTH_SHORT).show();
            }
        });
        updateNetworkStatus();
    }
    @Override
    protected void onResume() {
        super.onResume();
        // 在 Activity 可见时注册监听
        networkHelper.registerNetworkCallback();
    }
    @Override
    protected void onPause() {
        super.onPause();
        // 在 Activity 不可见时注销监听,防止内存泄漏
        networkHelper.unregisterNetworkCallback();
    }
    private void updateNetworkStatus() {
        if (networkHelper.isNetworkAvailable()) {
            networkStatusText.setText("当前网络: " + networkHelper.getNetworkType());
        } else {
            networkStatusText.setText("当前网络: 未连接");
        }
    }
}

使用 BroadcastReceiver (旧方法)

在 Android 8.0 (API 26) 之前,我们通常通过监听 ConnectivityManager.CONNECTIVITY_ACTION 广播来检测网络变化。

缺点

  • 已过时: 从 Android 8.0 开始,隐式广播(不指定包名的广播)不再对后台应用有效。
  • 阻塞主线程: 广播接收器在主线程执行,如果处理不当可能导致 ANR。
  • 不够精确: 无法区分是哪种网络类型的变化。

代码示例 (仅作了解)

创建 BroadcastReceiver

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.widget.Toast;
public class NetworkChangeReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
            ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
            if (activeNetworkInfo != null && activeNetworkInfo.isConnected()) {
                Toast.makeText(context, "网络已连接", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(context, "网络已断开", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

在 AndroidManifest.xml 中注册

<receiver android:name=".NetworkChangeReceiver">
    <intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</receiver>

使用 Jetpack WorkManager (后台任务)

如果你的需求是在网络恢复后执行一个后台任务(重新上传数据),WorkManager 是最佳选择,它可以监听网络状态的变化,并在条件满足时执行任务。

Android如何实时监控网络状态变化?-图3
(图片来源网络,侵删)

优点

  • 可靠: WorkManager 保证任务最终会执行,即使应用被杀死或手机重启。
  • 灵活: 可以组合多种约束条件,如网络状态、充电状态、电量等。

代码示例

创建一个 Worker

import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
public class NetworkAwareWorker extends Worker {
    public NetworkAwareWorker(@NonNull Context context, @NonNull WorkerParameters params) {
        super(context, params);
    }
    @NonNull
    @Override
    public Result doWork() {
        // 在这里执行你的网络任务
        Log.d("NetworkWorker", "Network is available, performing task...");
        // 上传数据、同步信息等
        return Result.success(); // 任务成功
    }
}

在需要的地方设置并启动任务

import androidx.work.Constraints;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
// ...
// 创建约束条件:只有在有网络连接时才执行
Constraints constraints = new Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED) // 或者 UNMETERED (仅非流量网络)
        .build();
// 创建一次性任务请求
OneTimeWorkRequest uploadWorkRequest =
        new OneTimeWorkRequest.Builder(NetworkAwareWorker.class)
                .setConstraints(constraints)
                .build();
// 提交任务
WorkManager.getInstance(context).enqueue(uploadWorkRequest);

总结与最佳实践

方法 优点 缺点 适用场景
NetworkCallback 现代、高效、精确、异步 需要 API 21+ 绝大多数场景,特别是需要实时响应用户界面变化的场景。
BroadcastReceiver 简单、兼容性好 已过时、不精确、可能无效 旧项目维护,或仅需兼容非常低版本的 API。
WorkManager 可靠、适合后台任务 不适合实时 UI 更新 在网络恢复后执行一次性周期性的后台任务。

最佳实践建议:

  1. 对于需要实时更新 UI 的场景(显示一个在线/离线的状态指示器),使用 ConnectivityManager.NetworkCallback,这是 Google 推荐的标准方式。
  2. 在 Activity/Fragment 的生命周期中管理回调:在 onResume()onStart() 中注册,在 onPause()onStop() 中注销,以避免内存泄漏和不必要的电量消耗。
  3. 对于后台数据同步使用 WorkManager,它能更好地处理任务调度和生命周期问题,确保数据最终能够同步成功。
  4. 避免在 BroadcastReceiver 中执行耗时操作,否则很容易导致 ANR,如果必须处理,应启动一个 Service 或 IntentService 来完成。
分享:
扫描分享到社交APP
上一篇
下一篇