11

Android フォンと別のデバイスの間で USB アクセサリ接続をセットアップしています。テストのために今のところバイトを前後に送信するだけです。最初は確実なコミュニケーションが取れますが、いつも1Java.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

私はいたるところにログを記録しているため、別の許可要求が受信されているなど、あまり明白ではないことがわかっています(したがって、ファイルストリームが読み取り中に再初期化されます)。ストリームも閉じていません。これは、コードのどこでも発生したことがないためです (今のところ)。デタッチまたはアタッチされたイベントも取得していません(発生した場合はログに記録します)。異常なことは何もありません。それはただ死ぬだけです。

並行性の問題かもしれないと思ったので、ロックとスリープを試してみましたが、何も機能しませんでした。スループットの問題ではないと思います。なぜなら、すべての読み取りを (両端で) スリープ状態にし、一度に 1 つのパケットを読み取る (非常に遅いビットレート) ときにも発生するからです。バッファが何らかの形で相手側でオーバーランしている可能性はありますか? これをクリアするにはどうしたらよいでしょうか?ホストモードを使用して、相手側のコードにもアクセスできます。これは Android デバイスでもあります。重要な場合は、そのコードも投稿できます-標準の一括転送。

Androidアクセサリモードのサポートが不十分なだけですか?私は 2 台の電話を試しましたが、どちらも同じように失敗するので、それは間違いです。

AndroidでUSBから書き込みまたは読み取りを行うときに、一般的にこのエラーが発生する原因は何ですか?

4

3 に答える 3

8

コードで同じ問題が発生しましたが、FileDescriptor オブジェクトが GC されたために発生することがわかりました。

アクティビティ (またはサービス) に ParcelFileDescriptor フィールドを追加して、この問題を修正しました。

最初のコード スニペットとベースにしたコードを確認しましたが、後者のスレッドには 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);
    }
} 
于 2015-09-22T03:29:08.383 に答える
1

OK、私が気付いたいくつかのことは、私がオープン アクセサリ モードで行っていることとは異なっているように見えました。これは、ほとんど 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をスレッドに含めるようにコードを更新しました。TextViewハンドラーは、 sなどの UI 上のアイテムを更新するために使用されます。

スレッド

FileDescriptor fd = mFileDescriptor.getFileDescriptor();
mInputStream = new FileInputStream(fd);
mOutputStream = new FileOutputStream(fd);
usbThread = new USB_Thread(mHandler, mInputStream);
usbThread.start();
于 2013-04-12T12:32:03.787 に答える