Android Beacon開發

Bluetooth BLE Beacon初探

iBeacon是蘋果公司2013年9月發佈的移動設備用OS(ios7)上配備的新功能。其主要的工作方式就是:配備有低功耗藍牙(BLE)通信功能的設備使用BLE技術向周圍發送自己特有的ID。

這個網址對iBeacon進行了基本介紹,建議大家去閱讀一下:http://www.beaconsandwich.com/what-is-ibeacon.html

在2015年,谷歌發佈Eddystone,它其實類似於iBeacon。

對於這兩者的主要區別,大家可以瀏覽這個網址的內容:https://www.zhihu.com/question/32708729

本文的demo開發是基於github上的一個開源項目Altbeacon:https://github.com/AltBeacon/android-beacon-library

下面進入主題:

  1. 創建工程之後,在buid.gradle中導入AltBeacon Library:
    dependencies {
        compile fileTree(include: ['*.jar'], dir: 'libs')
        testCompile 'junit:junit:4.12'
        compile 'com.android.support:appcompat-v7:23.2.1'
        compile 'com.android.support:design:23.2.1'
        compile 'org.altbeacon:android-beacon-library:2.9'
    }
  2. 創建一個Application,這個Application需要實現一個接口:BootstrapNotifier,關鍵代碼如下:
        private RegionBootstrap regionBootstrap;
        private BackgroundPowerSaver backgroundPowerSaver;
    
        @Override
        public void onCreate() {
            super.onCreate();
            BeaconManager beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
            beaconManager.getBeaconParsers().clear();
            beaconManager.getBeaconParsers().add(new BeaconParser()
            .setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"));
            Region region = new Region("all-region-beacon",null,null,null);
            regionBootstrap = new RegionBootstrap(this,region);
            backgroundPowerSaver = new BackgroundPowerSaver(this);
        }
    在以上代碼中,有幾個關鍵的點:調用getBeaconParsers()獲取BeaconParsers列表,然後添加我們自己定義的BeaconParsers。調用方法setBeaconLayout(String)來設置Beacon格式。
    setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25")
    上述方法的傳入參數格式說明如下: 
    m-matching byte sequence for this beacon type to parse(exactly one required)
    s - ServiceUuid for this beacon type to parse(option,only for Gatt-based beacons)
    i - identifier(at least one required,multiple allowed)
    p - power calibration field(exactly one required)
    d - data field(option,multiple allowed)果不進行重新設置,那麼它默認的BeaconLayout是:"m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25",它只會去匹配字節序列爲beac的Beacon設備,筆者開發使用的Beacon設備的匹配字節序列是0215,所以要修改成0215,下面會詳細介紹如何獲取Beacon的匹配字節序列。在進行BLE設備掃描時,會有一個回調方法被調用到--onLeScan(final BluetoothDevice device,int rssi,byte[]scanRecord),而以上所述的Beacon格式的所有內容都包含在scanRecord這個參數中,下面根據我所獲取到的數據來具體分析參數scanRecord的內容:
    02 01 1e 1a ff 4c 00 02 15 12 23 34 45 56 67 78 89 90 01 12 23 34 45 56 67 00 12 00 21 c5 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    將以上數據中的一些無關數據去除掉,剩下了:
    02 01 1e 1a ff 4c 00 02 15 12 23 34 45 56 67 78 89 90 01 12 23 34 45 56 67 00 12 00 21 c5
    以上數據共有30字節,包含了匹配字節序列、UUID、MajorID、MinorID、TxPower等信息。經過整理,我們得到:
    02 01 1e 1a ff 4c 00 02 15 //beacon的前綴,包含匹配字節序列
    12 23 34 45 56 67 78 89 90 01 12 23 34 45 56 67 //UUID
    00 12 //MajorID
    00 21 //MinorID
    c5 //TxPower
    以上代碼段第一行中的0215就是我們程序中需要設置的匹配字節序列。那麼經過修改之後,我們就能掃描到自己的Beacon設備了。在以上的關鍵代碼中,創建了一個BackgroundPowerSaver實例,只要在這個Application中持有這個類對象,那麼就能實現後臺節省電量的功能。Region region = new Region("all-region-becon",null,null,null),實例化該對象時可以傳入一些參數,他們分別是String uniqueId,Identifier id1,Identifier id2,Identifier id3,利用這些傳入參數可以選擇性的獲取指定的Beacon信號。筆者此處沒有傳入相關參數,表示獲取所有的Beacon信號。
  3. 創建一個Service來進行後臺監聽Beacon信號,關鍵代碼如下:
    public class BeaconService extends Service implements BeaconConsumer, RangeNotifier {
    
        private static final long DEFAULT_BACKGROUND_SCAN_PERIOD = 1000L;
        private static final long DEFAULT_BACKGROUND_BETWEEN_SCAN_PERIOD = 1000L;
    
        private BeaconManager beaconManager = BeaconManager.getInstanceForApplication(this);
    
        public BeaconService() {
    
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            initBeacon();
            beaconManager.bind(this);
        }
    
        private void initBeacon() {
            beaconManager.setBackgroundScanPeriod(DEFAULT_BACKGROUND_SCAN_PERIOD);
            beaconManager.setBackgroundBetweenScanPeriod(DEFAULT_BACKGROUND_BETWEEN_SCAN_PERIOD);
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            if (beaconManager != null)
                beaconManager.removeRangeNotifier(this);
        }
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        @Override
        public void onBeaconServiceConnect() {
            Region region = new Region("myRangingUniqueId", null, null, null);
            beaconManager.addRangeNotifier(this);
            try {
                beaconManager.startRangingBeaconsInRegion(region);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void didRangeBeaconsInRegion(Collection<Beacon> collections, Region region) {
            List<Beacon> beacons = new ArrayList<>();
            for (Beacon beacon : collections) {
                beacons.add(beacon);
            }
            Intent intent = new Intent(MainActivity.BEACON_ACTION);
            intent.putParcelableArrayListExtra("beacon", (ArrayList<? extends Parcelable>) beacons);//因爲Beacon繼承了Parcelable,
            sendBroadcast(intent);                                                                   // 所以能通過這個方式來傳遞數據
        }
    }
    

    在這裏,大家可以自定義設置一下後臺掃描間隔,不然,它默認的後臺掃描間隔是300000毫秒,也就是5分鐘setBackgroundScanPeriod()和setBackgroundBetweenScanPeriod(),設置後臺掃描的時間間隔,我沒有去仔細閱讀源碼,根據我的實驗,發現如下圖所示的結論(僅供參考):
  4. 在MainActivity中利用startService()方式開啓後臺服務:
    public class MainActivity extends AppCompatActivity {
    
        private BeaconBroadcastReceiver beaconBroadcastReceiver;
        private static final String TAG = "MainActivity";
        public static final String BEACON_ACTION = "com.juju.beacontest.beacon.action";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            beaconBroadcastReceiver = new BeaconBroadcastReceiver();
            Intent intent = new Intent(MainActivity.this, BeaconService.class);
            startService(intent);
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            registerReceiver(beaconBroadcastReceiver, getBeaconIntentFilter());
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            if (beaconBroadcastReceiver != null)
                unregisterReceiver(beaconBroadcastReceiver);
        }
    
        class BeaconBroadcastReceiver extends BroadcastReceiver {
    
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (BEACON_ACTION.equals(action)) {
                    List<Beacon> beacons = intent.getParcelableArrayListExtra("beacon");
                    for (Beacon beacon : beacons){
                        Log.i(TAG, "onReceive: "+beacon.getServiceUuid());
                    }
                }
            }
        }
    
        IntentFilter getBeaconIntentFilter() {
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction(BEACON_ACTION);
            return intentFilter;
        }
    }
    

本文的Demo代碼已經上傳,大家可以自行下載參考。第一次進行Beacon開發,所以想記錄一下beacon的基本開發過程。當然,本文可能會有一些錯誤的地方,歡迎各位大神指正。另外,如果大家有什麼疑問,可以在下面進行評論,在我的能力範圍之內,一定會給你回答的。

發佈了35 篇原創文章 · 獲贊 35 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章