android 远程 日志 log 的实现方案

201 阅读2分钟

如题,我是通过 UDP 通信实现安卓设备在局域网发送 log 日志到 PC 端

使用场景

  1. 安卓设备不能连接电脑
  2. 不支持无线 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")