睿诚科技协会

android网络断开连接

  1. 监听网络状态变化:如何实时知道网络已经断开或已连接。
  2. 检查当前网络状态:在执行网络操作前,主动检查网络是否可用。
  3. 更新 UI 和用户反馈:当网络断开时,如何通知用户。
  4. 处理后台任务:如果后台任务(如下载、上传)因网络中断而失败,应该如何处理。

监听网络状态变化 (核心)

在 Android 中,监听网络状态变化主要有两种方式:

android网络断开连接-图1
(图片来源网络,侵删)

使用 ConnectivityManagerNetworkCallback (推荐,API 24+)

这是 Android 7.0 (Nougat, API 24) 引入的现代化方式,它更高效、更灵活,因为它使用回调机制,而不是像广播那样需要全局监听。

关键类

  • ConnectivityManager: 系统服务,用于管理网络连接。
  • NetworkRequest: 描述你感兴趣的网络的请求。
  • NetworkCallback: 当网络状态发生变化时,系统会回调这个类中的方法。

实现步骤

  1. 获取 ConnectivityManager 实例

    android网络断开连接-图2
    (图片来源网络,侵删)
    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
  2. 创建 NetworkRequest 和 NetworkCallback

    // 创建一个网络请求,我们关心所有类型的网络
    NetworkRequest networkRequest = new NetworkRequest.Builder()
            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
            .build();
    // 创建网络回调
    ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
        @Override
        public void onLost(Network network) {
            // 网络连接丢失时调用 (从 WiFi 切换到移动数据,或 WiFi 断开)
            Log.d("NetworkCallback", "网络连接丢失");
            runOnUiThread(() -> {
                // 在这里更新 UI,例如显示网络断开提示
                Toast.makeText(MainActivity.this, "网络已断开", Toast.LENGTH_SHORT).show();
                // 禁用需要网络的功能按钮
            });
        }
        @Override
        public void onAvailable(Network network) {
            // 网络连接可用时调用
            Log.d("NetworkCallback", "网络连接可用");
            runOnUiThread(() -> {
                // 在这里更新 UI,例如隐藏网络断开提示
                Toast.makeText(MainActivity.this, "网络已连接", Toast.LENGTH_SHORT).show();
                // 启用需要网络的功能按钮
            });
        }
    };
  3. 注册和注销回调

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // ... 其他代码 ...
        // 注册网络回调
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            connectivityManager.registerNetworkCallback(networkRequest, networkCallback);
        } else {
            // 对于低于 API 24 的版本,需要使用广播的方式 (见下文)
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 在 Activity 销毁时,必须注销回调,防止内存泄漏
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            connectivityManager.unregisterNetworkCallback(networkCallback);
        }
    }

使用 BroadcastReceiver (兼容旧版本)

这种方式在所有 Android 版本上都能工作,但缺点是它是一个全局广播,即使你的 App 在后台,只要系统发送广播,你的接收器就会被唤醒,可能会影响性能。

关键类

android网络断开连接-图3
(图片来源网络,侵删)
  • BroadcastReceiver: 用于接收系统广播。
  • ConnectivityManager: 系统服务。
  • NetworkInfo: 描述网络连接的信息。

实现步骤

  1. 创建一个 BroadcastReceiver

    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()) {
                    // 网络未连接
                    Log.d("NetworkReceiver", "网络断开");
                    // 更新 UI 或执行其他操作
                } else {
                    // 网络已连接
                    Log.d("NetworkReceiver", "网络已连接");
                }
            }
        }
    }
  2. AndroidManifest.xml 中注册 Receiver

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

    注意:从 Android 7.0 (API 24) 开始,对 CONNECTIVITY_ACTION 隐式广播的接收限制非常严格,如果你的 targetSdkVersion 是 24 或更高,上面的方式可能无法正常工作。对于新项目,强烈推荐使用 NetworkCallback


检查当前网络状态

除了监听变化,很多时候你还需要在执行网络请求前主动检查一下网络是否可用。

使用 ConnectivityManager

public boolean isNetworkAvailable() {
    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    if (connectivityManager != null) {
        // 对于 API 23 及以上版本
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Network network = connectivityManager.getActiveNetwork();
            NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network);
            if (capabilities != null) {
                return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
                       capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
                       capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET);
            }
        } else {
            // 对于低于 API 23 的版本
            NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
            return activeNetworkInfo != null && activeNetworkInfo.isConnected();
        }
    }
    return false;
}

使用示例

if (isNetworkAvailable()) {
    // 执行网络请求
    makeNetworkRequest();
} else {
    // 显示提示,告诉用户网络不可用
    Toast.makeText(this, "网络不可用,请检查设置", Toast.LENGTH_SHORT).show();
}

更新 UI 和用户反馈

当检测到网络断开时,最佳实践是:

  • Toast 或 SnackBar:显示一个简短的提示信息,告诉用户网络已断开。
  • 禁用按钮:如果界面中有“上传”、“下载”、“刷新”等依赖网络的按钮,应将其设置为 setEnabled(false),防止用户误操作。
  • 显示占位符:在列表或数据加载区域,显示一个“无网络连接”的占位图或文本。
  • 数据缓存:App 有离线浏览的需求,可以提示用户“当前为离线模式,显示的是缓存数据”。

处理后台任务

如果你的 App 使用了 WorkManagerJobSchedulerAlarmManager 来执行后台任务,当网络断开时,任务应该被推迟或失败。

  • WorkManager (推荐): WorkManager 非常智能,你可以为你的 Worker 指定网络约束。

    public class DownloadWorker extends Worker {
        public DownloadWorker(@NonNull Context context, @NonNull WorkerParameters params) {
            super(context, params);
        }
        @NonNull
        @Override
        public Result doWork() {
            // 你的下载逻辑
            // ...
            return Result.success(); // 或 Result.retry()
        }
    }
    // 在提交任务时设置约束
    Constraints constraints = new Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED) // 只在连接时执行
            .build();
    OneTimeWorkRequest downloadWorkRequest =
            new OneTimeWorkRequest.Builder(DownloadWorker.class)
                    .setConstraints(constraints)
                    .build();
    WorkManager.getInstance(context).enqueue(downloadWorkRequest);

    当网络断开时,WorkManager 会自动推迟 NetworkType.CONNECTED 的任务,直到网络恢复。

  • 其他库 (如 Retrofit + OkHttp): 对于网络请求库,通常会提供一个重试机制,当请求失败时,库可以自动重试几次,如果网络是断开的,这些重试也都会失败,你可以在你的代码逻辑中判断:如果请求失败,并且原因是网络问题,就提示用户,而不是立即重试。


完整代码示例 (NetworkCallback 方式)

这是一个在 Activity 中实现网络监听的完整示例。

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private ConnectivityManager.NetworkCallback networkCallback;
    private Button networkDependentButton;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        networkDependentButton = findViewById(R.id.btn_network_action);
        networkDependentButton.setOnClickListener(v -> {
            if (isNetworkAvailable()) {
                Toast.makeText(this, "执行网络操作...", Toast.LENGTH_SHORT).show();
                // makeNetworkRequest();
            } else {
                Toast.makeText(this, "网络不可用,无法操作", Toast.LENGTH_SHORT).show();
            }
        });
        setupNetworkCallback();
    }
    private void setupNetworkCallback() {
        ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        if (connectivityManager == null) return;
        NetworkRequest networkRequest = new NetworkRequest.Builder()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .build();
        networkCallback = new ConnectivityManager.NetworkCallback() {
            @Override
            public void onLost(Network network) {
                Log.d("MainActivity", "网络连接丢失");
                runOnUiThread(() -> {
                    Toast.makeText(MainActivity.this, "网络已断开", Toast.LENGTH_SHORT).show();
                    networkDependentButton.setEnabled(false);
                });
            }
            @Override
            public void onAvailable(Network network) {
                Log.d("MainActivity", "网络连接可用");
                runOnUiThread(() -> {
                    Toast.makeText(MainActivity.this, "网络已连接", Toast.LENGTH_SHORT).show();
                    networkDependentButton.setEnabled(true);
                });
            }
        };
        // 注册回调
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            connectivityManager.registerNetworkCallback(networkRequest, networkCallback);
        } else {
            // 对于旧版本,可以在这里注册广播接收器
            // 但请注意 API 24+ 的限制
        }
    }
    private boolean isNetworkAvailable() {
        ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        if (connectivityManager != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                Network network = connectivityManager.getActiveNetwork();
                NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network);
                if (capabilities != null) {
                    return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
                           capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
                }
            } else {
                NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
                return activeNetworkInfo != null && activeNetworkInfo.isConnected();
            }
        }
        return false;
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (networkCallback != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
            if (connectivityManager != null) {
                connectivityManager.unregisterNetworkCallback(networkCallback);
            }
        }
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">
    <Button
        android:id="@+id/btn_network_action"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="执行网络操作" />
    <TextView
        android:id="@+id/tv_status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="等待网络状态..." />
</LinearLayout>

AndroidManifest.xml (别忘了权限)

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 如果需要访问移动网络 -->
<uses-permission android:name="android.permission.INTERNET" />
方法 优点 缺点 适用场景
NetworkCallback 高效、灵活、现代化、只在你的应用上下文中回调,性能好。 仅适用于 API 24 及以上版本。 新项目的首选
BroadcastReceiver 兼容所有版本。 全局广播、性能开销大、在 API 24+ 上受限。 需要兼容旧版本 (API < 24) 的项目。

最佳实践建议

  1. 主流程:使用 NetworkCallback 监听网络状态变化,实时更新 UI。
  2. 前置检查:在每次执行网络请求前,使用 isNetworkAvailable() 方法进行检查。
  3. 后台任务:使用 WorkManager 并设置 NetworkType 约束,让系统智能地处理任务调度。
  4. 兼容性:如果你的 minSdkVersion 低于 24,需要同时实现 BroadcastReceiver 作为备用方案,并在 onCreate 中根据版本选择使用哪种方式。
分享:
扫描分享到社交APP
上一篇
下一篇