12 changed files with 863 additions and 441 deletions
@ -0,0 +1,139 @@ |
|||||
|
// NetworkStateMonitor.kt |
||||
|
package com.yunqiinnovation.azure_speech |
||||
|
|
||||
|
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 android.util.Log |
||||
|
|
||||
|
/** |
||||
|
* 网络状态监听器,负责监控网络连接状态变化 |
||||
|
*/ |
||||
|
class NetworkStateMonitor(private val context: Context) { |
||||
|
private val tag = "NetworkStateMonitor" |
||||
|
|
||||
|
private var connectivityManager: ConnectivityManager? = null |
||||
|
private var networkCallback: ConnectivityManager.NetworkCallback? = null |
||||
|
private var isNetworkAvailable = true |
||||
|
private var networkStateListener: NetworkStateListener? = null |
||||
|
|
||||
|
/** |
||||
|
* 网络状态监听接口 |
||||
|
*/ |
||||
|
interface NetworkStateListener { |
||||
|
fun onNetworkAvailable() |
||||
|
fun onNetworkLost() |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 初始化网络状态监听 |
||||
|
*/ |
||||
|
fun initialize(listener: NetworkStateListener? = null) { |
||||
|
networkStateListener = listener |
||||
|
initNetworkMonitoring() |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查当前网络状态 |
||||
|
* @return true表示网络可用,false表示网络不可用 |
||||
|
*/ |
||||
|
fun checkNetworkStatus(): Boolean { |
||||
|
try { |
||||
|
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager |
||||
|
|
||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { |
||||
|
val network = connectivityManager.activeNetwork ?: return false |
||||
|
val networkCapabilities = connectivityManager.getNetworkCapabilities(network) ?: return false |
||||
|
|
||||
|
return networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && |
||||
|
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) |
||||
|
} else { |
||||
|
@Suppress("DEPRECATION") |
||||
|
val networkInfo = connectivityManager.activeNetworkInfo |
||||
|
return networkInfo?.isConnectedOrConnecting == true |
||||
|
} |
||||
|
} catch (e: Exception) { |
||||
|
Log.e(tag, "检查网络状态失败: ${e.message}") |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 初始化网络状态监听 |
||||
|
*/ |
||||
|
private fun initNetworkMonitoring() { |
||||
|
try { |
||||
|
connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager |
||||
|
|
||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { |
||||
|
networkCallback = object : ConnectivityManager.NetworkCallback() { |
||||
|
override fun onAvailable(network: Network) { |
||||
|
super.onAvailable(network) |
||||
|
Log.d(tag, "网络连接可用") |
||||
|
isNetworkAvailable = true |
||||
|
networkStateListener?.onNetworkAvailable() |
||||
|
} |
||||
|
|
||||
|
override fun onLost(network: Network) { |
||||
|
super.onLost(network) |
||||
|
Log.d(tag, "网络连接丢失") |
||||
|
isNetworkAvailable = false |
||||
|
networkStateListener?.onNetworkLost() |
||||
|
} |
||||
|
|
||||
|
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { |
||||
|
super.onCapabilitiesChanged(network, networkCapabilities) |
||||
|
val hasInternet = networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) |
||||
|
Log.d(tag, "网络能力变化,是否有网络: $hasInternet") |
||||
|
if (hasInternet && !isNetworkAvailable) { |
||||
|
isNetworkAvailable = true |
||||
|
networkStateListener?.onNetworkAvailable() |
||||
|
} else if (!hasInternet && isNetworkAvailable) { |
||||
|
isNetworkAvailable = false |
||||
|
networkStateListener?.onNetworkLost() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
val networkRequest = NetworkRequest.Builder() |
||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) |
||||
|
.build() |
||||
|
|
||||
|
connectivityManager?.registerNetworkCallback(networkRequest, networkCallback!!) |
||||
|
} |
||||
|
} catch (e: Exception) { |
||||
|
Log.e(tag, "初始化网络监听失败: ${e.message}") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 释放资源 |
||||
|
*/ |
||||
|
fun dispose() { |
||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && networkCallback != null) { |
||||
|
connectivityManager?.unregisterNetworkCallback(networkCallback!!) |
||||
|
networkCallback = null |
||||
|
} |
||||
|
connectivityManager = null |
||||
|
networkStateListener = null |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查是否是网络相关错误 |
||||
|
*/ |
||||
|
fun isNetworkRelatedError(reason: String, errorDetails: String): Boolean { |
||||
|
val networkErrorKeywords = arrayOf( |
||||
|
"network", "connection", "timeout", "unreachable", |
||||
|
"dns", "socket", "ssl", "tls", "certificate", |
||||
|
"网络", "连接", "超时", "不可达" |
||||
|
) |
||||
|
|
||||
|
val combinedText = "$reason $errorDetails".lowercase() |
||||
|
return networkErrorKeywords.any { keyword -> |
||||
|
combinedText.contains(keyword.lowercase()) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,177 @@ |
|||||
|
import Foundation |
||||
|
import SystemConfiguration |
||||
|
import Network |
||||
|
|
||||
|
/// 网络状态监听器(iOS) |
||||
|
final class NetworkStateMonitor { |
||||
|
private let tag = "NetworkStateMonitor" |
||||
|
|
||||
|
// iOS 12+ |
||||
|
private var monitor: NWPathMonitor? |
||||
|
private let monitorQueue = DispatchQueue(label: "NetworkStateMonitor.queue") |
||||
|
|
||||
|
// iOS 10–11 回退 |
||||
|
private var reachability: SCNetworkReachability? |
||||
|
private var reachabilityQueue: DispatchQueue? |
||||
|
|
||||
|
private(set) var isNetworkAvailable: Bool = true |
||||
|
private weak var networkStateListener: NetworkStateListener? |
||||
|
|
||||
|
/// 网络状态监听接口 |
||||
|
protocol NetworkStateListener: AnyObject { |
||||
|
func onNetworkAvailable() |
||||
|
func onNetworkLost() |
||||
|
} |
||||
|
|
||||
|
/// 初始化网络状态监听 |
||||
|
func initialize(listener: NetworkStateListener? = nil) { |
||||
|
networkStateListener = listener |
||||
|
initNetworkMonitoring() |
||||
|
} |
||||
|
|
||||
|
/// 检查当前网络状态 |
||||
|
/// - Returns: true 表示网络可用 |
||||
|
func checkNetworkStatus() -> Bool { |
||||
|
if #available(iOS 12.0, *) { |
||||
|
// NWPathMonitor 只在回调里更新状态,因此这里返回缓存的 isNetworkAvailable |
||||
|
return isNetworkAvailable |
||||
|
} else { |
||||
|
// 使用 Reachability 即时检测 |
||||
|
var zeroAddress = sockaddr_in() |
||||
|
zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size) |
||||
|
zeroAddress.sin_family = sa_family_t(AF_INET) |
||||
|
let ref = withUnsafePointer(to: &zeroAddress) { |
||||
|
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) { |
||||
|
SCNetworkReachabilityCreateWithAddress(nil, $0) |
||||
|
} |
||||
|
} |
||||
|
guard let reachRef = ref else { return false } |
||||
|
var flags = SCNetworkReachabilityFlags() |
||||
|
if !SCNetworkReachabilityGetFlags(reachRef, &flags) { return false } |
||||
|
return Self.isReachable(flags: flags) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// 释放资源 |
||||
|
func dispose() { |
||||
|
if #available(iOS 12.0, *) { |
||||
|
monitor?.cancel() |
||||
|
monitor = nil |
||||
|
} else { |
||||
|
if let reachability = reachability { |
||||
|
SCNetworkReachabilitySetDispatchQueue(reachability, nil) |
||||
|
SCNetworkReachabilitySetCallback(reachability, nil, nil) |
||||
|
} |
||||
|
reachability = nil |
||||
|
reachabilityQueue = nil |
||||
|
} |
||||
|
networkStateListener = nil |
||||
|
} |
||||
|
|
||||
|
/// 判断是否为网络相关错误(关键字简单匹配) |
||||
|
func isNetworkRelatedError(reason: String, errorDetails: String) -> Bool { |
||||
|
let networkErrorKeywords = [ |
||||
|
"network","connection","timeout","unreachable", |
||||
|
"dns","socket","ssl","tls","certificate", |
||||
|
"网络","连接","超时","不可达" |
||||
|
] |
||||
|
let combined = (reason + " " + errorDetails).lowercased() |
||||
|
return networkErrorKeywords.contains { combined.contains($0.lowercased()) } |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// MARK: - Private |
||||
|
private extension NetworkStateMonitor { |
||||
|
func initNetworkMonitoring() { |
||||
|
if #available(iOS 12.0, *) { |
||||
|
let monitor = NWPathMonitor() |
||||
|
self.monitor = monitor |
||||
|
monitor.pathUpdateHandler = { [weak self] path in |
||||
|
guard let self = self else { return } |
||||
|
let hasInternet = (path.status == .satisfied) |
||||
|
// 只有状态变化时才回调 |
||||
|
if hasInternet && !self.isNetworkAvailable { |
||||
|
self.isNetworkAvailable = true |
||||
|
self.networkStateListener?.onNetworkAvailable() |
||||
|
Self.log(self.tag, "网络连接可用") |
||||
|
} else if !hasInternet && self.isNetworkAvailable { |
||||
|
self.isNetworkAvailable = false |
||||
|
self.networkStateListener?.onNetworkLost() |
||||
|
Self.log(self.tag, "网络连接丢失") |
||||
|
} else { |
||||
|
Self.log(self.tag, "网络能力变化,是否有网络: \(hasInternet)") |
||||
|
} |
||||
|
} |
||||
|
monitor.start(queue: monitorQueue) |
||||
|
} else { |
||||
|
// iOS 10–11 使用 Reachability 回退 |
||||
|
var zeroAddress = sockaddr_in() |
||||
|
zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size) |
||||
|
zeroAddress.sin_family = sa_family_t(AF_INET) |
||||
|
reachability = withUnsafePointer(to: &zeroAddress) { |
||||
|
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) { |
||||
|
SCNetworkReachabilityCreateWithAddress(nil, $0) |
||||
|
} |
||||
|
} |
||||
|
guard let reachability = reachability else { |
||||
|
Self.log(tag, "初始化网络监听失败: Reachability 创建失败") |
||||
|
return |
||||
|
} |
||||
|
let queue = DispatchQueue(label: "NetworkStateMonitor.reachability") |
||||
|
reachabilityQueue = queue |
||||
|
|
||||
|
var context = SCNetworkReachabilityContext( |
||||
|
version: 0, |
||||
|
info: UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()), |
||||
|
retain: nil, |
||||
|
release: nil, |
||||
|
copyDescription: nil |
||||
|
) |
||||
|
let callback: SCNetworkReachabilityCallBack = { (_, flags, info) in |
||||
|
guard let info = info else { return } |
||||
|
let monitor = Unmanaged<NetworkStateMonitor>.fromOpaque(info).takeUnretainedValue() |
||||
|
let hasInternet = NetworkStateMonitor.isReachable(flags: flags) |
||||
|
if hasInternet && !monitor.isNetworkAvailable { |
||||
|
monitor.isNetworkAvailable = true |
||||
|
monitor.networkStateListener?.onNetworkAvailable() |
||||
|
NetworkStateMonitor.log(monitor.tag, "网络连接可用") |
||||
|
} else if !hasInternet && monitor.isNetworkAvailable { |
||||
|
monitor.isNetworkAvailable = false |
||||
|
monitor.networkStateListener?.onNetworkLost() |
||||
|
NetworkStateMonitor.log(monitor.tag, "网络连接丢失") |
||||
|
} else { |
||||
|
NetworkStateMonitor.log(monitor.tag, "网络能力变化,是否有网络: \(hasInternet)") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if SCNetworkReachabilitySetCallback(reachability, callback, &context), |
||||
|
SCNetworkReachabilitySetDispatchQueue(reachability, queue) { |
||||
|
// 触发一次初始查询 |
||||
|
var flags = SCNetworkReachabilityFlags() |
||||
|
if SCNetworkReachabilityGetFlags(reachability, &flags) { |
||||
|
self.isNetworkAvailable = Self.isReachable(flags: flags) |
||||
|
} |
||||
|
} else { |
||||
|
Self.log(tag, "初始化网络监听失败: 设置回调/队列失败") |
||||
|
SCNetworkReachabilitySetCallback(reachability, nil, nil) |
||||
|
SCNetworkReachabilitySetDispatchQueue(reachability, nil) |
||||
|
self.reachability = nil |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static func isReachable(flags: SCNetworkReachabilityFlags) -> Bool { |
||||
|
// 经典 Reachability 判定 |
||||
|
let reachable = flags.contains(.reachable) |
||||
|
let requiresConnection = flags.contains(.connectionRequired) |
||||
|
let canConnectAutomatically = flags.contains(.connectionOnTraffic) || flags.contains(.connectionOnDemand) |
||||
|
let canConnectWithoutUser = canConnectAutomatically && !flags.contains(.interventionRequired) |
||||
|
return reachable && (!requiresConnection || canConnectWithoutUser) |
||||
|
} |
||||
|
|
||||
|
static func log(_ tag: String, _ message: String) { |
||||
|
#if DEBUG |
||||
|
print("[\(tag)] \(message)") |
||||
|
#endif |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue