美文网首页
Android BLE-iBeacon系列(四)iBeacon区

Android BLE-iBeacon系列(四)iBeacon区

作者: Holly_dd20 | 来源:发表于2019-11-14 17:42 被阅读0次

干货分享:Android BLE 框架,简单易用,可能是迄今为止功能最全面的
https://github.com/a1anwang/okble


iBeacon系列文章:

\color{red}{注:iBeacon是苹果在BLE基础上的封装,android系统没有提供Beacon相关的封装}
\color{red}{本文iBeacon系列代码是完全按照ios的api来设计的,在APP开发时android和ios的逻辑是一模一样的}
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区域的进入和退出监听,所有的功能可以直接看文章开头提到的框架,直接使用。

相关文章

网友评论

      本文标题:Android BLE-iBeacon系列(四)iBeacon区

      本文链接:https://www.haomeiwen.com/subject/aflkictx.html