Android BLE Beacon的探索

来源:互联网 发布:c语言的缺点 编辑:程序博客网 时间:2024/04/29 03:32

Bluetooth BLE Beacon初探

转载请注明出处:http://blog.csdn.net/wjskeepmaking/article/details/52067585

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 //UUID00 12 //MajorID00 21 //MinorIDc5 //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.setBackgroundScanPeriod(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的基本开发过程。当然,本文可能会有一些错误的地方,欢迎各位大神指正。另外,如果大家有什么疑问,可以在下面进行评论,在我的能力范围之内,一定会给你回答的。

1 0
原创粉丝点击