Bluetooth通信の実装|Android開発

AndroidアプリでBluetooth通信を実装する方法です。
Bluetooth接続では、受信側と送信側で同一のUUIDを使用します。下記はシリアルポートプロファイル(SPP)でデータを送信するBluetooth機器との通信サンプルです。

前提条件

  • 接続デバイスとはペアリング済みにしておきます
  • AndroidManifest.xmlに以下の権限を追加しておきます
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

Bluetooth 通信手順

  1. BluetoothAdapterを取得します
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothDevice;
    
    BluetoothAdapter mBluetoothAdapter;
    
    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (mBluetoothAdapter == null) {
        // エラー: Bluetooth なし.
    }
    
  2. ペアリング済みのデバイス一覧を取得します
    getBondedDevices()はエラー時にnullを返すためエラー判定が必要です
  3. 取得したデバイス一覧から接続デバイスを検索します
    String deviceName = "接続デバイス名";
    
    // 対象 Bluetooth デバイスのシリアルポートプロファイルに接続する.
    SerialPortProfileConnectThread connectTread = null;
    Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
    // getBondedDevices() はエラー時に null を返すのでチェックが必要.
    if (pairedDevices != null) {
        for (BluetoothDevice device : pairedDevices) {
            if (deviceName.equals(device.getName())) {
                connectTread = new SerialPortProfileConnectThread(device);
                break;
            }
        }
    }
    
    if (connectTread == null) {
        // エラー: 接続デバイスなし.
    }
    
    SerialPortProfileConnectThreadクラスはシリアルポートプロファイルを使ってBluetooth接続するクラスです。
    実際の接続処理は親のBluetoothConnectThreadクラスで実装しています。
    import android.bluetooth.BluetoothDevice;
    import android.bluetooth.BluetoothSocket;
    
    import java.util.UUID;
    
    public class BluetoothConnectThread extends Thread {
        protected String TAG = "BluetoothConnectThread";
    
        protected final BluetoothSocket mmSocket;
    
        public BluetoothSocket getSocket() {
            return mmSocket;
        }
    
        public BluetoothConnectThread(BluetoothDevice device, UUID uuid) {
            BluetoothSocket tmp = null;
            try {
                tmp = device.createRfcommSocketToServiceRecord(uuid);
            } catch (IOException e) {
                e.printStackTrace();
                // NOP.
            }
            mmSocket = tmp;
        }
    
        public void run() {
            if (mmSocket == null) {
                return;
            }
    
            try {
                mmSocket.connect();
            } catch (IOException e) {
                e.printStackTrace();
                try {
                    mmSocket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                return;
            }
            Log.i(TAG, "Bluetooth connecting.");
        }
    }
    
    import android.bluetooth.BluetoothDevice;
    
    import java.util.UUID;
    
    public class SerialPortProfileConnectThread extends BluetoothConnectThread {
    
        // "00001101-0000-1000-8000-00805f9b34fb" = SPP (シリアルポートプロファイル) の UUID.
        public static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
    
        public SerialPortProfileConnectThread(BluetoothDevice device) {
            super(device, SPP_UUID);
            TAG = "SerialPortProfileConnectThread";
        }
    }
    
  4. 接続前に Bluetooth デバイスの検索を停止します
    mBluetoothAdapter.cancelDiscovery();
    
  5. SerialPortProfileConnectThreadを開始し、ソケットを接続します
    connectTread.start();
    
    // 接続完了を待つ.
    // 下記は sleep() を使用した暫定処理です.
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    
    接続が完了したら、このスレッドは終了します。
    Bluetooth 接続完了前に次項のBluetoothReceiveTaskでデータ読み出しを行うと例外が発生します。
  6. データ受信用のスレッドを生成・実行します
    この時、接続完了したソケットを使用します
    private BluetoothReceiveTask mReceiveTask;
    
    mReceiveTask = new BluetoothReceiveTask(connectTread.getSocket());
    mReceiveTask.start();
    
    BluetoothReceiveTaskでは接続機器の仕様に合わせて受信したデータを処理します。
    import android.bluetooth.BluetoothSocket;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    class BluetoothReceiveTask extends Thread {
        public static final String TAG = "BluetoothReceiveTask";
    
        protected InputStream mInputStream;
    
        protected BluetoothSocket mSocket;
    
        protected volatile boolean mIsCancel;
    
        public BluetoothReceiveTask(BluetoothSocket socket) {
            mIsCancel = false;
            mSocket = null;
            mInputStream = null;
            if (socket == null) {
                Log.e(TAG, "parameter socket is null.");
                return;
            }
    
            try {
                mInputStream = socket.getInputStream();
                mSocket = socket;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public void run() {
            byte[] buffer = new byte[64];
            int readSize;
    
            Log.i(TAG, "start read task.");
            while (mInputStream != null) {
                if (mIsCancel) {
                    break;
                }
    
                try {
                    readSize = mInputStream.read(buffer);
                    if (readSize == 64) {
                        // 処理.
                    } else {
                        Log.e(TAG, "NG " + readSize + "byte");
                    }
    
                    Thread.sleep(0);
                } catch (IOException e) {
                    e.printStackTrace();
                    break;
                } catch (InterruptedException e) {
                    Log.e(TAG, "InterruptedException!!");
                    // NOP.
                    break;
                }
            }
            Log.i(TAG, "exit read task.");
        }
    
        public void cancel() {
            mIsCancel = true;
        }
    
        public void finish() {
            if (mSocket == null) {
                return;
            }
    
            try {
                mSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

以上で Bluetooth 機器とシリアルポートプロファイルを使ったデータ通信が可能です。

Bluetooth通信終了処理

Bluetooth通信終了時はデータ受信のスレッドを終了させます。

if (mReceiveTask != null) {
    mReceiveTask.cancel();
}

// キャンセル完了を待つ処理など.

if (mReceiveTask != null) {
    mReceiveTask.finish();
    mReceiveTask = null;
}
このエントリーをはてなブックマークに追加
にほんブログ村 IT技術ブログへ

コメント

メールアドレスが公開されることはありません。 が付いている欄は必須項目です