【问题标题】:Java.io.IOException, "bad file number" USB connectionJava.io.IOException,“错误文件号”USB 连接
【发布时间】:2013-04-12 01:28:10
【问题描述】:

我正在我的 Android 手机和另一台设备之间建立 USB 附件连接。现在只是来回发送字节进行测试。起初我得到了一些明确的沟通,但它总是在一秒钟左右后以Java.io.IOException: write failed: EBADF (Bad file number)" 结束。有时阅读还活着,但写作却死了;其他人都死了。

我并没有像谷歌文档那样做任何超级花哨的事情,阅读和写作:

初始连接(在广播接收器内,我知道这部分至少在最初是有效的):

if (action.equals(ACTION_USB_PERMISSION))
{
    ParcelFileDescriptor pfd = manager.openAccessory(accessory);
    if (pfd != null) {
        FileDescriptor fd = pfd.getFileDescriptor();
        mIn = new FileInputStream(fd);
        mOut = new FileOutputStream(fd);
    }
}

阅读:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        byte[] buf = new byte[BUF_SIZE];
        while (true)
        {
            try {
                int recvd = mIn.read(buf);
                if (recvd > 0) {
                    byte[] b = new byte[recvd];
                    System.arraycopy(buf, 0, b, 0, recvd);
                    //Parse message
                }
            }
            catch (IOException e) {
                Log.e("read error", "failed to read from stream");
                e.printStackTrace();
            }
        }
    }
});
thread.start();

写作:

synchronized(mWriteLock) {
    if (mOut !=null && byteArray.length>0) {
        try {
            //mOut.flush();
            mOut.write(byteArray, 0, byteArray.length);
        }
        catch (IOException e) {
            Log.e("error", "error writing");
            e.printStackTrace();
            return false;
        }
    }
    else {
        Log.e(TAG, "Can't send data, serial stream is null");
        return false;
    }
}

错误堆栈跟踪:

java.io.IOException: write failed: EBADF (Bad file number)
W/System.err(14028):     at libcore.io.IoBridge.write(IoBridge.java:452)
W/System.err(14028):     at java.io.FileOutputStream.write(FileOutputStream.java:187)
W/System.err(14028):     at com.my.android.transport.MyUSBService$5.send(MyUSBService.java:468)
W/System.err(14028):     at com.my.android.transport.MyUSBService$3.onReceive(MyUSBService.java:164)
W/System.err(14028):     at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:781)
W/System.err(14028):     at android.os.Handler.handleCallback(Handler.java:608)
W/System.err(14028):     at android.os.Handler.dispatchMessage(Handler.java:92)
W/System.err(14028):     at android.os.Looper.loop(Looper.java:156)
W/System.err(14028):     at android.app.ActivityThread.main(ActivityThread.java:5045)
W/System.err(14028):     at java.lang.reflect.Method.invokeNative(Native Method)
W/System.err(14028):     at java.lang.reflect.Method.invoke(Method.java:511)
W/System.err(14028):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
W/System.err(14028):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
W/System.err(14028):     at dalvik.system.NativeStart.main(Native Method)
W/System.err(14028): Caused by: libcore.io.ErrnoException: write failed: EBADF (Bad file number)
W/System.err(14028):     at libcore.io.Posix.writeBytes(Native Method)
W/System.err(14028):     at libcore.io.Posix.write(Posix.java:178)
W/System.err(14028):     at libcore.io.BlockGuardOs.write(BlockGuardOs.java:191)
W/System.err(14028):     at libcore.io.IoBridge.write(IoBridge.java:447)
W/System.err(14028):     ... 13 more

我到处都有日志记录,因此我知道这并不是什么太明显的事情,例如收到另一个权限请求(因此文件流在读取过程中被重新初始化)。流也没有关闭,因为我的代码中从来没有发生过这种情况(现在)。我也没有收到任何分离或附加的事件(如果发生,我会记录下来)。似乎没有什么不寻常的。它只是死了。

我认为这可能是一个并发问题,所以我玩了锁和睡眠,但我没有尝试过。我也不认为这是吞吐量问题,因为当我每次读取(在两端)都睡觉时它仍然会发生,并且一次读取一个数据包(超慢比特率)。缓冲区是否有可能以某种方式在另一端被溢出?我将如何解决这个问题?我确实可以访问另一端的代码,它也是一个 Android 设备,使用主机模式。万一这很重要,我也可以发布该代码 - 标准批量传输。

手机对 Android 配件模式的支持是否乏善可陈?我试过两部手机,它们都失败了,所以我怀疑是这样。

我想知道在 Android 上从 USB 写入或读取时通常会导致此错误的原因是什么?

【问题讨论】:

  • 您使用的是什么版本的 Android?另外,你想和什么交流?我相信使用附件模式(您使用的是 Android 开放附件模式吗?)您需要一个兼容的主机设备,如 arduino 或 FT311D。
  • 安卓 4.0.4。我可以很好地获得初始连接,并且没有“兼容”设备之类的东西 - 如果您发送正确的控制请求,手机会进入配件模式(如果它支持)。我的手机上会弹出一个对话框,说明它看到了一个配件 - 只是在保持连接时遇到了问题。
  • 哦,好的,等待它看到一个配件,这意味着您的手机处于主机模式,而不是配件模式,对吧?哪个设备在提供电源,电话到设备还是设备向电话供电?
  • 不,Android 文档实际上与术语有点混淆。它看到一个附件,这意味着它进入了附件模式。当您在手机上处于附件模式时,您可以遍历 UsbAccessory 对象 - 您在附件模式下看到它们。该设备有点像 Android 平板电脑(我认为也是 4.0.4);它正在为我的手机供电。它通过主机模式进行通信。这一切最初都很好,只是这个“坏文件号”最终出现了。
  • 好吧,我大部分时间都在使用开放附件模式,所以我只是想弄清楚更多细节,看看我能帮你多少……所以听起来你有两个 android设备之间通过 USB 通信,一台主机,一个附件?那是对的吗?而您在配件 android 设备上收到“错误文件号”?

标签: java android usb


【解决方案1】:

我的代码中遇到了同样的问题,我发现这是因为 FileDescriptor 对象被 GCed。

我通过在活动(或服务)中添加 ParcelFileDescriptor 字段来解决此问题。

我检查了你的第一个代码 sn-p 和你基于的代码,后者在线程中有 ParcelFileDescriptor 字段。

我认为如果你像下面这样编辑你的代码,它工作得很好。

ParcelFileDescriptor mPfd;
...

if (action.equals(ACTION_USB_PERMISSION))
{
    mPfd = manager.openAccessory(accessory);
    if (mPfd != null) {
        FileDescriptor fd = mPfd.getFileDescriptor();
        mIn = new FileInputStream(fd);
        mOut = new FileOutputStream(fd);
    }
} 

【讨论】:

  • 感谢您为此做出贡献。我已经很多年没有这样做了,但希望人们会觉得它有帮助。
  • 这个答案绝对值得更多的支持,ParcelFileDescriptor 被 GC 处理的事实一点也不明显,我有一个方法返回 new FileOutputStream(mPfd.getFileDescriptor()) 并且在之后看到类似的错误几次转移。我花了很多时间,谢谢伙计!
【解决方案2】:

它最终成为一个线程问题。我需要更恰当地隔离写作,而不仅仅是阅读。

我最终使用this code 作为基础。

【讨论】:

  • 嗨,我有同样的问题,我已经看到了上面的链接,但对我不起作用。请分享线程问题。
【解决方案3】:

好的,我注意到的一些事情似乎与我为打开附件模式所做的不同,我主要遵循 USB 附件的文档,所以它应该非常相似,你的 mIn.read(buf); 应该是 @987654325 @据我所知。

另外,你应该在你的类声明中声明thread myThread;。然后在您的BroadcastReceiver 内创建新的FileInput/OutputStream 后,让myThread = new thread(myHandler, myInputStream); 跟随我的myThread.start();

现在我注意到您在线程中直接与 UI 进行通信。您应该使用一个处理程序,而不是线程将与之通信,然后再与您的 UI 通信,至少从我所读的内容来看。

这是我的处理程序和线程的示例:

final Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg){

    }
};

private class USB_Thread extends Thread {
    Handler thisHandler;
    FileInputStream thisInputStream;

    USB_Thread(Handler handler, FileInputStream instream){
        thisHandler = handler;
        thisInputStream = instream;
    }
    @Override
    public void run(){
        while(true) {
            try{
                if((thisInputStream != null) && (dataReceived == false)) {
                    Message msg = thisHandler.obtainMessage();
                    int bytesRead = thisInputStream.read(USB_Data_In, 0, 63);
                    if (bytesRead > 0){
                        dataReceived = true;
                        thisHandler.sendMessage(msg);
                    }
                }
            }
            catch(IOException e){

            }
        }
    }
}

此外,还有一些演示打开附件应用程序here。它们可能有助于您了解配件模式。

还有一个已知问题是应用程序没有以编程方式接收ACTION_USB_ACCESSORY/DEVICE_ATTACHED 的广播接收器。它只会通过清单文件接收它。您可以在此herehere 上找到更多信息。

我实际上没有测试将dataReceived 变量放入处理程序中,只是最近才更改了我的代码的那部分。我对其进行了测试,但它没有用,所以试图记住我读过的内容,我认为这不是关于线程内的变量通信,而是尝试使用类似 .setText() 的东西。我已经更新了我的代码以在线程中包含dataReceived=true。然后,该处理程序将用于更新 UI 上的项目,例如 TextViews 等。

线程

FileDescriptor fd = mFileDescriptor.getFileDescriptor();
mInputStream = new FileInputStream(fd);
mOutputStream = new FileOutputStream(fd);
usbThread = new USB_Thread(mHandler, mInputStream);
usbThread.start();

【讨论】:

  • 感谢您的提示,但我没有直接与 UI 交流;你从哪里得到那个的? mRead 重载是个好建议,我应该始终指定一个最大值,但它最终并没有太大变化。我认为这可能是硬件方面的问题....很难说。
  • 是的,用户界面是一个误读。这也可能是硬件问题。我已经在三款不同的平板电脑上测试了 Open Accessory 代码,一款完全无法使用的非品牌平板电脑,Nexus 7 运行良好,除非从关闭状态启动,然后是 Galaxy Tab 2 7",它的工作方式与 Nexus 类似但没有启动问题。虽然你在哪里开始你的阅读线程?
  • 我已经编辑了我的答案。在您的输入/输出流声明之后立即为您的线程尝试类似的操作。也许这会奏效......
  • 我刚刚注意到的另一件事,尝试将 synchronized() 命令移到 BroadcastReceiver 中的 ParcelFileDescriptor 之前。
  • 再次感谢您的提示,我尝试了您的一些建议以及其他方法,但仍然失败。然而,我进一步挖掘,我能看到的唯一看起来不合适的是StrictMode$InstanceCountViolation,显然你可以关闭严格模式。试过了,也没有运气。我将尝试在硬件中明确声明我正在使用的手机的权限,看看是否有帮助。
猜你喜欢
  • 2012-12-09
  • 2011-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多