昨日、「【Java】Android端末でBluethoothデバイスを検出する方法」という記事を投稿しましたが、今日はその続きで、検出した Bluetooth デバイスと Android 端末を接続する方法についてです。
ただし、実はまだ動いていません…。
今回参考にさせていただいた記事はこちらから。
03.Bluetoothデバイスとの接続・切断の処理を作る
https://www.hiramine.com/programming/bluetoothcommunicator/03_connect_disconnect.htmlAndroidからBluetooth機器にSPP (Serial Port Profile) で接続する|d.sunnyone.org
http://d.sunnyone.org/2013/09/androidbluetoothspp-serial-port-profile.html
実装にはまず、BluetoothService
という Class を定義します。
static public class BluetoothService { // 定数(Bluetooth UUID) private static final UUID UUID_SPP = UUID.fromString( "00001101-0000-1000-8000-00805F9B34FB" ); // 定数 static final int MESSAGE_STATECHANGE = 1; static final int STATE_NONE = 0; static final int STATE_CONNECT_START = 1; static final int STATE_CONNECT_FAILED = 2; static final int STATE_CONNECTED = 3; static final int STATE_CONNECTION_LOST = 4; static final int STATE_DISCONNECT_START = 5; static final int STATE_DISCONNECTED = 6; // メンバー変数 private int mState; private ConnectionThread mConnectionThread; private Handler mHandler; // 接続時処理用のスレッド private class ConnectionThread extends Thread { private BluetoothSocket mBluetoothSocket; // コンストラクタ ConnectionThread(BluetoothDevice bluetoothdevice) { try { // mBluetoothSocket = bluetoothdevice.createRfcommSocketToServiceRecord(UUID_SPP); mBluetoothSocket = bluetoothdevice.createInsecureRfcommSocketToServiceRecord(UUID_SPP); } catch( IOException e ) { Log.e( "BluetoothService", "failed : bluetoothdevice.createRfcommSocketToServiceRecord( UUID_SPP )", e ); } } // 処理 public void run() { while( STATE_DISCONNECTED != mState ) { switch( mState ) { case STATE_NONE: break; case STATE_CONNECT_START: // 接続開始 for (int i = 0; ; i++) { try { mBluetoothSocket.connect(); } catch (IOException ex) { if (i < 5) { Log.d("BluetoothService","Failed to connect. Retrying: " + ex.toString()); continue; } Log.d("BluetoothService", "Failed to connect: " + ex.toString()); setState(STATE_DISCONNECT_START); return; } break; } // 接続成功 setState( STATE_CONNECTED ); break; case STATE_CONNECT_FAILED: // 接続失敗 Log.d("BluetoothService", "STATE_CONNECT_FAILED"); // 接続失敗時の処理の実体は、cancel()。 break; case STATE_CONNECTED: // 接続済み(Bluetoothデバイスから送信されるデータ受信) break; case STATE_CONNECTION_LOST: // 接続ロスト // 接続ロスト時の処理の実体は、cancel()。 break; case STATE_DISCONNECT_START: // 切断開始 // 切断開始時の処理の実体は、cancel()。 break; case STATE_DISCONNECTED: // 切断完了 // whileの条件式により、STATE_DISCONNECTEDの場合は、whileを抜けるので、このケース分岐は無意味。 break; } } synchronized( BluetoothService.this ) { // 親クラスが保持する自スレッドオブジェクトの解放(自分自身の解放) mConnectionThread = null; } } // キャンセル(接続を終了する。ステータスをSTATE_DISCONNECTEDにすることによってスレッドも終了する) void cancel() { try { mBluetoothSocket.close(); } catch( IOException e ) { Log.e( "BluetoothService", "Failed : mBluetoothSocket.close()", e ); } setState( STATE_DISCONNECTED ); } } // コンストラクタ BluetoothService(Context context, Handler handler, BluetoothDevice device) { mHandler = handler; mState = STATE_NONE; // 接続時処理用スレッドの作成と開始 mConnectionThread = new ConnectionThread(device); mConnectionThread.start(); } // ステータス設定 private synchronized void setState( int state ) { mState = state; mHandler.obtainMessage( MESSAGE_STATECHANGE, state, -1 ).sendToTarget(); } // 接続開始時の処理 synchronized void connect() { if( STATE_NONE != mState ) { // 1つのBluetoothServiceオブジェクトに対して、connect()は1回だけ呼べる。 // 2回目以降の呼び出しは、処理しない。 return; } // ステータス設定 setState( STATE_CONNECT_START ); } // 接続切断時の処理 synchronized void disconnect() { if( STATE_CONNECTED != mState ) { // 接続中以外は、処理しない。 return; } // ステータス設定 setState( STATE_DISCONNECT_START ); mConnectionThread.cancel(); } }
この関数内で、BluetoothSocket
の設定から接続時の処理などを指定します。
次に、BluetoothService
から情報を取得するためのハンドラを定義します。
// Bluetoothサービスから情報を取得するハンドラ @SuppressLint("HandlerLeak") private final Handler mHandler = new Handler() { // ハンドルメッセージ // UIスレッドの処理なので、UI処理について、runOnUiThread対応は、不要。 @Override public void handleMessage( Message msg ) { if (msg.what == BluetoothService.MESSAGE_STATECHANGE) { switch (msg.arg1) { case BluetoothService.STATE_NONE: // 未接続 break; case BluetoothService.STATE_CONNECT_START: // 接続開始 Log.d("MainActivity", "STATE_CONNECT_START"); break; case BluetoothService.STATE_CONNECT_FAILED: // 接続失敗 Toast.makeText(MainActivity.this, "Failed to connect to the device.", Toast.LENGTH_SHORT).show(); Log.d("MainActivity", "STATE_CONNECT_FAILED"); break; case BluetoothService.STATE_CONNECTED: // 接続完了 Log.d("MainActivity", "STATE_CONNECTED"); break; case BluetoothService.STATE_CONNECTION_LOST: // 接続ロスト //Toast.makeText( MainActivity.this, "Lost connection to the device.", Toast.LENGTH_SHORT ).show(); break; case BluetoothService.STATE_DISCONNECT_START: break; case BluetoothService.STATE_DISCONNECTED: // 切断完了 mBtAdapter = null; // BluetoothServiceオブジェクトの解放 break; } } } };
なお、こちらもサンプルコードからほぼそのままコピーしてきたものですが、ボタンの無効化などの一部不要な処理を削除しておりますので、ご注意ください。
そして、デバイスと接続/切断するための関数を定義します。
// 端末と接続 private void connectDevice(BluetoothDevice device) { Log.d("MainActivity","device.name : "+device.getName()+", device.address : "+device.getAddress()); if( null != mBluetoothService ) { // mBluetoothServiceがnullでないなら接続済みか、接続中。 return; } // 接続 BluetoothDevice connectDevice = mBtAdapter.getRemoteDevice(device.getAddress()); mBluetoothService = new BluetoothService(this, mHandler, connectDevice); mBluetoothService.connect(); } // 端末との接続を切断 private void disconnectDevice() { if( null == mBtAdapter ) { // mBluetoothServiceがnullなら切断済みか、切断中。 return; } // 切断 mBluetoothService.disconnect(); mBluetoothService = null; }
なお、これらを追加すると恐らく変数などの定義でエラーが発生するため、onCreate
よりも上の階層で下記を定義してください。
private BluetoothService mBluetoothService;
あとは任意のタイミングで、connectDevice(BluetoothDevice)
を呼び出せば、接続処理が実行されます。
私は、デバイスが見つかったタイミングで実行しています。
そのタイミングでなら、デバイス情報も取得できますしね。
…が、こちらを実行すると、java.io.IOException: read failed, socket might closed or timeout, read ret: -1
というエラーが発生しました…。
ソケットが閉じているか、接続にかかる時間が長すぎてタイムアウトしているようですが、どう直せばいいのかまだわかっていません。
エラーメッセージで検索しているのですが、有益な記事にはたどり着けずにいます。
UUID が悪さをしているのかもしれませんので、このあたりから攻め直してみたいと思います!
以上、まだ完成していませんが、Android 端末と Bluetooth デバイスを接続する方法(途中)でした。
解決方法が分かったら、またまとめたいと思います。