Skip to content

Most visited

Recently visited

navigation

Bluetooth

Android プラットフォームでは Bluetooth ネットワーク スタックをサポートしているため、端末はワイヤレスで他の Bluetooth 端末とのデータを交換できます。このアプリのフレームワークにより、Android Bluetooth API を介した Bluetooth 機能へのアクセスが可能になります。 これらの API を使用して、アプリは他の Bluetooth 端末とワイヤレスで接続し、ポイントツーポイントおよびマルチポイントのワイヤレス機能を使用できます。

Bluetooth API を使用して、Android アプリは次の操作を実行できます。

このドキュメントでは、クラシック Bluetooth の使用方法について説明します。クラシック Bluetooth は、ストリーミングや Android 端末との通信など、電池の消費量が多い操作に適した選択肢です。 省電力要件がある Bluetooth 端末向けに、Android 4.3(API レベル 18)で Bluetooth Low Energy の API サポートが導入されました。 詳細については、Bluetooth Low Energy をご覧ください。

基本

このドキュメントでは、Android Bluetooth API を使用して、Bluetooth 通信に不可欠な 4 つの主なタスクを完了する方法について説明します。そのタスクとは、Bluetooth の設定、ペア設定済みの端末またはローカル エリアで使用可能な端末の検索、端末の接続、端末間でのデータ通信です。

Bluetooth API はすべて、android.bluetooth パッケージ内にあります。 次に、Bluetooth 接続を作成するために必要なクラスとインターフェースの概要を説明します。

BluetoothAdapter
ローカル Bluetooth アダプタ(Bluetooth 無線通信)を表します。BluetoothAdapter は、Bluetooth のあらゆる操作のエントリ ポイントになります。 これを使用して他の Bluetooth 端末を検出し、ボンディング(ペア設定)した端末のリストを問い合わせて、既知の MAC アドレスを使用して BluetoothDevice をインスタンス化し、BluetoothServerSocket を作成して他の端末の通信をリッスンします。
BluetoothDevice
リモート Bluetooth 端末を表します。これを使用して、BluetoothSocket を介してリモート端末と接続したり、名前、アドレス、クラス、ボンディング状態など端末に関する情報を問い合わせたりします。
BluetoothSocket
Bluetooth ソケットのインターフェースを表します(TCP Socket と同様)。これは、アプリが InputStream および OutputStream を介して別の Bluetooth 端末とデータ交換するための接続点です。
BluetoothServerSocket
受信リクエストをリッスンするためにオープンしているサーバー ソケットを表します(TCP ServerSocket と同様)。 2 つの Android 端末を接続するには、一方の端末がこのクラスを使用してサーバー ソケットをオープンする必要があります。 リモート Bluetooth 端末はこの端末への接続リクエストを行い、接続が受け入れられると、BluetoothServerSocket が接続された BluetoothSocket を返します。
BluetoothClass
Bluetooth 端末の一般的な特性と機能を示します。 これは端末の大 / 小のクラスとサービスを定義する読み取り専用のプロパティのセットです。 ただし、これは端末によってサポートされるすべての Bluetooth のプロファイルとサービスについて確実に記述したものではなく、端末の種類を識別するヒントとして活用できます。
BluetoothProfile
Bluetooth プロファイルを表すインターフェース。 Bluetooth プロファイルは、端末間で Bluetooth ベースの通信を行うためのワイヤレス インターフェース仕様です。 その一例が、Hands-Free プロファイルです。 プロファイルの詳細については、プロファイルの使用をご覧ください。
BluetoothHeadset
スマートフォンで使用する Bluetooth ヘッドセットをサポートします。 これには、Bluetooth Headset と Hands-Free(v1.5)の両方のプロファイルが含まれています。
BluetoothA2dp
端末間で Bluetooth 接続によって高品質のオーディオをストリーミングできる方法を定義しています。"A2DP" は、Advanced Audio Distribution Profile(高度オーディオ配信プロファイル)の略です。
BluetoothHealth
Bluetooth サービスを制御する Health Device Profile プロキシを表します。
BluetoothHealthCallback
BluetoothHealth コールバックの実装に使用する抽象クラス。このクラスを拡張してコールバック メソッドを実装し、アプリの登録状態と Bluetooth チャンネルの状態の変更に関するアップデートを受信します。
BluetoothHealthAppConfiguration
Bluetooth Health サードパーティ アプリが、リモート Bluetooth ヘルス機器と通信するために登録したアプリの設定を表します。
BluetoothProfile.ServiceListener
サービスへの接続時およびサービスから切断時に BluetoothProfile IPC クライアントに通知するインターフェース(つまり、特定のプロファイルを実行する内部サービス)。

Bluetooth のパーミッション

アプリで Bluetooth 機能を使用するには、BLUETOOTH で Bluetooth パーミッションを宣言する必要があります。このパーミッションは、接続のリクエスト、接続の受け入れ、データの転送など、Bluetooth 通信を実行するために必要です。

アプリで端末の検出を開始したり Bluetooth の設定を操作する場合は、BLUETOOTH_ADMIN パーミッションも宣言する必要があります。 ほとんどのアプリは、ローカル Bluetooth 端末の検出のみのためにこのパーミッションが必要です。 このパーミッションで付与されたその他の機能は、アプリがユーザーのリクエストに応じて Bluetooth の設定を変更する「Power Manager」として機能する場合を除いて、使用すべきではありません。 注: BLUETOOTH_ADMIN パーミッションを使用する場合は、BLUETOOTH パーミッションも必要です。

アプリのマニフェスト ファイルで Bluetooth のパーミッションを宣言してください。次に例を示します。

<manifest ... >
  <uses-permission android:name="android.permission.BLUETOOTH" />
  ...
</manifest>

アプリのパーミッション宣言の詳細については、<uses-permission> のリファレンスをご覧ください。

Bluetooth の設定

図 1: Bluetooth を有効にするダイアログ

アプリで Bluetooth 通信を行う前に、その Bluetooth が端末でサポートされているか、サポートされている場合は有効かどうかを確認する必要があります。

Bluetooth がサポートされていない場合は、すべての Bluetooth 機能を適切に無効化してください。 Bluetooth がサポートされているものの無効化されている場合、アプリから離れることなく、ユーザーが Bluetooth を有効化するようにリクエストすることができます。 この設定は、BluetoothAdapter を使用して、2 つのステップで完了します。

  1. BluetoothAdapter を取得する

    すべての Bluetooth のアクティビティには BluetoothAdapter が必要です。 BluetoothAdapter を取得するには、静的メソッド getDefaultAdapter() を呼び出します。これは、端末独自の Bluetooth アダプタ(Bluetooth 無線通信)を表す BluetoothAdapter を返します。 システム全体には 1 つの Bluetooth アダプタが存在し、アプリはこのオブジェクトを使用して Bluetooth アダプタとやり取りができます。 getDefaultAdapter() が null を返した場合、端末は Bluetooth をサポートしていないため、ここで終了です。 次に例を示します。

    BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (mBluetoothAdapter == null) {
        // Device does not support Bluetooth
    }
    
  2. Bluetooth を有効化する

    次に、Bluetooth が有効であることを確認する必要があります。isEnabled() を呼び出して、Bluetooth が現在有効かどうかを確認します。 このメソッドが false を返したら、Bluetooth は無効です。Bluetooth の有効化をリクエストするには、ACTION_REQUEST_ENABLE アクション インテントを指定して startActivityForResult() を呼び出します。これにより(アプリを停止せずに)システム設定で Bluetooth を有効化するようにリクエストを発行します。 次に例を示します。

    if (!mBluetoothAdapter.isEnabled()) {
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    }
    

    図 1 に示すように、ユーザーに Bluetooth を有効化するためのパーミッションを求めるダイアログが表示されます。ユーザーが [Yes] を選択すると、システムは Bluetooth の有効化を開始し、プロセスが完了(または失敗)するとフォーカスをアプリに戻します。

    startActivityForResult() に渡される REQUEST_ENABLE_BT 定数は、ローカルで定義された整数(1 以上)です。システムはこの値を requestCode パラメータとして onActivityResult() の実装に渡します。

    Bluetooth の有効化に成功した場合、アクティビティは onActivityResult() コールバックで結果コード RESULT_OK を受け取ります。 エラー(またはユーザーが [No] を選択)により Bluetooth が有効化されなかった場合、結果コードは RESULT_CANCELED です。

オプションで、アプリは ACTION_STATE_CHANGED ブロードキャスト インテントをリッスンします。このインテントは Bluetooth の状態が変更されるたびに、システムによってブロードキャストされます。 このブロードキャストには追加フィールドの EXTRA_STATEEXTRA_PREVIOUS_STATE が含まれており、それぞれ Bluetooth の以前の状態と新しい状態が格納されています。 これらの追加フィールドで使用できる値は、STATE_TURNING_ONSTATE_ONSTATE_TURNING_OFFSTATE_OFF です。 このブロードキャストのリッスンは、アプリの実行中に Bluetooth 状態の変更を検出するうえで便利です。

ヒント: 検出の許可を有効にすると、自動的に Bluetooth が有効になります。 Bluetooth アクティビティを実行する前に、端末の検出の許可を一貫して有効にするつもりであれば、上記のステップ 2 をスキップすることができます。 以下の検出の許可を有効化するをご覧ください。

端末の検索

BluetoothAdapter を使用して、端末の検出を使用するか、ペア設定(ボンディング)された端末のリストを問い合わせて、リモート Bluetooth 端末を見つけることができます。

端末の検出はローカル エリアで Bluetooth が有効な端末を検索し、各端末の情報をリクエストするスキャン手順です(この処理は、「検出」「問い合わせ」「スキャン」と呼ばれる場合もあります)。ただし、ローカル エリア内の Bluetooth 端末は、現在検出可能になっている場合にのみ検出リクエストに応答します。 端末が検出可能な場合、端末名、クラス、一意の MAC アドレスなどの情報を共有することで検出リクエストに応答します。 検出を実行した端末はこの情報を使用して、検出された端末への接続開始を選択できます。

リモート端末との接続が初めて確立されるとペア設定リクエストが自動的にユーザーに表示されます。 端末がペア設定されると、端末の基本情報(端末名、クラス、MAC アドレス)が保存され、Bluetooth API を使用して読み取れるようになります。 リモート端末の既知の MAC アドレスを使用すると、検出を実行しなくてもいつでも接続を開始できます(端末が範囲内に存在することが前提です)。

ペア設定と接続には違いがある点に注意してください。ペア設定は、端末がお互いの存在を認識し、認証に使用できる共有のリンクキーを持ち、相互に暗号化された接続を確立できることを意味します。 接続は、端末が現在 RFCOMM チャンネルを共有し、相互にデータを送信できることを意味します。 現在の Android Bluetooth API では、RFCOMM 接続を確立する前に端末のペア設定が必要です (Bluetooth API を使用して暗号化された接続を開始すると自動的にペア設定が実行されます)。

次のセクションでは、ペア設定済みの端末を見つける、または端末の検出を使用して新しい端末を見つける方法について説明します。

注: Android 搭載端末は、デフォルトでは検出可能になっていません。 ユーザーは、システム設定を使用して一定の時間だけ端末を検出可能にすることができます。またはアプリで、ユーザーがアプリから離れることなく、検出の許可を有効化するようにリクエストできます。 検出の許可を有効化する方法については、以下で説明します。

ペア設定済みの端末の問い合わせ

端末検出を実行する前に、ペア設定済みの端末のセットを問い合わせて、目的の端末を既に認識しているかどうかを問い合わせることをお勧めします。 問い合わせるには、getBondedDevices() を呼び出します。 これは、ペア設定済みの端末を表す BluetoothDevice のセットを返します。 たとえば、ペア設定済みの端末をすべて問い合わせて、ArrayAdapter を使用して各端末の名前をユーザーに表示することができます。

Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
    // Loop through paired devices
    for (BluetoothDevice device : pairedDevices) {
        // Add the name and address to an array adapter to show in a ListView
        mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }
}

接続を開始するために BluetoothDevice オブジェクトから取得する必要があるのは、 MAC アドレスだけです。 この例では、ユーザーに表示する ArrayAdapter の一部として保存されています。 その後、MAC アドレスを抽出して接続を開始できます。 接続の確立の詳細については、端末の接続に関するセクションをご覧ください。

端末の検出

端末の検出を開始するには、startDiscovery() を呼び出すだけです。このプロセスは非同期で実行され、メソッドは検出が正常に開始されたどうかを示す boolean をすぐに返します。 検出プロセスでは通常、約 12 秒間の問い合わせのスキャンが行われ、その後、検出された各端末のページ スキャンによってそれぞれの Bluetooth 名を取得します。

検出された各端末に関する情報を受信するには、アプリで ACTION_FOUND インテントの BroadcastReceiver を登録する必要があります。 端末ごとに、システムは ACTION_FOUND インテントをブロードキャストします。 このインテントには追加フィールドの EXTRA_DEVICEEXTRA_CLASS があり、それぞれ BluetoothDeviceBluetoothClass を格納しています。 たとえば、端末が検出されたときに、ブロードキャストを処理するために登録する方法は次のとおりです。

// Create a BroadcastReceiver for ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        // When discovery finds a device
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // Get the BluetoothDevice object from the Intent
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            // Add the name and address to an array adapter to show in a ListView
            mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
        }
    }
};
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

接続を開始するために BluetoothDevice オブジェクトから取得する必要があるのは、 MAC アドレスだけです。 この例では、ユーザーに表示する ArrayAdapter の一部として保存されています。 その後、MAC アドレスを抽出して接続を開始できます。 接続の確立の詳細については、端末の接続に関するセクションをご覧ください。

警告: 端末の検出を実行すると Bluetooth アダプタに負荷がかかり、アダプタの多くのリソースを消費します。 接続する端末が見つかったら、接続を試みる前に、cancelDiscovery() を使用して必ず検出を停止してください。 また、既に端末との接続を確立している場合、検出を実行すると接続に使用できる帯域幅が大幅に減少するため、接続中は検出を実行しないでください。

検出の許可の有効化

ローカル端末を他の端末から検出可能にするには、ACTION_REQUEST_DISCOVERABLE アクション インテントを指定して startActivityForResult(Intent, int) を呼び出します。 これはシステム設定を使用して(アプリを停止することなく)検出可能モードを有効にするリクエストを発行します。 デフォルトでは、端末は 120 秒間検出可能になります。 EXTRA_DISCOVERABLE_DURATION インテントをさらに追加して、別の時間を定義することもできます。 アプリが設定できる最長時間は 3,600 秒で、値 0 は端末が常に検出可能であることを意味します。 0 未満または 3,600 より大きい値を指定すると、自動的に 120 秒に設定されます。 たとえば、このスニペットでは時間を 300 秒に設定します。

Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
図 2: 検出の許可を有効にするダイアログ

図 2 に示すように、端末を検出可能にする許可をユーザーに求めるダイアログが表示されます。ユーザーが [Yes] を選択すると、端末は指定された時間、検出可能になります。 アクティビティは onActivityResult()) コールバックの呼び出しを受け取ります。この結果コードは、端末を検出可能にする時間と同じです。 ユーザーが [No] を選択するかエラーが発生した場合、結果コードは RESULT_CANCELED になります。

注: Bluetooth が端末で有効になっていない場合、端末の検出の許可を有効にすると、Bluetooth が自動的に有効になります。

端末は割り当てられた時間、検出可能モードをサイレントに継続します。検出可能モードに変化があったときに通知を受けたい場合は、ACTION_SCAN_MODE_CHANGED インテントの BroadcastReceiver を登録できます。 これには、追加フィールドの EXTRA_SCAN_MODEEXTRA_PREVIOUS_SCAN_MODE が含まれており、それぞれ新しいスキャン モードと以前のスキャン モードを示します。 それぞれに使用できる値は、SCAN_MODE_CONNECTABLE_DISCOVERABLESCAN_MODE_CONNECTABLE、または SCAN_MODE_NONE です。これは、端末が検出可能モードになっている、検出可能モードではないがまだ接続を受信できる、検出可能モードではなく接続も受信できない、のいずれかを示します。

リモート端末との接続を開始しようとするときに、端末の検出の許可を有効にする必要はありません。 リモート端末は、接続を開始する前に端末を検出できていることが前提であるため、検出の許可の有効化は、アプリで接続要求を受け入れるサーバー ソケットをホストしなければならない場合に限られます。

端末の接続

2 つの端末上のアプリ間で接続を確立するには、サーバー側とクライアント側の両方の仕組みを実装する必要があります。一方の端末はサーバー ソケットをオープンし、他方の端末は接続を開始する必要があるためです(接続を開始するには、サーバー端末の MAC アドレスを使用)。 サーバーとクライアントは、それぞれが同じ RFCOMM チャンネルで接続された BluetoothSocket を取得できれば接続完了と見なされます。 この時点で、各端末は入力ストリームと出力ストリームを取得してデータ転送を開始できます。この内容については、接続の管理のセクションで取り上げます。 このセクションでは、2 つの端末間で接続を開始する方法について説明します。

サーバー端末とクライアント端末はそれぞれ、異なる方法で必要な BluetoothSocket を取得します。サーバーは、接続要求を受け入れたときにソケットを受け取ります。 クライアントは、RFCOMM チャンネルをサーバーに対してオープンしたときにソケットを受け取ります。

図 3: Bluetooth のペア設定のダイアログ

実装のテクニックとして、各端末を自動的にサーバーとして準備しておくと、それぞれがサーバー ソケットをオープンして接続をリッスンできます。そうすると、片方の端末がもう一方の端末との接続を開始して、クライアントになることができます。 または、片方の端末がオンデマンドで明示的に接続を「ホスト」してサーバー ソケットをオープンすると、もう一方の端末は容易に接続を開始できます。

注: 2 つの端末がまだペア設定されていない場合、Android フレームワークは自動的にペア設定リクエストの通知を表示するか、または図 3 に示すように、接続手順の実行中にユーザーにダイアログを表示します。したがって、端末に接続しようとするとき、端末がペア設定済みかどうかをアプリで考慮する必要はありません。 RFCOMM 接続の試行は、ユーザーがペア設定に成功するまではブロックされます。またはユーザーがペア設定を拒否するか、ペア設定が失敗 / タイムアウトしたときには失敗します。

サーバー側の接続

2 つの端末を接続するには、一方がオープンした BluetoothServerSocket を保持してサーバーとして動作する必要があります。 サーバー ソケットの目的は、受信接続リクエストをリッスンして、それを受け入れたときに、接続した BluetoothSocket を提供することです。 BluetoothSocketBluetoothServerSocket から取得したら、複数の接続を受け入れる必要がない限り、BluetoothServerSocket は破棄できます(破棄する必要があります)。

サーバー ソケットを設定して接続を受け入れる基本的な手順は次のとおりです。

  1. listenUsingRfcommWithServiceRecord(String, UUID) を呼び出して BluetoothServerSocket を取得します。

    この文字列はサービスを識別できる名前です。端末上のシステムは自動的に新しい Service Discovery Protocol(SDP)データベースのエントリを書き込みます(この名前は任意です。単純にアプリ名を指定することもできます)。 UUID は SDP エントリにも含まれており、クライアント端末との接続許可のベースになります。 つまり、クライアントがこの端末との接続を試みたとき、端末は接続に使用したいサービスを一意に識別できる UUID を持っています。 (次のステップで)接続を受け入れるには、この UUID が一致している必要があります。

  2. accept() を呼び出して、接続リクエストのリッスンを開始します。

    これはブロッキング コールです。接続が受け入れられたか、例外が発生したときに制御が戻ります。 接続が受け入れられるのは、リモート端末が、このリスニング サーバー ソケットで登録した UUID と一致する UUID を指定した接続リクエストを送信した場合だけです。 接続に成功した場合、accept() は接続された BluetoothSocket を返します。

  3. 追加の接続を受け入れる必要がない限り、close() を呼び出します。

    これにより、サーバー ソケットとそのすべてのリソースが解放されますが、accept() で返された接続済みの BluetoothSocket はクローズしません。 TCP/IP とは異なり、RFCOMM はチャンネルごとに、同時に 1 つのクライアントのみ接続を許可します。したがってほとんどの場合、接続されたソケットの受け入れ直後に BluetoothServerSocketclose() を呼び出すのが適切です。

accept() の呼び出しは、メイン アクティビティの UI スレッドで実行しないでください。これはブロッキング コールなので、アプリがその他の操作を受け付けられなくなります。 通常、BluetoothServerSocket または BluetoothSocket の操作はすべて、アプリで管理される新しいスレッドで実行するのが適切な方法です。 accept() のようなブロックされる呼び出しを中止するには、BluetoothServerSocket(または BluetoothSocket)で別のスレッドから close() を呼び出すと、ブロックされた呼び出しからすぐに戻ります。 BluetoothServerSocket または BluetoothSocket のすべてのメソッドがスレッドセーフというわけではないため、注意してください。

これは、接続要求を受け入れるサーバー コンポーネントのスレッドを簡略化したものです。

private class AcceptThread extends Thread {
    private final BluetoothServerSocket mmServerSocket;

    public AcceptThread() {
        // Use a temporary object that is later assigned to mmServerSocket,
        // because mmServerSocket is final
        BluetoothServerSocket tmp = null;
        try {
            // MY_UUID is the app's UUID string, also used by the client code
            tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (IOException e) { }
        mmServerSocket = tmp;
    }

    public void run() {
        BluetoothSocket socket = null;
        // Keep listening until exception occurs or a socket is returned
        while (true) {
            try {
                socket = mmServerSocket.accept();
            } catch (IOException e) {
                break;
            }
            // If a connection was accepted
            if (socket != null) {
                // Do work to manage the connection (in a separate thread)
                manageConnectedSocket(socket);
                mmServerSocket.close();
                break;
            }
        }
    }

    /** Will cancel the listening socket, and cause the thread to finish */
    public void cancel() {
        try {
            mmServerSocket.close();
        } catch (IOException e) { }
    }
}

この例では、1 つの接続要求のみが想定されており、接続が受け入れられるとすぐに BluetoothSocket が取得され、アプリは取得した BluetoothSocket を別のスレッドに送信し、BluetoothServerSocket をクローズしてループを終了します。

accept()BluetoothSocket を返したとき、ソケットは既に接続されているため、(クライアント側で処理したように)connect() を呼び出さないでください。

manageConnectedSocket() はこのアプリの架空のメソッドで、データ転送用のスレッドを開始します。詳細については、接続の管理に関するセクションで説明します。

通常、接続要求のリッスンが完了したらすぐに BluetoothServerSocket をクローズする必要があります。 この例では、BluetoothSocket を取得してすぐ close() が呼び出されています。 また、サーバー ソケットのリッスンを停止する必要がある場合は、スレッド内でプライベートの BluetoothSocket をクローズできるパブリック メソッドを提供することもできます。

クライアント側の接続

リモート端末(オープンなサーバー ソケットを保持している端末)との接続を開始するには、まずリモート端末を表す BluetoothDevice オブジェクトを取得する必要があります(BluetoothDevice の取得については、端末の検索に関する上記のセクションをご覧ください)。 BluetoothDevice を使用して BluetoothSocket を取得し、接続を開始する必要があります。

基本的な手順は次のとおりです。

  1. BluetoothDevice を使用し、createRfcommSocketToServiceRecord(UUID) を呼び出して BluetoothSocket を取得する。

    これにより、BluetoothDevice に接続する BluetoothSocket を初期化します。 ここで渡される UUID は、サーバー端末が(listenUsingRfcommWithServiceRecord(String, UUID) を使用して)BluetoothServerSocket をオープンしたときに使用した UUID と一致している必要があります。 同じ UUID を使用するには単純に、UUID の文字列をアプリにハードコードして、サーバーとクライアントの両方のコードからその文字列を参照するだけです。

  2. connect() を呼び出して接続を開始する。

    この呼び出しによって、システムは UUID を照合するためにリモート端末の SDP ルックアップを実行します。 ルックアップに成功してリモート端末が接続を受け入れると、その接続で使用する RFCOMM チャンネルが共有され、connect() から制御が戻ります。 このメソッドはブロッキング コールです。 なんらかの理由で接続に失敗するか connect() メソッドがタイムアウトになると(約 12 秒後)、このメソッドは例外をスローします。

    connect() はブロッキング コールであるため、この接続手順は必ずメインのアクティビティ スレッドとは別のスレッドで実行する必要があります。

    注: connect() を呼び出すときは必ず、端末の検出を実行していないことを確認してください。 検出を実行していると、接続の試行処理が大幅に遅くなり、失敗する可能性があります。

以下は、Bluetooth 接続を開始するスレッドの基本的な例です。

private class ConnectThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;

    public ConnectThread(BluetoothDevice device) {
        // Use a temporary object that is later assigned to mmSocket,
        // because mmSocket is final
        BluetoothSocket tmp = null;
        mmDevice = device;

        // Get a BluetoothSocket to connect with the given BluetoothDevice
        try {
            // MY_UUID is the app's UUID string, also used by the server code
            tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
        } catch (IOException e) { }
        mmSocket = tmp;
    }

    public void run() {
        // Cancel discovery because it will slow down the connection
        mBluetoothAdapter.cancelDiscovery();

        try {
            // Connect the device through the socket. This will block
            // until it succeeds or throws an exception
            mmSocket.connect();
        } catch (IOException connectException) {
            // Unable to connect; close the socket and get out
            try {
                mmSocket.close();
            } catch (IOException closeException) { }
            return;
        }

        // Do work to manage the connection (in a separate thread)
        manageConnectedSocket(mmSocket);
    }

    /** Will cancel an in-progress connection, and close the socket */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}

cancelDiscovery() は、接続が確立される前に呼び出される点に注意してください。 必ず接続前に呼び出してください。これは実際に実行中かどうかを確認せずに呼び出しても問題ありません(どうしても確認したい場合は、isDiscovering() を呼び出します)。

manageConnectedSocket() はこのアプリの架空のメソッドで、データ転送用のスレッドを開始します。詳細については、接続の管理に関するセクションで説明します。

BluetoothSocket の処理が完了したら、必ず close() を呼び出してクリーンアップを実行してください。これにより接続済みソケットがクローズされ、内部のリソースがクリーンアップされます。

接続の管理

2 つ(またはそれ以上)の端末を正常に接続すると、各端末は接続済みの BluetoothSocket を受け取ります。 ここから、いよいよ端末間のデータ共有が始まります。 BluetoothSocket を使用すると、任意のデータを送信するための一般的な手順が簡素化されます。

  1. ソケットを介した転送を処理する InputStreamOutputStream を取得します。それぞれ、getInputStream()getOutputStream() を使用します。
  2. read(byte[])write(byte[]) を使用して、ストリームに対してデータを読み書きします。

これだけです。

もちろん、実装の詳細については検討が必要です。まず第一に、すべてのストリームの読み書きには必ず専用のスレッドを使用してください。 その理由は read(byte[]) メソッドと write(byte[]) メソッドがどちらもブロッキング コールであるためです。read(byte[]) は、ストリームからなにかを読み出すまでブロックします。write(byte[]) は通常ブロックしませんが、リモート端末がすぐに read(byte[]) を呼び出さずに中間バッファがいっぱいになったとき、フロー制御のためにブロックする場合があります。したがって、スレッド内のメイン ループは InputStream の読み込み専用にする必要があります。 スレッド内の別のパブリック メソッドを使用して、OutputStream への書き込みを開始することができます。

この処理の例を次に示します。

private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket) {
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;

        // Get the input and output streams, using temp objects because
        // member streams are final
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) { }

        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }

    public void run() {
        byte[] buffer = new byte[1024];  // buffer store for the stream
        int bytes; // bytes returned from read()

        // Keep listening to the InputStream until an exception occurs
        while (true) {
            try {
                // Read from the InputStream
                bytes = mmInStream.read(buffer);
                // Send the obtained bytes to the UI activity
                mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                break;
            }
        }
    }

    /* Call this from the main activity to send data to the remote device */
    public void write(byte[] bytes) {
        try {
            mmOutStream.write(bytes);
        } catch (IOException e) { }
    }

    /* Call this from the main activity to shutdown the connection */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}

コンストラクタが必要なストリームを取得し、実行すると、InputStream からデータを受信するまでスレッドは待機します。 read(byte[]) がストリームから取得したバイトを返すと、親クラスのメンバーである Handler によって、データはメイン アクティビティに送信されます。 元の処理に戻り、ストリームの次のバイトを待機します。

データの送信は、メイン アクティビティからスレッドの write() メソッドを呼び出し、送信するバイトを渡すだけなので簡単です。 このメソッドは、単純に write(byte[]) を呼び出して、データをリモート端末に送信します。

BluetoothSocket をクローズしていつでも接続を終了できるため、スレッドの cancel() メソッドは重要です。Bluetooth 接続の使用を完了したら、必ずこのメソッドを呼び出してください。

Bluetooth API を使用したデモについては、Bluetooth Chat サンプル アプリをご覧ください。

プロファイルの使用

Android 3.0 以降、Bluetooth API では Bluetooth プロファイルの操作がサポートされています。 Bluetooth プロファイルは、端末間で Bluetooth ベースの通信を行うためのワイヤレス インターフェース仕様です。 その一例が、Hands-Free プロファイルです。 スマートフォンをワイヤレス ヘッドセットに接続する場合、どちらの端末も Hands-Free プロファイルをサポートしている必要があります。

インターフェース BluetoothProfile を実装して、特定の Bluetooth プロファイルをサポートする独自のクラスを記述することができます。 Android Bluetooth API は、次の Bluetooth プロファイルの実装を提供しています。

プロファイルを操作するための基本的なステップは次のとおりです。

  1. Bluetooth の設定で説明したとおり、デフォルトのアダプタを取得します。
  2. getProfileProxy() を使用して、プロファイルと関連付けられたプロファイル プロキシ オブジェクトとの接続を確立します。以下の例で、プロファイル プロキシ オブジェクトは BluetoothHeadset のインスタンスです。
  3. BluetoothProfile.ServiceListener を設定します。このリスナーは BluetoothProfile IPC クライアントに、サービスへの接続またはサービスからの切断を通知します。
  4. onServiceConnected() で、プロファイル プロキシ オブジェクトのハンドルを取得します。
  5. プロファイル プロキシ オブジェクトを取得したら、それを使用して接続状態を監視し、プロファイルに関連したその他の操作を実行できます。

たとえば、このコード スニペットは BluetoothHeadset プロキシ オブジェクトに接続して、Headset プロファイルを制御する方法を示しています。

BluetoothHeadset mBluetoothHeadset;

// Get the default adapter
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

// Establish connection to the proxy.
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);

private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
    public void onServiceConnected(int profile, BluetoothProfile proxy) {
        if (profile == BluetoothProfile.HEADSET) {
            mBluetoothHeadset = (BluetoothHeadset) proxy;
        }
    }
    public void onServiceDisconnected(int profile) {
        if (profile == BluetoothProfile.HEADSET) {
            mBluetoothHeadset = null;
        }
    }
};

// ... call functions on mBluetoothHeadset

// Close proxy connection after use.
mBluetoothAdapter.closeProfileProxy(mBluetoothHeadset);

ベンダー固有の AT コマンド

Android 3.0 以降、ヘッドセットから送信された事前定義済みのベンダー固有の AT コマンドに対するシステムのブロードキャストを受信するため、アプリを登録できるようになりました(Plantronics +XEVENT コマンドなど)。 たとえば、接続された端末の電池レベルを示すブロードキャストをアプリが受信できれば、ユーザーに通知するなど、必要に応じてアクションを実行できます。 ヘッドセットのベンダー固有の AT コマンドを処理するには、ACTION_VENDOR_SPECIFIC_HEADSET_EVENT インテントのブロードキャスト レシーバーを作成します。

Health Device Profile

Android 4.0(API レベル 14)で、Bluetooth Health Device Profile(HDP)のサポートが導入されました。 これにより、Bluetooth を使用して、心拍モニタ、血圧計、体温計、体重計など、Bluetooth をサポートするヘルス機器と通信するアプリを作成できます。 Bluetooth Health API には、BluetoothHealthBluetoothHealthCallbackBluetoothHealthAppConfiguration の各クラスが含まれています。クラスの詳細については基本セクションをご覧ください。

Bluetooth Health API を使用するにあたり、HDP の主なコンセプトを理解しておくと役に立ちます。

コンセプト 説明
SourceHDP で定義されているロール。 Source は、Android 端末やタブレットなどの端末に医療データ(体重、血糖値、体温など)を送信するヘルス機器です。
SinkHDP で定義されているロール。 HDP では、Sink は医療データを受信するスマート端末です。 Android HDP アプリでは、Sink は BluetoothHealthAppConfiguration オブジェクトで表されます。
Registration特定のヘルス機器に Sink を登録することを指します。
Connectionヘルス機器と、Android 端末やタブレットなどのスマート デバイス間でチャンネルをオープンすることを指します。

HDP アプリの作成

Android HDP アプリの作成に必要な基本ステップは次のとおりです。

  1. BluetoothHealth プロキシ オブジェクトへの参照を取得します。

    通常のヘッドセットや A2DP プロファイル端末と同様に、BluetoothProfile.ServiceListener とプロファイルの種類である HEALTH を指定して getProfileProxy() を呼び出し、プロファイル プロキシ オブジェクトとの接続を確立します。

  2. BluetoothHealthCallback を作成して、Health Sink として機能するアプリ設定(BluetoothHealthAppConfiguration)を登録します。
  3. ヘルス機器との接続を確立します。一部の端末は、接続を開始します。 そのような端末では、このステップは実行する必要がありません。
  4. ヘルス機器と正常に接続できたら、ファイル ディスクリプタを使用してヘルス機器に対して読み取り / 書き込みを行います。

    受信したデータは、IEEE 11073-xxxxx 仕様を実装した Health Manager を使用して解釈する必要があります。

  5. 完了したら、Health チャンネルをクローズしてアプリの登録を解除します。チャンネルは、長時間使用されていない場合にもクローズされます。

これらのステップを示す完全なコード サンプルについては、Bluetooth HDP(Health Device Profile)をご覧ください。

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)