如题,我是通过 UDP 通信实现安卓设备在局域网发送 log 日志到 PC 端
使用场景
- 安卓设备不能连接电脑
- 不支持无线 adb
安卓端
同时也是客户端,log 的发送端
package com.atoto.ota.utils
import android.util.Log
import com.atoto.ota.BuildConfig
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetAddress
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.concurrent.Executors
import java.util.concurrent.LinkedBlockingDeque
/**
* 用于 usb 无法连接设备,通过网络发送 log
*/
object NetLog {
private const val address = "192.168.11.100"
private const val port: Int = 8888
private const val SPLIT_SIZE = 1000
private const val enableLog = true
private val socket:DatagramSocket by lazy { DatagramSocket() }
private val executor = Executors.newSingleThreadExecutor()
private val queue = LinkedBlockingDeque<Runnable>()
private val sdf = object :ThreadLocal<DateFormat>() {
override fun initialValue(): DateFormat {
return SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS",Locale.US)
}
}
private val date by lazy { Date() }
private var mActive: Runnable? = null
fun sendLog(str:String) {
if (!BuildConfig.DEBUG || !enableLog) {
return
}
queue.offer(Runnable {
sendLogContent(str)
scheduleNext()
})
if (mActive == null) {
scheduleNext()
}
}
private fun scheduleNext() {
if (queue.poll().also { mActive = it } != null) {
executor?.execute(mActive)
}
}
private fun sendLogContent(str: String) {
date.time = System.currentTimeMillis()
val time = sdf.get()?.format(date)
val sendContent = "$time\t$str"
kotlin.runCatching {
// 创建远程主机IP地址对象
val inetAddress = InetAddress.getByName(address)
val buffer = sendContent.toByteArray(charset = Charsets.UTF_8)
// 加入 buffer 拆包,小于 1024 字节不拆
val splitBuffer = splitGroup(list = buffer)
val spliterator = splitBuffer.iterator()
var idx = 0
while (spliterator.hasNext()) {
val item = spliterator.next()
if (idx == 0) {
val fItem =
if (splitBuffer.size > 1) item + "{start}".toByteArray(charset = Charsets.UTF_8) else item
val packet = DatagramPacket(fItem, fItem.size, inetAddress, port)
socket.send(packet)
} else {
val fItem =
if (idx == splitBuffer.size - 1) item + "{end}".toByteArray(charset = Charsets.UTF_8) else item + "{split}".toByteArray(
charset = Charsets.UTF_8
)
val packet = DatagramPacket(fItem, fItem.size, inetAddress, port)
socket.send(packet)
}
++idx
}
}.onFailure {
Log.e("NetLog=", "sendLog error ${it}")
}
}
private fun splitGroup(list:ByteArray):List<ByteArray> {
val splitBuffer = arrayListOf<ByteArray>()
val groupNum = list.size / SPLIT_SIZE
val remainLast = list.size % SPLIT_SIZE
for (index in 0 until groupNum) {
val start = index * SPLIT_SIZE
val temp = ByteArray(SPLIT_SIZE)
for (idx in 0 until SPLIT_SIZE) {
temp[idx] = list[idx + start]
}
splitBuffer.add(temp)
}
if (remainLast > 0) {
val index = (groupNum) * SPLIT_SIZE
val temp = ByteArray(remainLast)
for (idx in 0 until remainLast) {
temp[idx] = list[idx + index]
}
splitBuffer.add(temp)
}
return splitBuffer
}
}
服务端
这里我用 Python 实现,毕竟人生苦短,我用 Python
import socket
# 服务端配置
host = '192.168.1.163'
port = 8888
# 创建 udp 套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定到地址和端口
server_socket.bind((host, port))
print(f"UDP 服务: {host}:{port}")
while True:
# 接收客户端传来的消息
data, client_address = server_socket.recvfrom(1024 * 15)
receive = data.decode('utf-8')
if "{end}" in receive:
freceive = receive.replace("{end}","")
print(f"{freceive}")
elif "{split}" in receive:
freceive = receive.replace("{split}","")
print(f"{freceive}", end="")
elif "{start}" in receive:
freceive = receive.replace("{start}","")
print(f"来自 {client_address} 的 log:{freceive}", end="")
else:
print(f"来自 {client_address} 的 log:{receive}")
if receive == "quit":
break
if not receive :
break
# 关闭套接字
server_socket.close()
使用方法
1、把 Python 服务跑起来
2、在安卓项目中使用
NetLog.sendLog("SplashActivity initData jumpToMain")