[Android]AltBeaconを使ってiBeaconを受信する

スポンサーリンク

はじめに

とある施設内にチェックポイントとしてiBeaconを置いて、施設内を回るスタンプラリー的なことをできるアプリを作った際のこと。

基本的に以下のサイトを参考にして実装しました。
AltBeaconを使ってAndroidでiBeaconを検知しよう

一応、AltBeaconのライブラリのコード全部見て、個人情報とか抜いてないことは確認した。
一部、ライブラリ本体には「android.permission.INTERNET」の権限付与されてないのに、ライブラリ内で「android.permission.INTERNET」を必要とするところがあったので見てみたら、距離計算のためのフォーマットファイル?をダウンロードしているだけだった。
android.permission.INTERNET」が付与されてない場合はローカルファイルを読みに行く実装でした。

上記サイトと違う点

・言語がKotlin
・startRangingBeaconsInRegion/stopRangingBeaconsInRegionをするタイミング
・onBeaconServiceConnectした時にNotifier等を削除

言語がKotlin

Kotlin楽しいよ。Kotlin。わからないことだらけだけど!
配列の初期化とか無理やりチックに似できるのが楽しい(ぇ

startRangingBeaconsInRegion/stopRangingBeaconsInRegionをするタイミング

参考にしたサイトではdidEnterRegionstartdidExitRegionstopしてるんだけど、BeaconManagerをunbind→bindすると何故かonBeaconServiceConnect後にdidEnterRegionが呼ばない。
なので、startRangingBeaconsInRegionが呼べずにRegionのモニタリングができなくなった。
ってことでdidDetermineStateForRegionの出入りのタイミングで登録、削除をしてあげるようにした。

onBeaconServiceConnectした時にNotifier等を削除

BeaconActivityが破棄されて、もう一度生成され、BeaconManagerを取得しーのNotifierをセットするとaddされる感覚で通知がたくさん来るようになった。
まぁ、呼び出してるのadd~~~なんだけど、、、。
unbindしたタイミングで破棄してくれないみたい。
同じインスタンスでセットする分には重複チェックされてるのかaddされてない感じ。
実装上はaddしてるんだけど、CopyOnWriteArraySetってクラス使っててよくわからない(調べてない)
なのでとりあえず、onBeaconServiceConnect時に削除するようにした。
unbindないしonDestroyとかのタイミングで消しても良さそうっちゃ良さそうだが、、、。

ってことで実装

//
abstract class BeaconActivity : BaseActivity(), BeaconConsumer {
 
 
    companion object {
        private val TAG = "hoge"
 
        // iBeaconのデータを認識するためのParserフォーマット
        val IBEACON_FORMAT = "m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"
    }
 
    // BeaconManagerクラスの変数を定義
    protected var mBeaconManager: BeaconManager? = null
    // Beacon UUIDの作成
    protected var mBeaconIdentifier = Identifier.parse("hogehage-0000-0000-0000-000000000000")
    protected var mNearestBeacon: Beacon? = null
 
    private val mRegion = Region("unique-id-001", mBeaconIdentifier, null, null)
 
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // staticメソッドでBeaconManagerクラスのインスタンスを取得
        mBeaconManager = BeaconManager.getInstanceForApplication(applicationContext)
        // BeaconParseをBeaconManagerに設定
        mBeaconManager?.beaconParsers?.add(BeaconParser().setBeaconLayout(IBEACON_FORMAT))
 
    }
 
    override fun onResume() {
        super.onResume()
        mBeaconManager?.bind(this)
    }
 
    override fun onPause() {
        super.onPause()
        mBeaconManager?.unbind(this)
    }
 
 
    override fun onBeaconServiceConnect() {
        Log.d(TAG, "onBeaconServiceConnect")
        // 2重登録されるので一旦削除
        mBeaconManager?.removeAllMonitorNotifiers()
        mBeaconManager?.removeAllRangeNotifiers()
        mBeaconManager?.rangedRegions?.forEach {region ->
            mBeaconManager?.stopRangingBeaconsInRegion(region)
        }
        // BeaconManagerクラスのモニタリング設定
        mBeaconManager?.addMonitorNotifier(mMonitorNotifier)
        // BeaconManagerクラスのレンジング設定
        mBeaconManager?.addRangeNotifier(mRangeNotifier)
        startMonitoringBeacons()
    }
 
    /**
     * ビーコンのモニタリング開始
     */
    private fun startMonitoringBeacons() {
 
        try {
            // モニタリングの開始
            mBeaconManager?.startMonitoringBeaconsInRegion(mRegion)
        } catch (e: RemoteException) {
            e.printStackTrace()
        }
 
    }
 
    /**
     * ビーコンモニター
     */
    private val mMonitorNotifier = object : MonitorNotifier {
        override fun didEnterRegion(region: Region) {
            // 領域進入時に実行
            Log.d(TAG, "didEnterRegion")
        }
 
        override fun didExitRegion(region: Region) {
            // 領域退出時に実行
            Log.d(TAG, "didExitRegion")
        }
 
        override fun didDetermineStateForRegion(i: Int, region: Region) {
            // 領域への侵入/退出のステータスが変化したときに実行
            Log.d(TAG, "didDetermineStateForRegion i = " + i)
 
            try {
                if (i == 1) {
                    mBeaconManager?.startRangingBeaconsInRegion(region)
                } else {
                    mBeaconManager?.stopRangingBeaconsInRegion(region)
                }
            } catch (e: RemoteException) {
                Log.d(TAG, "didDetermineStateForRegion e = " + e.message)
            }
 
        }
    }
 
    /**
     * ビーコンレンジ
     */
    private val mRangeNotifier = RangeNotifier { beacons, region ->
        // 検出したビーコンの情報を全部Logに書き出す
 
        for (beacon in beacons) {
            Log.d(TAG, "UUID:" + beacon.id1 + ", major:" + beacon.id2 +
                    ", minor:" + beacon.id3 + ", Distance:" + beacon.distance +
                    ",RSSI" + beacon.rssi + ", TxPower" + beacon.txPower)
        }
    }
}

これで受信できるようになるはず。
ただ、BlueToothがON状態じゃないと受信できないのと、6系以上は「Manifest.permission.ACCESS_COARSE_LOCATION」が許可されていないと受信できない。

https://amzn.to/2LIG1XC
タイトルとURLをコピーしました