问题 Java.io.IOException,“坏文件号”USB连接


我正在设置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时一般会导致此错误的原因是什么?


10686
2018-04-12 01:28


起源

您使用的是哪个版本的Android?另外,你想和他们沟通什么?我相信使用附件模式(您使用的是Android开放附件模式吗?),您需要一个与arduino或FT311D兼容的主机设备。 - TronicZomB
Android 4.0.4。我可以很好地获得初始连接,并且没有“兼容”设备这样的东西 - 如果你发送正确的控制请求,如果它支持它,手机就会进入配件模式。我的手机上弹出一个对话框,说明它看到了一个附件 - 只是在保持连接方面遇到问题。
哦,好吧,等它看到一个配件,这意味着你的手机处于主机模式,而不是配件模式吗?哪种设备为电话提供电源,电话到设备或设备? - TronicZomB
不,Android文档实际上与术语有点混淆。它看到一个配件,意味着它进入配件模式。当您在手机上处于配件模式时,您可以进行迭代 UsbAccessory 对象 - 您在配件模式下看到它们。该设备有点像Android平板电脑(我认为也是4.0.4);它为我的手机供电。它通过主机模式进行通信。这一切都工作正常,最终这个“坏文件号”出现了。
好吧,我主要使用Open Accessory模式,所以我只是想弄清楚更具体的内容,看看我能帮助你多少......所以听起来你有两个Android设备通过USB互相通话,一个主人,一个配件?那是对的吗?而你在配件android设备上得到“坏文件号”? - TronicZomB


答案:


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

我通过在Activity(或Service)中添加ParcelFileDescriptor字段来解决此问题。

我检查了你的第一个代码片段和你所基于的代码,后者在Thread中有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);
    }
} 

8
2017-09-22 03:29



感谢您为此做出贡献。我现在已经好几年了,但希望人们觉得它很有用。
这个答案肯定值得更多的赞成,ParcelFileDescriptor是GC的事实并不明显,我有一个方法返回新的FileOutputStream(mPfd.getFileDescriptor())并在几次传输后看到这样的错误。我花了好几个小时,感谢队友! - Miro Kropacek


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

我最终使用了 这段代码 作为基础。


7
2018-04-16 22:51



嗨,我有同样的问题,我已经看到上面的链接,但没有为我工作。请分享什么是线程问题。 - ashokk


好吧,我注意到的一些事情与我为开放附件模式做的事情有所不同,我主要关注USB配件的文档,所以它应该非常相似,是你的 mIn.read(buf); 应该 mIn.read(buf, 0, 64); 我所知道的。

此外,您应该在您的类声明中声明 thread myThread;。在你的内心 BroadcastReceiver 创建新的后 FileInput/OutputStreammyThread = 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){

            }
        }
    }
}

此外,还有一些演示开放配件应用程序 这里。它们可能有助于您理解配件模式。

并且还存在未收到BroadcastReceiver的应用程序的已知问题 ACTION_USB_ACCESSORY/DEVICE_ATTACHED 编程。它只会通过清单文件接收它。你可以在这里找到更多 这里 和 这里

我实际上没有测试 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();

1
2018-04-12 12:32



感谢您的提示,但我没有直接与UI沟通;你从哪里得到那个的?该 mRead 超载是很好的建议,我应该总是指定一个最大值,但它最终没有变化太多。我认为这可能是硬件方面的问题......很难说。
用户界面是一个误读。它也可能是一个硬件问题。我已经在三个不同的平板电脑上测试了开放附件代码,一个完全不起作用的关闭品牌,Nexus 7除了从关闭状态启动以及然后像Nexus一样工作的Galaxy Tab 2 7时工作良好但是没有启动问题。虽然你在哪里开始阅读线程? - TronicZomB
我在多个地方尝试过它;现在在 onCreate。
我编辑了我的答案。输入/输出流声明后立即为您的线程尝试类似的东西。也许那会奏效...... - TronicZomB
我刚注意到的另一件事,试着移动 synchronized() 命令到你之前 ParcelFileDescriptor 在你的BroadcastReceiver中。 - TronicZomB