Android调用ip neigh show命令进程返回退出码1且InputStreamReader无输出
问题原因
- 权限差异:你在adb shell下执行命令时,身份是高权限的
shell用户,而App运行时是普通第三方应用的独立UID,权限等级完全不同。从Android 10(API 29)开始,系统严格限制普通应用读取ARP缓存表的权限,ip neigh show命令正好需要该权限,所以执行时返回退出码1,无正常输出。 - 未读取错误流:你当前只读取了进程的
inputStream(正常输出流),命令执行的报错信息全部在errorStream中,所以看不到具体报错原因。 - 命令解析坑:直接向
Runtime.exec()传入带空格的整串命令,部分场景下系统会解析错误,更稳妥的方式是用字符串数组拆分命令和参数。
解决方案
第一步:添加并动态申请必要权限
在AndroidManifest.xml中添加权限声明:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
注意:定位权限属于Android危险权限,需要在代码中动态申请,用户同意后才能生效。
第二步:调整代码逻辑,增加错误流读取
修改后的执行代码示例:
// 用数组拆分命令和参数,避免解析错误 val p = Runtime.getRuntime().exec(arrayOf("/system/bin/ip", "neigh", "show")) // 先读取错误流排查问题 val errorInfo = InputStreamReader(p.errorStream).buffered().readText() Log.d("IP_CMD_ERROR", errorInfo) // 读取正常输出 InputStreamReader(p.inputStream).buffered().forEachLine fori@{ line -> val teDhenat = line.split("\\s+".toRegex()) if (teDhenat[0] == ip) { if (teDhenat.size < 4) { return@fori } else { uGjet = true macAddress = teDhenat[4] return@fori } } } // 等待进程执行完毕,确认退出码 val exitCode = p.waitFor() Log.d("IP_CMD", "执行完毕,退出码:$exitCode")
你可以通过打印的errorInfo确认具体报错原因,90%以上的概率是权限不足的提示。
替代方案(适配高版本系统限制)
如果申请权限后依旧无法执行,说明你测试的ROM对ip命令做了额外限制,可改为直接读取ARP缓存文件实现相同需求:
try { BufferedReader(FileReader("/proc/net/arp")).use { reader -> var line: String? // 跳过表头第一行 reader.readLine() while (reader.readLine().also { line = it } != null) { val split = line?.split("\\s+".toRegex()) ?: continue if (split.size < 4) continue val currentIp = split[0] val currentMac = split[3] if (currentIp == ip) { uGjet = true macAddress = currentMac break } } } } catch (e: Exception) { e.printStackTrace() }
如果连/proc/net/arp也无法读取,那就是Android 11+的强制权限限制,普通第三方应用无法绕过,需要使用系统级API实现对应需求。
内容的提问来源于stack exchange,提问作者EAK TEAM
相关产品推荐
相关产品推荐

