fix(scale): 修复主设备无法发现子设备及IP显示未知的问题
- UdpBroadcastReceiver 补充 socket.broadcast=true,修复广播包被内核丢弃 - MdnsDiscoveryManager 改为串行解析队列,修复多设备并发解析报 FAILURE_ALREADY_ACTIVE - ScaleWebSocketServer 推送数据时动态填充本机 IP,解决子设备 IP 始终显示未知
This commit is contained in:
@@ -4,10 +4,14 @@ import android.content.Context
|
|||||||
import android.net.nsd.NsdManager
|
import android.net.nsd.NsdManager
|
||||||
import android.net.nsd.NsdServiceInfo
|
import android.net.nsd.NsdServiceInfo
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import java.util.LinkedList
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mDNS 设备发现管理器(仅主设备运行)
|
* mDNS 设备发现管理器(仅主设备运行)
|
||||||
* 持续扫描局域网,发现或丢失 DishMatch 子设备时通过回调通知连接管理器
|
* 持续扫描局域网,发现或丢失 DishMatch 子设备时通过回调通知连接管理器
|
||||||
|
*
|
||||||
|
* 注意:Android NsdManager.resolveService 不支持并发调用,多台子设备同时被发现时
|
||||||
|
* 会报 FAILURE_ALREADY_ACTIVE(3) 导致解析失败。此处使用串行队列逐一解析。
|
||||||
*/
|
*/
|
||||||
class MdnsDiscoveryManager(private val context: Context) {
|
class MdnsDiscoveryManager(private val context: Context) {
|
||||||
|
|
||||||
@@ -22,6 +26,11 @@ class MdnsDiscoveryManager(private val context: Context) {
|
|||||||
private var discoveryListener: NsdManager.DiscoveryListener? = null
|
private var discoveryListener: NsdManager.DiscoveryListener? = null
|
||||||
private var isDiscovering = false
|
private var isDiscovering = false
|
||||||
|
|
||||||
|
/** 待解析队列,串行处理避免并发解析失败 */
|
||||||
|
private val resolveQueue = LinkedList<NsdServiceInfo>()
|
||||||
|
@Volatile
|
||||||
|
private var isResolving = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发现新设备时的回调:(deviceId, host, port)
|
* 发现新设备时的回调:(deviceId, host, port)
|
||||||
*/
|
*/
|
||||||
@@ -62,7 +71,7 @@ class MdnsDiscoveryManager(private val context: Context) {
|
|||||||
// 只处理 DishMatch 服务
|
// 只处理 DishMatch 服务
|
||||||
if (!service.serviceName.startsWith("DishMatch-")) return
|
if (!service.serviceName.startsWith("DishMatch-")) return
|
||||||
Log.d(TAG, "发现设备: ${service.serviceName}")
|
Log.d(TAG, "发现设备: ${service.serviceName}")
|
||||||
resolveService(service)
|
enqueueResolve(service)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceLost(service: NsdServiceInfo) {
|
override fun onServiceLost(service: NsdServiceInfo) {
|
||||||
@@ -87,18 +96,45 @@ class MdnsDiscoveryManager(private val context: Context) {
|
|||||||
nsdManager.resolveService(service, object : NsdManager.ResolveListener {
|
nsdManager.resolveService(service, object : NsdManager.ResolveListener {
|
||||||
override fun onResolveFailed(info: NsdServiceInfo, errorCode: Int) {
|
override fun onResolveFailed(info: NsdServiceInfo, errorCode: Int) {
|
||||||
Log.e(TAG, "解析设备地址失败: ${info.serviceName}, errorCode=$errorCode")
|
Log.e(TAG, "解析设备地址失败: ${info.serviceName}, errorCode=$errorCode")
|
||||||
|
// 解析结束(失败),继续处理队列中下一个
|
||||||
|
processNextResolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceResolved(info: NsdServiceInfo) {
|
override fun onServiceResolved(info: NsdServiceInfo) {
|
||||||
val deviceId = info.serviceName.removePrefix("DishMatch-")
|
val deviceId = info.serviceName.removePrefix("DishMatch-")
|
||||||
val host = info.host?.hostAddress ?: return
|
val host = info.host?.hostAddress ?: run {
|
||||||
|
processNextResolve()
|
||||||
|
return
|
||||||
|
}
|
||||||
val port = info.port
|
val port = info.port
|
||||||
Log.d(TAG, "设备地址已解析: $deviceId -> $host:$port")
|
Log.d(TAG, "设备地址已解析: $deviceId -> $host:$port")
|
||||||
onDeviceFound?.invoke(deviceId, host, port)
|
onDeviceFound?.invoke(deviceId, host, port)
|
||||||
|
// 解析结束(成功),继续处理队列中下一个
|
||||||
|
processNextResolve()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 将待解析服务加入队列,若当前无解析任务则立即开始 */
|
||||||
|
@Synchronized
|
||||||
|
private fun enqueueResolve(service: NsdServiceInfo) {
|
||||||
|
resolveQueue.add(service)
|
||||||
|
if (!isResolving) {
|
||||||
|
processNextResolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 从队列取出下一个服务进行解析 */
|
||||||
|
@Synchronized
|
||||||
|
private fun processNextResolve() {
|
||||||
|
val next = resolveQueue.poll() ?: run {
|
||||||
|
isResolving = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isResolving = true
|
||||||
|
resolveService(next)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 停止扫描,在应用退出时调用
|
* 停止扫描,在应用退出时调用
|
||||||
*/
|
*/
|
||||||
@@ -112,5 +148,9 @@ class MdnsDiscoveryManager(private val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
discoveryListener = null
|
discoveryListener = null
|
||||||
|
synchronized(this) {
|
||||||
|
resolveQueue.clear()
|
||||||
|
isResolving = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ object ScaleServiceManager {
|
|||||||
Log.d(TAG, "启动秤服务, deviceId=$deviceId, role=$role")
|
Log.d(TAG, "启动秤服务, deviceId=$deviceId, role=$role")
|
||||||
|
|
||||||
// 所有设备:启动 WebSocket 服务端 + mDNS 注册
|
// 所有设备:启动 WebSocket 服务端 + mDNS 注册
|
||||||
wsServer = ScaleWebSocketServer(deviceId).also { it.start() }
|
wsServer = ScaleWebSocketServer(deviceId, context).also { it.start() }
|
||||||
mdnsRegister = MdnsRegisterManager(context).also { it.register(deviceId) }
|
mdnsRegister = MdnsRegisterManager(context).also { it.register(deviceId) }
|
||||||
|
|
||||||
if (role == DeviceRole.MASTER) {
|
if (role == DeviceRole.MASTER) {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.shuwei.dish.match.scale
|
package com.shuwei.dish.match.scale
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
|
import com.shuwei.dish.match.utils.NetworkUtil
|
||||||
import com.shuwei.dish.match.utils.WeightUtil
|
import com.shuwei.dish.match.utils.WeightUtil
|
||||||
import org.java_websocket.WebSocket
|
import org.java_websocket.WebSocket
|
||||||
import org.java_websocket.handshake.ClientHandshake
|
import org.java_websocket.handshake.ClientHandshake
|
||||||
@@ -12,7 +14,7 @@ import java.net.InetSocketAddress
|
|||||||
* 秤数据 WebSocket 服务端(主设备和子设备均运行)
|
* 秤数据 WebSocket 服务端(主设备和子设备均运行)
|
||||||
* 监听 WeightUtil 回调,将本机秤数据实时推送给所有已连接的主设备客户端
|
* 监听 WeightUtil 回调,将本机秤数据实时推送给所有已连接的主设备客户端
|
||||||
*/
|
*/
|
||||||
class ScaleWebSocketServer(private val deviceId: String) {
|
class ScaleWebSocketServer(private val deviceId: String, private val context: Context) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "ScaleWebSocketServer"
|
private const val TAG = "ScaleWebSocketServer"
|
||||||
@@ -53,7 +55,8 @@ class ScaleWebSocketServer(private val deviceId: String) {
|
|||||||
address = address,
|
address = address,
|
||||||
weight = weight,
|
weight = weight,
|
||||||
state = state,
|
state = state,
|
||||||
ts = now
|
ts = now,
|
||||||
|
ip = NetworkUtil.getLocalIpAddress(context)
|
||||||
)
|
)
|
||||||
latestData[address] = data
|
latestData[address] = data
|
||||||
broadcast(gson.toJson(data))
|
broadcast(gson.toJson(data))
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class UdpBroadcastReceiver {
|
|||||||
|
|
||||||
private fun listenLoop() {
|
private fun listenLoop() {
|
||||||
try {
|
try {
|
||||||
socket = DatagramSocket(UdpBroadcastSender.BROADCAST_PORT)
|
socket = DatagramSocket(UdpBroadcastSender.BROADCAST_PORT).also { it.broadcast = true }
|
||||||
val buf = ByteArray(BUFFER_SIZE)
|
val buf = ByteArray(BUFFER_SIZE)
|
||||||
while (running) {
|
while (running) {
|
||||||
val packet = DatagramPacket(buf, buf.size)
|
val packet = DatagramPacket(buf, buf.size)
|
||||||
|
|||||||
Reference in New Issue
Block a user