7

アプリケーションの一部がBluetooth経由でデバイスに接続し、通常は正常に動作しますが、接続できない場合があり、次のエラーが発生します

03-11 10:29:20.328: E/BluetoothComService(8059): accept() failed
03-11 10:29:20.328: E/BluetoothComService(8059): java.io.IOException: Operation Canceled
03-11 10:29:20.328: E/BluetoothComService(8059):    at android.bluetooth.BluetoothSocket.acceptNative(Native Method)
03-11 10:29:20.328: E/BluetoothComService(8059):    at android.bluetooth.BluetoothSocket.accept(BluetoothSocket.java:316)
03-11 10:29:20.328: E/BluetoothComService(8059):    at android.bluetooth.BluetoothServerSocket.accept(BluetoothServerSocket.java:105)
03-11 10:29:20.328: E/BluetoothComService(8059):    at android.bluetooth.BluetoothServerSocket.accept(BluetoothServerSocket.java:91)
03-11 10:29:20.328: E/BluetoothComService(8059):    at com.mypackage.name.bluetooth.BluetoothService$AcceptThread.run(BluetoothService.java:298)

これは私が例外を得る行です

socket = mmServerSocket.accept();    

そしてこれは完全なAcceptThreadです

private class AcceptThread extends Thread {
    // The local server socket
    private BluetoothServerSocket mmServerSocket;
    public boolean successInit = false;

    public AcceptThread() {
        closeAllConnections();

        /*
         * if(mmServerSocket != null) { try { mmServerSocket.close(); } catch
         * (IOException e) { e.printStackTrace(); } }
         */
        BluetoothServerSocket tmp = null;

        // Create a new listening server socket
        while (!successInit) {
            try {
                tmp = mAdapter
                        .listenUsingRfcommWithServiceRecord(NAME, MY_UUID);

                successInit = true;
            } catch (Exception e) {

                successInit = false;
            }
        }

        /*
         * try { tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME,
         * MY_UUID); successInit= true; } catch (IOException e) { Log.e(TAG,
         * "listen() failed", e); tmp = null; successInit = false; }
         */
        mmServerSocket = tmp;
    }

    public void run() {
        if (D)
            Log.d(TAG, "BEGIN mAcceptThread" + this);
        setName("AcceptThread");
        BluetoothSocket socket = null;

        // Listen to the server socket if we're not connected
        while (mState != STATE_CONNECTED) {
            try {
                // This is a blocking call and will only return on a
                // successful connection or an exception
                mAdapter.cancelDiscovery();

                socket = mmServerSocket.accept();
            } catch (IOException e) {
                Log.e(TAG, "accept() failed", e);
                Log.e("Error", "This isn't connecting");
                break;
            }

            // If a connection was accepted
            if (socket != null) {
                synchronized (BluetoothService.this) {
                    switch (mState) {
                    case STATE_LISTEN:
                    case STATE_CONNECTING:
                        // Situation normal. Start the connected thread.
                        connected(socket, socket.getRemoteDevice());
                        break;
                    case STATE_NONE:
                    case STATE_CONNECTED:
                        // Either not ready or already connected. Terminate new
                        // socket.
                        try {
                            socket.close();
                        } catch (IOException e) {
                            Log.e(TAG, "Could not close unwanted socket", e);
                        }
                        break;
                    }
                }
            }
        }
        if (D)
            Log.i(TAG, "END mAcceptThread");
    }

    public void cancel() {
        if (D)
            Log.d(TAG, "cancel " + this);
        try {
            mmServerSocket.close();
        } catch (IOException e) {
            Log.e(TAG, "close() of server failed", e);
        }
    }
}     

AcceptThreadこれは、すべてを閉じて再起動することを期待して、最初に呼び出す関数です。

public void closeAllConnections() {
    if (mmInStream != null) {
        try {mmInStream.close();}
        catch  (Exception e){Log.e(TAG, "close() of connect socket failed", e);}
    }
    if (mmOutStream != null) {
        try {mmOutStream.close();}
        catch (Exception e){Log.e(TAG, "close() of connect socket failed", e);}
    }
    if (mmSocket != null) {
        try {
            mmSocket.close();
            //mmSocket.connect();
        }
        catch (IOException e) {
            Log.e(TAG, "close() of connect socket failed", e);
        }
    }
}

Bluetooth DocsとSOの質問を読みましたが、うまくいくものが見つかりませんでした。BTを介して接続するのはこれが初めてなので、少し混乱します。

ノート

これが発生したときに私が見つけた唯一の「修正」は、BTアダプターをオフにし、プログラムを強制的に閉じ、BTアダプターを再起動し、アプリを再起動することです。これは明らかな理由で適切ではありません。プログラムでアダプタを再起動しようとしましたが、それでも接続できません。

誰かが私のBlutoothServiceクラスのどこが間違っているのかわかりAcceptThreadますか?または、この問題を解決するにはどうすればよいですか?ありがとう!

アップデート

実際、ある接続が閉じられ、別の接続を再接続しようとしているように見えThreadます。問題は、別の場所で接続しようとする原因やThread、これが発生した場合の修正方法がわからないことです。

これを正常に再現できる唯一の方法は、BTデバイスの電源がオフになっている場合は、BTアダプターの電源をオフにすることです。すべてをオンに戻すと、例外が発生し、接続できなくなります。私はそれがランダムにそして定期的に起こるという顧客を持っているので、私は問題が関連していることを望んでいます。

4

3 に答える 3

1

Although this is old post but I recently contact same issue so I want to write down the way I solve it.

It seems your code is from Google samples BlutoothChat (as it looks same, sorry if I misunderstand). I also create my own application that base on this sample (on API level 10). I meet the accept() fail issues if I try to connect one device to other device but at the end I solve this question by simply remove some code in MainActivty

On the Google samples main activity it contain many methods when activity change (Start, Pause, etc).

Original code have this

@Override
public synchronized void onResume() {
    super.onResume();
    if(D) Log.e(TAG, "+ ON RESUME +");
    if (mChatService != null) {
        if (mChatService.getState() == ChatService.STATE_NONE) {
          mChatService.start();
        }
    }
}

This code start the Chat Service and running the AcceptThread to listening incoming connections.

When application start, this method will be call "once" and create the AcceptThread. If you do any other things that make the main activity onPause() pause (In Google samples case, if you click menu to start device_list activity the main activity will pause), when the application back to main activity it will call create AcceptThread method "one more time", this cause the problem because one thread already running but you try to interrupt it. And at the end happen accept() fail error and throw java.io.IOException: Operation Canceled error.

So to avoid this is simply remove the codes in OnResume()

@Override
public synchronized void onResume() {
    super.onResume();
    if(D) Log.e(TAG, "+ ON RESUME +");
}

or if you don't want delete any codes because you afraid cause some problem, put this code (mChatService != null) mChatService.stop(); in...

@Override
public synchronized void onPause() {
    super.onPause();
    if (mChatService != null) mChatService.stop();
    if(D) Log.e(TAG, "- ON PAUSE -");
}

Both works perfect in my project. Method 1 not create the new thread if activity resume, and Method 2 kill all the thread if you leave the current activity and disconnect all current connections if you already have one (it will start the thread again once you turn back). Method 1 wouldn't return any error but method 2 will throw accept fail again if you leave the current activity, so I suggest to use method 1.

Need to notice that this error usually happen after you modify Google samples BlutoothChat, it will never appear on the original app.

I have seen many post talk about this issue, but not see any one come out with this answer, so just want to share this. Hope this is helpful.

于 2013-04-07T15:08:37.517 に答える
0

この例外は、BluetoothServerSocket が閉じられているか、ガベージ コレクションが行われたときに発生するはずです。スレッドの古いコピーで例外が発生していると思われます。次のようなものです。新しいスレッドを作成すると、古いスレッドがキャンセルされ、BluetoothServerSocket が閉じられるため、受け入れが正しく失敗し、そのエラーが発生します。デバッガーでこれを確認するか、さまざまなイベントが発生するスレッドのログを記録します。たとえば、受け入れ後の行とおそらくキャンセル機能にブレークポイントを設定し、そこでスレッド ID を調べます。前のスレッドで例外が発生していますか?

于 2013-03-11T16:18:46.173 に答える