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

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 21
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值