如何终止新终端中启动的rfcomm watch子进程并恢复蓝牙连接?
解决方法
这个问题我之前也碰到过——用终端启动子进程的时候,直接杀终端进程有时候搞不定后台的rfcomm,毕竟它还套了sudo。给你几个靠谱的解决思路,按你的需求选:
方法1:启动时记录rfcomm的PID(最可靠)
因为你是通过lxterminal启动的rfcomm watch,直接杀终端进程可能留着rfcomm在后台跑。不如在启动时把rfcomm的PID写入临时文件,后续直接通过PID精准杀进程。
修改你的启动命令,用shell脚本包裹,把rfcomm的PID存起来:
String BLUETOOTH_CHANNEL = "0"; // 替换成你的通道号 // 用sh -c包裹逻辑:启动rfcomm后把PID写入临时文件,同时wait住避免终端立即关闭 String launchCmd = String.format( "lxterminal -e sudo sh -c 'rfcomm watch %s & echo $! > /tmp/rfcomm_watch_%s.pid; wait'", BLUETOOTH_CHANNEL, BLUETOOTH_CHANNEL ); Process terminalProc = Runtime.getRuntime().exec(launchCmd);
当需要终止连接时,读取PID文件并杀掉rfcomm,再关闭终端:
File pidFile = new File(String.format("/tmp/rfcomm_watch_%s.pid", BLUETOOTH_CHANNEL)); if (pidFile.exists()) { try { String pidStr = new String(Files.readAllBytes(pidFile.toPath())).trim(); int rfcommPid = Integer.parseInt(pidStr); // 杀掉rfcomm进程 Process killProc = Runtime.getRuntime().exec(String.format("sudo kill %d", rfcommPid)); int exitCode = killProc.waitFor(); if (exitCode == 0) { System.out.println("rfcomm进程已终止,设备文件已释放"); pidFile.delete(); // 清理临时PID文件 terminalProc.destroy(); // 关闭终端窗口 } else { System.err.println("终止rfcomm失败,可能进程已不存在"); } } catch (IOException | InterruptedException | NumberFormatException e) { e.printStackTrace(); } }
方法2:通过设备文件找到占用进程
如果不想改启动命令,可以直接查哪个进程在占用/dev/rfcomm<CHANNEL>,然后杀掉它:
String devicePath = String.format("/dev/rfcomm%s", BLUETOOTH_CHANNEL); // 用lsof获取占用设备的PID(-t参数只返回PID,简化输出) Process lsofProc = Runtime.getRuntime().exec(String.format("sudo lsof -t %s", devicePath)); String pidStr = new BufferedReader(new InputStreamReader(lsofProc.getInputStream())).readLine(); if (pidStr != null && !pidStr.isEmpty()) { int pid = Integer.parseInt(pidStr); Process killProc = Runtime.getRuntime().exec(String.format("sudo kill %d", pid)); killProc.waitFor(); terminalProc.destroy(); // 顺便关掉终端窗口 }
注意:
lsof工具大多数Linux发行版默认自带,如果没有可以用apt install lsof或对应的包管理器安装,而且执行需要sudo权限。
方法3:直接杀掉终端的子进程(Java 9+适用)
如果你用的是Java 9或更高版本,可以通过Process.pid()获取终端进程的PID,然后用pgrep找它的子进程(也就是rfcomm):
long terminalPid = terminalProc.pid(); // pgrep -P 表示查找父进程为terminalPid的子进程,指定rfcomm过滤结果 Process pgrepProc = Runtime.getRuntime().exec(String.format("pgrep -P %d rfcomm", terminalPid)); String rfcommPidStr = new BufferedReader(new InputStreamReader(pgrepProc.getInputStream())).readLine(); if (rfcommPidStr != null) { int rfcommPid = Integer.parseInt(rfcommPidStr); Runtime.getRuntime().exec(String.format("sudo kill %d", rfcommPid)).waitFor(); terminalProc.destroy(); }
局限性:如果
rfcomm进程因为sudo变成了root的子进程,普通用户的pgrep可能看不到父子进程关联,这时候方法1或2更稳定。
关键注意事项
- sudo权限问题:你的Java程序执行
sudo命令时可能会要求输入密码,导致进程卡住。解决办法是在/etc/sudoers里添加规则,让运行Java程序的用户可以无密码执行rfcomm和kill:
编辑sudoers一定要用your_username ALL=(ALL) NOPASSWD: /usr/bin/rfcomm, /usr/bin/killvisudo命令,避免语法错误导致sudo失效。 - 进程清理:杀掉
rfcomm后,记得清理临时PID文件(方法1),避免下次启动时读到旧的无效PID。
内容的提问来源于stack exchange,提问作者DeMo
相关产品推荐
相关产品推荐

