要在 Capacitor 应用中检测网络状态,请安装 @capacitor/network,调用 Network.getStatus() 获取当前状态,并订阅 networkStatusChange 以获取更新。这可以告诉你设备是否已连接,以及使用的是 Wi-Fi 还是蜂窝网络。若要确认互联网是否真正可用,或连接是否为计费流量,请添加连通性检查或 @capgo/capacitor-network-diagnostics。本指南通过 Capacitor 8 code 涵盖了这三个层面。
“用户是否在线?”背后的三个问题
| 问题 | API | 备注 |
|---|---|---|
| 是否存在活跃的网络接口? | Network.getStatus().connected |
快速、事件驱动,但不代表互联网访问权限 |
| 连接类型是什么? | connectionType |
wifi, cellular, none, unknown 在官方插件中 |
| 互联网是否真正可用? | HTTP 检查或 internetReachable |
需要请求或 OS 验证 |
| 我应该避免大文件下载吗? | expensive, constrained |
计费网络与 iOS 低数据模式 |
大多数离线 Bug 都源于误判。例如,带有登录页面的酒店 Wi-Fi 会报告已连接, connected: true 而每一次 API 调用都会超时。
安装 Network 插件
bun add @capacitor/network
bunx cap sync
无需任何配置。在 Android 上,该插件会将 ACCESS_NETWORK_STATE 添加到合并后的 manifest 中,这是一种安装时授予的常规权限。iOS 则无需任何操作。
读取当前状态
import { Network } from '@capacitor/network';
const status = await Network.getStatus();
console.log(status.connected); // true or false
console.log(status.connectionType); // 'wifi' | 'cellular' | 'none' | 'unknown'
在应用启动时调用它,以决定是从缓存还是网络加载数据。它会在几毫秒内返回,因为它直接读取 OS 状态,不会产生网络流量。
监听网络变化
import { Network, type ConnectionStatus } from '@capacitor/network';
import type { PluginListenerHandle } from '@capacitor/core';
let handle: PluginListenerHandle | undefined;
export async function watchNetwork(onChange: (s: ConnectionStatus) => void) {
onChange(await Network.getStatus());
handle = await Network.addListener('networkStatusChange', onChange);
}
export async function stopWatchingNetwork() {
await handle?.remove();
}
两个实用的细节:
- 对事件进行防抖处理。 从 Wi-Fi 切换到蜂窝网络时,两个平台都可能在短时间内报告
none新连接建立前处于断网状态。在显示“离线”横幅前进行 1 到 2 秒的防抖,可以避免界面闪烁。 - 移除监听器 当组件卸载时,请务必移除监听器,否则在热重载或页面跳转后,回调函数可能会多次触发。
React hook
import { useEffect, useState } from 'react';
import { Network, type ConnectionStatus } from '@capacitor/network';
export function useNetworkStatus() {
const [status, setStatus] = useState<ConnectionStatus>({ connected: true, connectionType: 'unknown' });
useEffect(() => {
let timer: ReturnType<typeof setTimeout>;
const listener = Network.addListener('networkStatusChange', (s) => {
clearTimeout(timer);
timer = setTimeout(() => setStatus(s), s.connected ? 0 : 1500);
});
Network.getStatus().then(setStatus);
return () => {
clearTimeout(timer);
listener.then((l) => l.remove());
};
}, []);
return status;
}
上线会立即生效,下线则会等待 1.5 秒。同样的模式也可以通过 Vue composable 或 Angular service 实现。 BehaviorSubject.
为什么“已连接”并不代表“在线”
connected: true 该状态来自操作系统的网络栈。在以下情况下,该状态依然成立:
- Wi-Fi 网络存在强制门户(如机场、酒店、火车)
- 路由器没有上行连接
- DNS 解析失败或被拦截
- 公司防火墙或 VPN 拦截了您的 API 域名
- 您的后端服务宕机
解决方案是对您控制的服务进行连通性检查:
export async function canReachApi(timeoutMs = 4000): Promise<boolean> {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
const res = await fetch('https://api.example.com/health', {
method: 'HEAD',
cache: 'no-store',
signal: controller.signal,
});
return res.ok;
} catch {
return false;
} finally {
clearTimeout(timer);
}
}
在应用启动时、 networkStatusChange 报告 connected: true以及请求失败后运行。不要每隔几秒轮询一次,这会耗尽电量和流量。确保 health endpoint 在 CORS 中允许您应用的 origin(capacitor://localhost 在 iOS 上, https://localhost Android 默认允许),或者使用 CapacitorHttp 从原生 code 发送请求。
通过网络诊断获取更丰富的状态
@capgo/capacitor-network-diagnostics 可以读取更多操作系统层面的信息,并运行可以绕过 WebView CORS 的原生检查:
bun add @capgo/capacitor-network-diagnostics
bunx cap sync
import { NetworkDiagnostics } from '@capgo/capacitor-network-diagnostics';
const status = await NetworkDiagnostics.getNetworkStatus();
// {
// connected: true,
// connectionType: 'wifi' | 'cellular' | 'ethernet' | 'vpn' | 'other' | 'unknown' | 'none',
// internetReachable: true,
// expensive: false, // metered / cellular
// constrained: false, // iOS Low Data Mode
// captivePortal: false, // Android only
// details: { ... }
// }
各字段与操作系统的映射关系:
| 字段 | Android | iOS |
|---|---|---|
internetReachable |
NET_CAPABILITY_VALIDATED (Android 通过互联网探测) |
镜像自 NWPathMonitor 路径状态 |
expensive |
isActiveNetworkMetered() |
NWPath.isExpensive |
constrained |
始终 false |
NWPath.isConstrained (低数据模式) |
captivePortal |
NET_CAPABILITY_CAPTIVE_PORTAL |
未报告 |
vpn 类型 |
是 | 是 |
在 Android 上, internetReachable 是一个强信号,因为操作系统已经测试过连接。而在 iOS 上,它更接近于 connected,因此在关键业务流程中请保留 HTTP 检查。
原生网络可达性检查
const api = await NetworkDiagnostics.testUrl({
url: 'https://api.example.com/health',
method: 'HEAD',
timeoutMs: 4000,
});
// api.reachable, api.statusCode, api.durationMs, api.errorCode
const socket = await NetworkDiagnostics.testWebSocket({ url: 'wss://realtime.example.com' });
const port = await NetworkDiagnostics.testPort({ host: 'mqtt.example.com', port: 8883 });
原生请求不受 CORS 限制,因此你也可以测试第三方主机。
支持“网络报告”界面
当用户说“App 无法运行”时,一键生成的诊断报告可以节省数小时的沟通成本:
const report = await NetworkDiagnostics.runDiagnostics({
urls: [{ url: 'https://api.example.com/health' }, { url: 'https://cdn.example.com/ping' }],
websockets: [{ url: 'wss://realtime.example.com' }],
download: { url: 'https://cdn.example.com/1mb.bin', maxBytes: 1_048_576 },
packetLoss: { url: 'https://api.example.com/health', count: 5 },
});
console.log(report.issues); // human-readable list of problems
console.log(report.download?.mbps);
console.log(report.packetLoss?.lossPercent);
将 JSON 附件添加到支持工单中。你可以立即查明问题是 DNS、WebSocket 被拦截、强制门户(captive portal)还是链路延迟。
适配按流量计费和低数据模式连接
在移动数据费用较高的市场,尊重用户的数据限制是提升应用评分的捷径:
export async function shouldPrefetchMedia() {
const s = await NetworkDiagnostics.getNetworkStatus();
if (!s.connected) return false;
if (s.constrained) return false; // user asked iOS to save data
if (s.expensive) return false; // cellular or metered Wi-Fi
return true;
}
利用它来决定视频自动播放、图片质量、后台同步以及大型下载。在 Android 上,用户可以将 Wi-Fi 设置为按流量计费,因此不要默认 Wi-Fi 是免费的。
构建离线状态横幅
比起请求失败,显示一个小横幅更直观。使用上述 Hook:
export function OfflineBanner() {
const { connected } = useNetworkStatus();
if (connected) return null;
return (
<div role="status" aria-live="polite" className="offline-banner">
You are offline. Changes will sync when you reconnect.
</div>
);
}
role="status" 带 aria-live="polite" 能让屏幕阅读器播报状态变化。关于特定框架的 UI,请参阅我们的 Vue、Angular 和 React 离线界面构建指南.
离线时排队处理任务
检测状态只是离线支持的一半。当状态变为离线时:
- 从本地存储读取数据。使用 SQLite 数据库等工具,
@capgo/capacitor-fast-sql处理大数据集的效果比localStorage. - 将用户操作连同时间戳和幂等键一起写入 outbox 表。
- 在
networkStatusChange恢复连接时,请运行canReachApi(),然后按顺序清空发件箱。 - 使用指数退避算法重试失败的请求,最大间隔为几分钟。
对于必须在应用关闭后仍能完成的上传任务,应交给 native uploader 处理,而不是 fetch.
Web 回退
@capacitor/network 同样在浏览器中运行,它基于 navigator.onLine 以及 online 和 offline 事件。这意味着你的 code 可以无需修改地在 bun run dev 和 PWA 中运行,但是 connectionType 通常会 unknown 在 Web 端。Chrome 的 navigator.connection 暴露了 effectiveType 和 saveData,Safari 并不会这样做,因此请将其视为可选提示。
故障排除
监听器在 iOS Simulator 上永远不会触发:模拟器共享 Mac 的网络。请切换 Mac 的 Wi-Fi 或在开启飞行模式的真机上进行测试。
connectionType 是 unknown 正在使用 VPN:官方插件仅报告 wifi, cellular, none 和 unknown。请使用 diagnostics 插件,它会返回 vpn 和 ethernet.
切换网络时,App 会短暂显示为离线状态:如上所示,对离线状态切换进行防抖处理。
健康检查在设备上失败,但在浏览器中正常:CORS。允许 capacitor://localhost 和 https://localhost,或使用 NetworkDiagnostics.testUrl,它以原生方式运行。
navigator.onLine 当插件显示离线时,其值为 true:信任插件。某些 Android WebView 不会 navigator.onLine 可靠地更新。
通过 OTA 推送离线修复方案
离线处理主要通过 JavaScript 实现:阈值、横幅、重试逻辑、缓存规则。配合 Capgo Live Update, 你可以在当天为每个用户进行微调。Capgo 会在后台下载 update bundles,如果下载失败则保留当前版本,因此糟糕的网络连接绝不会导致用户处于更新了一半的状态。此外, 网络诊断文档 列出了所有选项。