web-dev-qa-db-ja.com

Android)でBluetoothのInputStreamからデータを読み取るときにエラーが発生しました

私はAndroidアプリに取り組んでいます。このアプリは、Bluetooth接続を使用して、SPPプロファイルを使用してAndroidスマートフォンとAndroid以外のBluetoothモジュール間でデータを転送します。 Android開発者サイトのBluetoothチャットの例を参照として使用しました。

2つのデバイスを相互に接続し、スマートフォンからBluetoothモジュールに簡単な文字列を送信することに成功しました。しかし、モジュールから返送されたデータの読み取り中にエラーが発生しました。次のコードを使用しました。これはBluetoothチャットの例とまったく同じで、InputStreamからデータを読み取ります。

while (true) {
    try {
        // Read from the InputStream
        bytes = mmInStream.read(buffer);
        String str = new String(buffer);
        Log.i(TAG, "mmInStream - " + str);
        // Send the obtained bytes to the UI Activity
        mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
                .sendToTarget();
    } catch (IOException e) {
        Log.e(TAG, "disconnected", e);
        connectionLost();
        break;
    }
}

Bluetoothモジュールが単純な文字列を電話に送信すると、その文字列が正しく受信されません。それはランダムな方法でいくつかの部分に分割されます。たとえば、「1234567890abcdef1234567890abcdef0123456789」を電話に3回送信すると、EclipseのLogcatは次のログを記録します。

mmInstream - 12345678910abcdef��������(continuing null)
mmInstream - 1��������(continuing null)
mmInstream - 2345678910abcdef0123456789��������(continuing null)

初めて。 2回目と3回目のデータ送信では、データは異なる部分で受信されます。

mmInstream - 1234567891�������(continuing null)
mmInstream - 0abcdef012�������(continuing null)
mmInstream - 3456789���������(continuing null)

mmInstream - 1234567891����������������(continuing null)
mmInstream - 0abcdef0123456789������������(continuing null)

なぜこれが起こるのか、そしてこの問題を解決する方法がわかりません。このように任意の方法でデータを受信すると、処理に必要なデータを取得できません。どうすればワンピースにできますか?

どんな助けでもいただければ幸いです。

どうもありがとう。

16
theman

私があなたのコードで気づいた2つのこと:

  • まず第一に、あなたが読んだバッファへの参照をアプリにさらに送信することは必ずしも良い解決策ではありません:その間にバッファが上書きされた場合はどうなりますか? たとえば、stackoverflowでこのバグを参照してください Bluetoothから読み取ったデータのコピーを作成する(たとえば、buffer.clone()を使用する)か、使用したくない場合は、これをバイパスできますメモリが多すぎると、読み取りバッファを循環型にすることができます。

  • データが別々のパケットで受信された場合でも、データを再コンパイルできるはずです(ただし、パケットは短い時間で受信されます)。たとえば、開始/停止フラグを作成できます。多くの場合、Bluetooth経由で送信するオブジェクトの種類によって異なります...

そして、前の2つの警告が役に立たない場合に考えられる解決策は、次のとおりです。

.read(ブロッキング呼び出し)を呼び出す無限ループの代わりに、次のようなことを行うことができます。

while(true) {
     if mmInStream.getAvailable()>0 { 
          -your read code here-
     }
     else SystemClock.sleep(100);
}

これはハックであり、メッセージの一部しか読み取れない場合もありますが、非常にまれです。

Plsは有用であれば投票する/修正する!

15
Radu

私はこの問題を抱えており、このキャラクターの問題をこのように解決しました

public void run() {        
    int bytes; // bytes returned from read()
    int availableBytes = 0;        
    // Keep listening to the InputStream until an exception occurs
    while (needRun) {
        try {
            availableBytes = mmInStream.available();
            if(availableBytes > 0){
                byte[] buffer = new byte[availableBytes];  // buffer store for the stream
                // Read from the InputStream


                bytes = mmInStream.read(buffer);
                Log.d("mmInStream.read(buffer);", new String(buffer));
                if( bytes > 0 ){                        
                    // Send the obtained bytes to the UI activity
                    mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();                     
                }                                   
            }
        } catch (IOException e) {
            Log.d("Error reading", e.getMessage());
            e.printStackTrace();
            break;
        }
    }
}
3
papucho

私は最後の2つのコードを使用しましたが、接続が失われた場合、ユーザーインターフェイスは通知されないため、状態はSTATE_NONEに変更されません。私の場合、接続が失われるたびにアプリが最後のデバイスを再接続しようとします!!多くの方法を試した後、私は最終的にこの方法で問題を解決しました:

  1. END文字を作成しました。この文字は使用しないことがわかっていますが、送信する文字列の最後にあります。 「。」文字列の終わりです。
  2. 一時的な文字列tmp_msgを作成しました。
  3. ストリームが受信されるたびに、そのストリームから2番目の一時的な文字列「readMessage」が作成されます。
  4. 「readMessage」で終了文字(「。」)が検索されます。
  5. 終了文字が検出されない場合、「readMessage」はtmp_msgに連結されます。したがって、終了文字が1回目、2回目、3回目に受信されない場合、tmp_msg = readMessage1 + readMessage2 + readMessage3。
  6. 終了文字「。」の場合が検出されると、tmp_msgは最後のreadMessageと連結され、バイト「バッファ」がそれから構築されます。
  7. 次に、「バッファ」がユーザーインターフェイスに送信され、tmp_msgが「」(空の文字列)に再初期化されます。コード全体は次のとおりです(BluetoothChatアクティビティで変更を行う必要はありません)。

    public void run() { 
        Log.i(TAG, "BEGIN mConnectedThread");
        byte[] buffer = new byte[1024];
        int bytes;
        // Keep listening to the InputStream while connected
        String tmp_msg =""; 
        while (true) {
            try {
                // Read from the InputStream
                bytes = mmInStream.read(buffer);
                String readMessage = new String(buffer, 0,bytes);
                if (readMessage.contains(".")){
                    tmp_msg+=readMessage;
                    byte[] buffer1 = tmp_msg.getBytes();
                    int bytes1=buffer1.length;
                    tmp_msg="";
                    // Send the obtained bytes to the UI Activity
                    mHandler.obtainMessage(BluetoothChat.MESSAGE_READ,bytes1,-1,buffer1).sendToTarget();
                }else{
                    tmp_msg+=readMessage;
                }
            } catch (IOException e) {
            //  Log.e(TAG, "disconnected", e);
                connectionLost();
                // Start the service over to restart listening mode
                BluetoothChatService.this.start();
                break;
            }
        }
    }
    
1
mea.samir

Raduの修正は素晴らしい働きをします!。私自身、Bluetoothチャットのサンプルコードを使用して、かなり長い間この問題に取り組んできました。以下は、リモートセンサーからの温度測定値をキャプチャして表示するために使用しているものです。

  // Keep listening to the InputStream while connected
        while (true) {

             try {
                 byte[] buffer = new byte[128];
                 String readMessage;
                 int bytes;
                if (mmInStream.available()>2) { 
                    try {
                       // Read from the InputStream
                        bytes = mmInStream.read(buffer); 
                        readMessage = new String(buffer, 0, bytes);

                       }catch (IOException e) {
                        Log.e(TAG, "disconnected", e);
                        break;
                    }
                     // Send the obtained bytes to the UI Activity
                     mHandler.obtainMessage(HomeBlueRemote.MESSAGE_READ, bytes, -1, readMessage)
                          .sendToTarget();
        }
        else {
            SystemClock.sleep(100);
               }
            } catch (IOException e) {

                e.printStackTrace();
            }

       }

    }

ご覧のとおり、(buffer.available()> 0)を> 2に変更しました。これは、マイクロコントローラーが温度に対して2バイトを送信しているためです。 。この修正の前は、バイトカウントの入力ストリームは変化し、Androidアプリでの温度表示の読み取りを台無しにする1バイトのみをキャプチャすることがありました。繰り返しになりますが、ウェブ上のすべての提案、Radu Android inputstreamバグに対する最善の回避策があります。

0
Ryan Harris

見つけた!

バッファをリセットする必要があります。

buffer = new byte [1024];

この前に:

bytes = mmInStream.read(buffer); 

少なくとも私にとってはうまくいき、sleepコマンドは必要ありません。

0
Lukeskymuh

良い答えはたくさんあると思います。私の貢献は、次のようにBluetooth入力ストリームに入る各桁を一度に1つずつ処理することです。これの値は、データのすべてのセットが(文字列として)長さが1つの値であることを保証することです。これは、新しい値(メインのアクティビティハンドラーのreadMessage文字列に含まれる)をリストに入れることで非常に簡単に処理できます。そうすれば、List(String)(エディターでは<>を使用できません)オブジェクトを使用して、を介して抽出することにより、データを実数に戻すことができます。

Integer.valueOf(List(String)オブジェクトがここに渡されます)

//接続中はInputStreamをリッスンし続けますwhile(true){

         try {
             byte[] buffer = new byte[1]; // make this one byte to pass one digit at a time through Bluetooth Handler
             String readMessage;
             int bytes;
            if (mmInStream.available()>2) { 
                try {
                   // Read from the InputStream
                    bytes = mmInStream.read(buffer); 
                    readMessage = new String(buffer, 0, bytes);

                   }catch (IOException e) {
                    Log.e(TAG, "disconnected", e);
                    break;
                }
                 // Send the obtained bytes to the UI Activity
                 mHandler.obtainMessage(HomeBlueRemote.MESSAGE_READ, bytes, -1, readMessage)
                      .sendToTarget();
    }
0
add_em_