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