昨日、「【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 デバイスを接続する方法(途中)でした。
解決方法が分かったら、またまとめたいと思います。