我正在设置Android手机和其他设备之间的USB配件连接。只是来回发送字节进行测试。我一开始就得到了一些明确的沟通,但是在一秒左右之后,它总会在Java.io.IOException: write failed: EBADF (Bad file number)"
时死亡。有时阅读仍然存在,但写作已经消亡;其他人都死了。
我没有像Google文档那样做任何超级想象,阅读和写作的事情:
初始连接(在广播接收器内部,我知道这部分至少在最初工作):
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或从USB读取时导致此错误的原因是什么?
答案 0 :(得分:8)
我的代码中遇到了同样的问题,我发现它发生了,因为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);
}
}
答案 1 :(得分:7)
它最终成为一个线程问题。我需要更恰当地隔离甚至写作,而不仅仅是阅读。
我最终以this code为基础。
答案 2 :(得分:1)
好的,我注意到的一些事情与我对开放附件模式的看法有所不同,我主要关注USB配件的文档,所以它应该非常相似,是你的mIn.read(buf);
应该是mIn.read(buf, 0, 64);
据我所知。
此外,您应该在您的班级声明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
的BroadcastReceiver。它只会通过清单文件接收它。您可以在here和here上找到更多信息。
我实际上没有测试将dataReceived
变量放在处理程序中,并且最近才更改了我的代码部分。我测试了它并且没有用,所以试着记住我读过的内容,我认为这不是关于在线程内进行通信的变量,而是尝试使用像.setText()
这样的东西。我已更新我的代码以在线程中包含dataReceived=true
。然后,处理程序将用于更新UI上的项目,例如TextView
s等。
主题
FileDescriptor fd = mFileDescriptor.getFileDescriptor();
mInputStream = new FileInputStream(fd);
mOutputStream = new FileOutputStream(fd);
usbThread = new USB_Thread(mHandler, mInputStream);
usbThread.start();