干货分享:Android BLE 框架,简单易用,可能是迄今为止功能最全面的
https://github.com/a1anwang/okble
iBeacon系列文章:
Android BLE-iBeacon系列(一)iBeacon介绍
Android BLE-iBeacon系列(二)扫描识别iBeacon设备
Android BLE-iBeacon系列(三)iBeacon区域介绍
Android BLE-iBeacon系列(四)iBeacon区域的进入和退出监听
Android BLE-iBeacon系列(五)手机模拟为iBeacon(待续)
正文
上篇文章说道iBeacon区域,可以由具有相同的uuid来构成;也可以由相同的uuid和相同的major来构成;还可以由相同的uuid,major,minor来构成。这就意味着iBeacon区域有3个构造方法
public class BeaconRegion {
private int major=-1;
private int minor=-1;
private String uuid;
@Override
public String toString() {
return "BeaconRegion:[uuid:"+uuid+" major:"+major+" minor:"+minor+"]";
}
public static BeaconRegion getInstance(String uuid){
return new BeaconRegion(uuid);
}
public static BeaconRegion getInstance(String uuid, int major){
return new BeaconRegion(uuid,major);
}
public static BeaconRegion getInstance(String uuid ,int major,int minor){
return new BeaconRegion(uuid,major,minor);
}
private BeaconRegion(String uuid) {
super();
this.uuid = uuid;
}
private BeaconRegion(String uuid, int major) {
super();
this.uuid = uuid;
this.major=major;
}
private BeaconRegion(String uuid ,int major,int minor){
super();
this.major=major;
this.minor=minor;
this.uuid=uuid;
}
//区域的唯一标识
public String getIdentifier(){
return this.uuid+"_"+this.major+"_"+this.minor;
}
public String getUuid() {
return uuid;
}
public int getMajor() {
return major;
}
public int getMinor() {
return minor;
}
}
接下来我定义一个BeaconRegionManager用来监听Beacon区域。再定义一个接口BeaconRegionListener用来回调状态。
public interface OKBLEBeaconRegionListener{
void onEnterBeaconRegion(BeaconRegion beaconRegion);
void onExitBeaconRegion(BeaconRegion beaconRegion);
}
public class BeaconRegionManager implements BeaconScanManager.BeaconListener {
private Context mContext;
private BeaconScanManager scanManager;//这是系列二文章里写好的Beacon设备扫描器
private BeaconListener scanCallback;//beacon扫描回调
private BeaconRegionListener regionListener;
public BeaconManager(Context context){
super();
this.mContext=context;
scanManager=new BeaconScanManager(mContext);
scanManager.setBeaconListener(this);
}
public void setRegionListener(BeaconRegionListener regionListener){
this.regionListener=regionListener;
}
//开始监控Beacon区域的方法
public void startMonitoringForRegion(BeaconRegion region) {
}
//停止监控Beacon区域的方法
public void stopMonitoringForRegion(OKBLEBeaconRegion region){
}
//扫描回调
@Override
public void onScanBeacon(Beacon beacon) {
Log.e(TAG, " 扫描到beacon设备:" + beacon.toString());
//在这里判断区域的退出和进入
}
}
定义好之后,接下来该是怎么实现了。当开启监控Beacon区域的时候,需要一直扫描周围的Beacon设备,然后判断扫描到的Beacon设备是否属于 所监控区域的某一个,是的话就回调进入该区域,这个有一个注意点就是:扫描Beacon设备的时候,是一直回调的,也就是说同一个Beacon设备会重复扫描到,所以要做好进入区域的回调判断,如果已经进入过该区域且未退出,那么不需要再回调进入区域。
如何判断退出某一个区域呢?那就是持续一段时间之内一直没有扫描到该区域里的Beacon设备,视为退出区域。我们暂定这个持续时间为20秒,就是说当进入一个区域后,在连续的20s内都没有再扫描到该区域的Beacon设备,则表示我们离开了该区域。
苹果ios系统也是采用的20s,这个时间是我实测的,我用苹果手机监控一个区域,给Beacon设备上电,回调进入区域,然后我立刻把Beacon设备电池拔掉,等了有20s左右,ios回调退出区域。
接下来看代码实现:
首先封装一层BeaconRegion,命名为RegionObject:
private class RegionObject{
boolean hasEntered;//用来判断是否已经进入过区域,避免重复回调
BeaconRegion region;
int regionID;//id,不同的区域id不一样
public RegionObject(OKBLEBeaconRegion region, int regionID) {
super();
this.region=region;
this.regionID=regionID;
}
}
实现监控方法:
private Map<String, RegionObject> monitoringBeaconRegions = new HashMap<String, RegionObject>();//监控区域集合
private int monitoringBeaconRegionID = 0;//监控的iBeacon区域的id
public void startMonitoringForRegion(BeaconRegion region) {
String key = region.getIdentifier();//获取需要监控的区域的唯一标识
if (!monitoringBeaconRegions.containsKey(key)) {
//避免重复监控同一个区域
monitoringBeaconRegionID++;//用一个递增的int作为所监控区域的id
RegionObject regionObject=new RegionObject(region,monitoringBeaconRegionID);
monitoringBeaconRegions.put(key, regionObject);
if(!isScanning()){
startScanBeacon();
}
}
}
/**
* 开始扫描iBeacon
*/
public void startScanBeacon(){
scanManager.stopScan();
scanManager.startScan();
}
public boolean isScanning(){
return scanManager.isScanning();
}
当扫描到Beacon设备时,我们就判断该设备是否属于某一个区域
//扫描回调
@Override
public void onScanBeacon(Beacon beacon) {
Log.e(TAG, " 扫描到beacon设备:" + beacon.toString());
//在这里判断区域的退出和进入
//这里请大家再次注意一下上面的BeaconRegion的唯一标识的定义,是由uuid,major,minor组成。
// major,minor默认为-1,大家知道BeaconRegion有三个构造方法
//可以由单独的uuid构成
//也可以由uuid+major构成
//还可以由uuid+major+minor构成。
String key=beacon.uuid+"_-1_-1";//这个对应了由单独的uuid构成的区域
if (monitoringBeaconRegions.containsKey(key)) {
//正在监控这个区域
RegionObject regionObject=monitoringBeaconRegions.get(key);
handleEnterRegion(regionObject);
}
key=beacon.uuid+beacon.major+"_-1";//这个对应了由uuid+major构成的区域
if (monitoringBeaconRegions.containsKey(key)) {
//正在监控这个区域
RegionObject regionObject=monitoringBeaconRegions.get(key);
handleEnterRegion(regionObject);
}
key=beacon.uuid+"_"+beacon.major+"_"+beacon.minor;//这个对应了由uuid+major+minor构成的区域
if (monitoringBeaconRegions.containsKey(key)) {
//正在监控这个区域
RegionObject regionObject=monitoringBeaconRegions.get(key);
handleEnterRegion(regionObject);
}
}
private void handleEnterRegion(RegionObject regionObject) {
if(!regionObject.hasEntered){//避免重复回调
regionObject.hasEntered = true;
if(regionListener!=null){
if(isScanning()){
regionListener.onEnterBeaconRegion(regionObject.region);
}
}
}
}
这样监控区域的进入已经完成。监控区域的退出稍微复杂点,因为需要持续一段时间没有扫描到才认为是退出。那么想法就是,当判断到进入区域时,立刻开启一个延时20秒的方法,当这个延时方法执行到的时候就认为退出了这个区域,这里需要注意的是:当处于一个区域时,是会不断的扫描到该区域的Beacon设备的,所以一但扫描到Beacon设备并且Beacon设备属于某个区域,那么需要重置这个20秒延迟,取消上一个延迟,重新开一个延迟。我是用Hanler来实现的,请看
private int regionExitOverTime = 20 * 1000;//20秒,退出区域的超时时间,持续regionExitOverTime这么长的时间内没有再次扫描到这个区域,则视为退出区域
private void handleEnterRegion(RegionObject regionObject) {
handler.removeMessages(regionObject.regionID);//移除超时时间后回调退出区域的消息
Message msg = new Message();
msg.what = regionObject.regionID;
msg.obj = regionObject.region.getIdentifier();
handler.sendMessageDelayed(msg,regionExitOverTime);//重新发送一个延时消息,
if(!regionObject.hasEntered){
regionObject.hasEntered = true;
if(regionListener!=null){
if(okbleScanManager.isScanning()){
regionListener.onEnterBeaconRegion(regionObject.region);
}
}
}
}
Handler handler= new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String key = (String) msg.obj;
//收到消息,表示已经持续了一段时间没有扫描到区域内的beacon了,视为退出区域
if (monitoringBeaconRegions.containsKey(key)) {
RegionObject regionObject= monitoringBeaconRegions.get(key);
BeaconRegion beaconRegion =regionObject.region;
regionObject.hasEntered = false;
if(regionListener!=null){
if(okbleScanManager.isScanning()){
regionListener.onExitBeaconRegion(beaconRegion);
}
}
}
}
};
到此,完成iBeacon区域的进入和退出监听,所有的功能可以直接看文章开头提到的框架,直接使用。
网友评论