AndroidのBluetoothでスキャンしてみる

はじめに

今までiOSやReactNative, RxAndroidなどではBluetoothアプリの実装をしたことがあったのですが、 Androidで素のBluetoothの実装はやったことながなかったので試してみました。

CoreBluetoothで実装した時と似ていて、スキャン→発見→ディスカバーサービスのように連鎖的に処理をつなげていくようです。

※アプリ起動後初回は設定から位置情報の権限を許可しないとBluetoothが使えないので注意

解説

permission

AndroidでBluetoothを使用するためには位置情報の許可が必要なのでpermissionを記述する必要があります。

AndroidManifest.xmlに以下のpermissionを追加します。

    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

コード

CentralのインスタンスBluetoothManagerを作成してBluetoothManagerのbluetoothLeScannerを使ってスキャン行います。

startScan()メソッドの引数にスキャンを行う際のフィルターやセッティング、コールバックの受け取り先を指定します。

        val manager: BluetoothManager =
            getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
        val adapter = manager.getAdapter()
        val bluetoothLeScanner: BluetoothLeScanner = adapter.bluetoothLeScanner

        ~省略~
        bluetoothLeScanner.startScan(scanFilterList, scanSettings, scanCallback);

フィルターの例でいうと、特定の名前のデバイス以外を検索したくない場合は以下のように名前を指定しておくことで余計なデバイスを弾くことができます。

var scanFilter: ScanFilter = ScanFilter.Builder()
            .setDeviceName("名前")
            .build()

スキャンのセッティングについてはこの記事にわかりやすくまとめられていたので参考にしてみてください。

あとはスキャン結果のデバイス情報を受け取るコールバックリスナーを作ってあげれば完成です!

デバイスのmacアドレスをコンソールに出力しています。

val scanCallback: ScanCallback = @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    object : ScanCallback() {
        override fun onScanResult(callbackType: Int, result: ScanResult) {
            super.onScanResult(callbackType, result)
            Log.d("scanResult:", result.device.address)
        }
    }

コード一覧


import android.bluetooth.BluetoothManager
import android.bluetooth.le.*
import android.content.Context
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.annotation.RequiresApi

class MainActivity : AppCompatActivity() {

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val manager: BluetoothManager =
            getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
        val adapter = manager.getAdapter()
        val bluetoothLeScanner: BluetoothLeScanner = adapter.bluetoothLeScanner


        var scanFilter: ScanFilter = ScanFilter.Builder()
            .build()
        var scanFilterList: ArrayList<ScanFilter> = ArrayList()
        scanFilterList.add(scanFilter);

        var scanSettings: ScanSettings = ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_BALANCED)
            .build()
        Log.d("TAG", "startScan set")
        bluetoothLeScanner.startScan(scanFilterList, scanSettings, scanCallback);
    }

    //スキャンで見つかったデバイスが飛んでくる
    val scanCallback: ScanCallback = @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    object : ScanCallback() {
        override fun onScanResult(callbackType: Int, result: ScanResult) {
            super.onScanResult(callbackType, result)
            Log.d("scanResult:", result.device.address)
        }
    }
}

参考文献

BLE ScanSettingsの概要