Android开发板ADB设备序列号重复?用transport_id和无线ADB精准区分
2026/9/20 19:05:59 网站建设 项目流程

如果手上有两块同款 Android 开发板,又刷的是同一个固件镜像,第一次插上去很可能被adb devices的输出搞得一头雾水:两行一模一样的序列号,都显示device,但你根本不知道哪一行对应哪块板子。想用adb -s指定其中一块,不是报more than one device,就是运气好连上一块,但连到的是哪块全看心情。这个问题在 RK3568、全志 T113、高通 410c 这类 Android 开发板里特别常见,尤其是一个人同时调多块板子、或者产线批量烧录后要逐台验证的时候,足够浪费一上午。

这篇文章不会只给你一个"拔掉另一台再连"的临时解法,而是把 ADB 识别设备的原理、transport_id 的用法、无线 ADB 的切换、以及从根源上把序列号改干净的方法都梳理一遍。适合正在被同类问题折磨的嵌入式开发、安卓系统集成、测试和产线工具链开发的同学直接抄作业。

1. 先弄明白:ADB 到底拿什么当设备 ID

1.1adb devices里的序列号是什么

很多人的第一反应是:adb devices显示的不就是硬件序列号吗?既然是硬件序列号,两台板子怎么会一样?

这里有个认知偏差。ADB 在 USB 连接模式下,设备显示出来的 serial 并不一定等于你刻在芯片外壳上的那个硬件序列号。它首先来自 USB 设备描述符里的iSerialNumber字段,而 Android 系统在启动时,通常把ro.serialno这个系统属性的值填到 USB gadget 的序列号里。ro.serialno则来自 bootloader 传给内核的androidboot.serialno参数,或者某些平台上的固定存储区。

问题就出在这个链路。很多开发板方案商出厂的工程固件,根本没有在 bootloader 里写入独立序列号,或者写死了一个公共默认值。于是不管烧多少台板子,系统起来后ro.serialno都是同一个字符串,USB 描述符里的iSerialNumber自然也就完全相同。你看到的"硬件序列号相同",实际上是"系统上报的序列号相同",不一定代表底层芯片 ID 真的重复。

所以第一步不要急着怀疑硬件坏了。先分别执行:

adb shell getprop ro.serialno adb shell getprop ro.boot.serialno adb shell cat /sys/class/android_usb/android0/iSerial

如果三个值都一样,而且跟adb devices里显示的一致,就基本能确认是固件层面写死了序列号。如果ro.serialnoro.boot.serialno不同,那更说明序列号链路里有一个环节没有把唯一 ID 送上来。

1.2 两行相同序列号会引发什么乱子

正常情况下,ADB server 给每个设备分配一个 transport,每个 transport 有一个唯一的 serial 字符串。客户端用adb -s <serial>指定设备时,ADB server 会在所有 transport 里精确匹配这个字符串。一旦有两台设备的 serial 完全一样,精确匹配就失去意义了。

ADB 的匹配逻辑在不同版本里行为不太一样。老一点版本遇到重复 serial,直接提示more than one device/emulator;新一些的版本可能默认选择其中一个 transport 执行命令,但你没法预测选的是哪一块。更麻烦的是,像adb installadb shelladb reboot这种命令,一旦连错了板子,轻则装错包,重则把正在调试的板子重启掉,测试数据全丢。

那还能不能区分?能。ADB server 虽然对客户端隐藏了一部分细节,但adb devices -l会额外暴露一个字段:transport_id。这个 ID 是 ADB server 为每个 transport 分配的自增编号,只要设备连接没断开,它就不会重复,也不会因为 serial 相同而混在一起。

adb devices -l

输出里会出现类似:

ABCDEF123456 device product:rk30board model:RK3566 device:rockchip transport_id:1 ABCDEF123456 device product:rk30board model:RK3566 device:rockchip transport_id:2

注意看,serial 两行一模一样,但transport_id分别是 1 和 2。这就是我们精准定位每一块板子的钥匙。

1.3 为什么说 transport_id 是突破口

只要 ADB server 还活着,transport_id 就是全局唯一的。你可以用adb -t <transport_id>来绕过 serial 的重复问题:

adb -t 1 shell getprop ro.serialno adb -t 2 shell getprop ro.serialno

这两条命令会分别落在两块不同的板子上,不会再出现"选到哪台算哪台"的情况。

但 transport_id 有个特点:它是 adb server 在运行时分配的连接编号。你adb kill-server、重启电脑、拔插 USB,transport_id 都可能重新分配。比如刚才 transport_id 1 是编号靠前的板子 A,重启后可能变成 transport_id 2 或 3。所以 transport_id 适合在当次调试会话里使用,不适合写死在长期脚本里。

理解了这一点,后面的所有操作方法就都有了解释。你可以按 transport_id 来手动指定,也可以通过无线 ADB 让不同板子以IP:port的形式出现在设备列表里,还可以直接把底层序列号改成唯一值,让问题从根源消失。

2. 三条区分路线:物理隔离、IP 直连、改序列号

在动手之前,先总览一下三个方向,根据场景选合适的,别一上来就折腾底层分区。

方案操作成本稳定性适用场景
物理隔离,同一时间只连一台最低最高只有一块板子需要反复调试时
transport_id 精准指定USB 多设备同机连接,临时区分
无线 ADB,用 IP 做设备 ID需要长期保持多台在线
改掉重复序列号中高最高批量设备管理、产线工具链

2.1 物理隔离:最朴素但永远有效

这是最不容易出错的思路。既然 ADB 认不了相同的 serial,那我就不让它同时出现在设备列表里。插上板子 A,执行完所有命令,拔出,再插板子 B。对只有一两块板的个人调试场景,这个方案足够。

不过要注意,单纯物理隔离不代表什么都不用管。插上 A 之后,最好先跑一遍adb kill-serveradb devices,避免 adb server 缓存了上一次的 USB 状态。实际测试中,插拔顺序反了确实会遇到设备状态变成offline的情况。

这类方案适合开会前救急,但如果你每天要面对二三十块开发板,物理插拔能把人累到怀疑人生。

2.2 无线 ADB:把设备 ID 强制变成 IP:port

无线 ADB 的核心思路是:绕开 USB serial 这套标识,让设备以IP:port的身份出现在 ADB 设备列表里。只要两台板子 IP 不同,ADB 就不会再把它们认成同一台设备。

操作上需要先在 USB 连接状态下给每台板子打开 TCP 监听端口:

adb tcpip 5555

然后通过网络连接:

adb connect 192.168.1.101:5555 adb connect 192.168.1.102:5555

现在再看adb devices,两行显示的是192.168.1.101:5555192.168.1.102:5555,天然可区分。之后所有命令都可以带-s指定 IP,不会再跟序列号扯上关系。

无线方案有个前置麻烦:如果两台板子当前都通过 USB 连着,而 serial 又完全相同,你没法用adb -s去执行adb tcpip。这时候要么先物理隔离,要么用adb -t指定 transport_id。实际操作中我建议直接用adb -t,一条命令的事。

2.3 改序列号:从根上解决

物理隔离和无线 ADB 都是绕路,真正干净的做法是让每一块板子的序列号唯一。序列号的最终来源通常在 bootloader 层面,不同芯片平台的改法不同。Rockchip 一般在 parameter/UBOOT 环境变量里配置;全志平台可能在 boot_package 或 sys_config 里;高通平台可以走 fastboot 的序列号分区。

对普通开发者来说,最快的验证办法是先在 userdebug/eng 固件上临时改一改系统属性:

adb root adb shell resetprop ro.serialno UNIQUE_SN_001 adb kill-server adb devices

resetprop可以覆盖只读属性,但前提是固件允许 root,而且改完之后要重启 adbd 才能让 USB serial 刷新。这种方法适合验证流程,不代表量产时能用。量产固件需要在烧录阶段给每台板子烧入不同的androidboot.serialno,这是方案商和产线应该做的底层配置。

如果你只是自己维护几块板子,又不想动 bootloader,那把无线方案和标签台账配合起来,也比一直忍受重复 serial 强得多。

3. 实操:不同场景下的连接步骤

3.1 准备工作:版本、驱动和授权

开始之前,先把环境理一遍,否则后面排查起来很乱。

ADB 工具版本尽量新一些,直接使用 Android SDK 自带的platform-tools。打开终端执行:

adb version

版本号在 30 以上,一般就能完整显示transport_id字段。太老的 adb 版本可能只在adb devices -l里显示 serial,不给你 transport_id,那就很难办。

Windows 上如果插上开发板驱动装不上,优先处理 udev 或者通用 ADB 驱动。Linux 下需要在/etc/udev/rules.d/51-android.rules里把开发板的 USB Vendor ID 加进去,并chmod a+r,然后重启 udev:

sudo udevadm control --reload-rules sudo udevadm trigger

接着打开开发板的开发者选项和 USB 调试。第一次连接时,板子上会弹"是否允许 USB 调试"的对话框,勾选"一律允许"可以省掉后面很多unauthorized问题。

3.2 场景 A:只有一台需要连接,按顺序轮换

这个场景最简单,但也要规范步骤。

  1. 把板子 A 插到电脑 USB 口,执行adb kill-server && adb start-server
  2. 执行adb devices,确认状态是device
  3. 操作完直接adb rebootadb disconnect,然后拔出 USB。
  4. 插上板子 B,重新执行adb devices

如果板子 A 在关掉 USB 调试或重启后没有被正确释放,再插 B 时可能出现offline。这时候不要慌,先adb kill-server,拔掉 B,插 A,走一遍正常流程,再换 B。

这套方案虽然笨,但对不会配置网络的场景非常有效,尤其是在没法给开发板分配固定 IP 的隔离网环境里。

3.3 场景 B:两台板子同时插在同一个主机上

这是这篇文章的核心场景。要同时管理两台及以上重复序列号的板子,transport_id是首选。

先把两块板子分别插到电脑的两个 USB 口。最好插在不同 USB 控制器对应的口上,比如一个插主板后置 USB 3.0,一个插前置 USB 2.0,减少电气干扰。然后:

adb devices -l

你会看到两行 serial 相同但 transport_id 不同的记录。接下来用-t参数指定:

adb -t 1 shell getprop ro.serialno adb -t 2 shell getprop ro.serialno

怎么确认 transport_id 1 是哪块板子?既然序列号一样,不能靠 serial 判断,可以用物理特征、MAC 地址或者其他配置来反推。比如:

adb -t 1 shell cat /sys/class/net/wlan0/address adb -t 2 shell cat /sys/class/net/wlan0/address

如果两块板子的无线 MAC 不同,你就能通过 MAC 把 transport_id 和实际板卡对应起来。没有无线网卡的话,也可以读存储分区信息、外设地址,或者给一块板子拔掉网络再执行ping之类的命令做区分。

如果需要反复操作多个 id,写个脚本来枚举最省事:

for id in $(adb devices -l | sed -n 's/.*transport_id:\([0-9]*\).*/\1/p'); do echo "===== transport_id: $id =====" adb -t "$id" shell getprop ro.serialno adb -t "$id" shell cat /sys/class/net/wlan0/address done

这个脚本会把当前在线所有 transport 遍历一遍,打印序列号和 MAC。对重复 serial 的板子来说,MAC 往往是更好的区分依据。

adb logcat抓日志也是一样的逻辑,想抓哪台就指定哪个 transport:

adb -t 1 logcat -c adb -t 1 logcat > board1_log.txt adb -t 2 logcat > board2_log.txt

这个方法能让你在同一台电脑上同时跟踪两块板子的日志,效率和舒适度都明显提升。

3.4 场景 C:无线 ADB 连接多台板子

如果开发板已经接入局域网,我更推荐切换到无线模式。下面是一个完整流程:

  1. 先通过 USB 把两台板子都连上主机。
  2. 执行adb devices -l,拿到 transport_id。
  3. 分别执行:
adb -t 1 tcpip 5555 adb -t 2 tcpip 5555
  1. 查看板子 IP。可以用adb -t 1 shell ip addr show wlan0,也可以用串口、路由器后台确认 IP。
  2. 网络连接:
adb connect 192.168.1.101:5555 adb connect 192.168.1.102:5555
  1. 查看列表:
adb devices

这时候列表里的设备 ID 就是IP:port,不再相同。后续指定设备直接写:

adb -s 192.168.1.101:5555 shell adb -s 192.168.1.102:5555 logcat

无线连接在某些开发板上有一个小坑:USB 模式下执行adb tcpip 5555后,USB 连接会断开,显示offline或直接消失,这不算异常。还有一种情况是板子重启后不会自动打开 tcpip 监听,需要重新执行一次adb tcpip 5555。想让一劳永逸,可以把setprop service.adb.tcp.port 5555写进开机脚本,或者用adb -t配合自动化脚本在每次重启后统一配置。

为了减少 IP 漂移问题,尽量在路由器里给每块板子绑定静态 DHCP,或者在板子网络配置里写死静态 IP。否则板子重启后 IP 变了,ADB 连接列表里还会留着旧的offline记录,干扰判断。

4. 常见报错与排查清单

4.1adb unauthorized反复出现

这是开发板调试里最常见的拦路虎。原因通常是电脑端的 adb key 和设备端的授权记录没对上。有两种处理思路:

一是重新授权。在板子上取消"USB 调试"授权,或者在开发者选项里清除授权记录,拔插 USB 后重新点"允许"。

二是清理电脑端的 key:

adb kill-server rm -f ~/.android/adbkey ~/.android/adbkey.pub adb start-server

执行完再插上板子,重新弹窗授权。这个操作适合换电脑、换系统或者批量拷贝过 adb key 的场景。

4.2adb devices只显示一台板子

两台板子同时插上,却只出现一台,大概率不是 serial 问题,而是 USB 驱动或物理链路问题。先检查:

  • 线材是否支持数据传输,有些充电线只能充电不能传数据。
  • 开发板用的是哪个 USB 口,很多板子的 OTG 口和普通 USB Host 口混在一起,插错了不会触发 adbd。
  • Windows 上是否两个设备用了同一个驱动实例,拔掉一个再重新插,看设备管理器有没有未知设备。
  • 板子供电是否稳定,USB Hub 供电不足会导致 adbd 起不来。

如果确认物理链路正常,可以adb kill-server后重新adb devices。还不行就把 adb server 彻底杀掉,拔掉所有设备,从第一台开始重新枚举,基本能定位。

4.3adb offlinemore than one device

offline状态一般说明 adb server 和设备端版本不匹配,或者设备端 adbd 异常。先检查 USB 线、换一个 USB 口,再执行:

adb kill-server adb start-server adb devices

如果还是 offline,就在板子上重启 adbd:

  • 有 root 权限:adb root之后adb shell setprop ctl.restart adbd
  • 没 root 权限:直接adb reboot

出现more than one device/emulator报错时,说明你用了-s但 serial 不唯一,或者没带任何指定参数。看到这个报错,用adb devices -l查 transport_id,然后用-t替代-s,问题立刻解决。

4.4 无线连接失败:cannot connect to 192.168.x.x:5555

最常见的是板子和电脑不在同一网段,或者路由器开了 AP 隔离。先确认两边 IP 能互相 ping 通。开发板上有ping工具的话,可以直接在adb shell里测试。还有就是防火墙拦截了 5555 端口,需要放行 TCP 5555。

还有个小细节:adb connect默认走 5555,但有些定制开发板把 adbd 的默认端口改了。这时候adb connect IP:端口里的端口要对应改。

5. 从临时连到量产:给开发板设备管理的一些建议

5.1 序列号唯一化要趁早做

如果这批板子以后要长期使用,最好别一直靠 transport_id 和 MAC 区分现场,越早把序列号刷唯一越好。

对量产板,核心是在烧录阶段写入唯一标识。具体做法依赖平台,但原理都差不多:bootloader 读取某个存储区域或烧录参数,把值传给内核,内核再以androidboot.serialno=xxxx的形式传给 Android init,最终反映到ro.serialno。可以把这个值做成烧录脚本里的一个变量,批量生产时逐个递增。

对已经拿在手里的工程板,如果固件允许 root,可以试试:

adb root adb shell resetprop ro.serialno BOARD_A_001 adb kill-server adb devices

注意覆盖只读属性不一定在所有机型上都生效,而且重启后会恢复。想固化,还是要改 bootloader 或系统启动脚本。

5.2 建立最简单的设备台账

哪怕只有三五块板子,也建议建一个表格,记录:

  • 板子上贴的标签名
  • 当前ro.serialno实际值
  • 无线网卡 MAC 地址
  • 固定 IP
  • 系统版本和烧录时间
  • 备注(比如哪块板接了传感器、哪块是备用)

我自己的习惯是每块板子拿到手,先通过 USB 执行一次信息收集:

for prop in ro.serialno ro.product.board ro.build.version.release; do echo "$prop=$(adb -t 1 shell getprop $prop | tr -d '\r')" done

然后把输出和板子上的标签贴在同一个表格里。后面无论设备列表里 serial 有多乱,只要拿到 transport_id,读一次 MAC,就能对上号。

5.3 用脚本封装日常操作

如果每天都要在多块板子之间切换,写一个简单的 bash 函数能省很多时间。比如把板名和 transport_id 的对应关系记在文件里,然后包一层命令:

function adb_board() { local board="$1" shift local tid=$(grep "^$board " /tmp/board_map.conf | awk '{print $2}') adb -t "$tid" "$@" }

配合之前的枚举脚本,先拿到 transport_id 和 MAC 的映射,更新到配置文件里,然后就能用adb_board A logcat这种抽象命令操作指定板子。代码量不大,但能避免反复复制粘贴一长串-t参数。

有一点要提醒:只要 adb server 重启,transport_id 就会变,所以脚本里最好每次从adb devices -l动态获取,而不要完全依赖上一次的记录。

说实话,这个问题不难,但很多人卡在第一步:默认认为adb devices给出的 serial 就是板上绝对唯一的硬件信息。实际上 ADB 的标识链路里,序列号只是其中一环,而 USB 描述符、系统属性、bootloader 参数都可能成为"相同"的源头。搞清楚了这一点,再用 transport_id 和无线 IP 去绕开重复,基本就能在各种开发板上自由操作了。

最后再分享一个小技巧:如果哪天你又遇到设备列表混乱,先别急着拔线,执行一行adb devices -l,把 transport_id、MAC、serial 打出来贴到记事本里。这个习惯看起来简单,但能帮你省下大量靠猜和靠运气连设备的时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询