文档章节

Android 蓝牙防丢(二)

iSnowFlake
 iSnowFlake
发布于 2015/10/17 17:40
字数 1973
阅读 1247
收藏 6

 

github 源码: https://github.com/AnyLifeZLB/Bluetooth-AntiLost

 

Demo 地址:https://play.google.com/store/apps/details?id=com.bipbip.ble  (翻墙下载)

 

/**
 * 防丢核心算法,早期整理版本不完善,根据自身业务完善,
 * @anylife.zlb@gmail.com
 */

public class BlePreventLostCore {
 private static String TAG=BlePreventLostCore.class.getSimpleName();
 private final  static int leastNum=3; // 设备采集到的SSID[] size>3才是有意义的值
 private final  static double deviateBaseFlag=0.1; //
 private final  static double WCF=0.5;         //Weight Compensation Flag,权重补偿因子
 
 private static Map<String,List<Integer>> sacnedBleDevicesData;
 /**
  * 去除采集到数据中的脏值
  * deviateBaseFlag 为基准的脏值偏移标志,如果长度比较大的话,可以适当的加大             
  * 先简单的以算术平均数作为参照,大于average 的deviateBaseFlag*average 就判断是脏值。
  * 
  * 
  */
 public static void clearDigest() {
//  double deviateFlag=0.2**……&&N; //
  Map<String,Double> sAverageSSID=new HashMap<>();   //简单算术平均数
  Set<String> ks =sacnedBleDevicesData.keySet();
  
  //求对应手表的算术平均数。
  for(String key : ks ){
   double average=0.0;   
   List<Integer> ssidList=sacnedBleDevicesData.get(key);
   int size=sacnedBleDevicesData.get(key).size();
   for(int i=0;i<size;i++){
    average=average+ssidList.get(i);
   }   
   average=(double)average/size;
   sAverageSSID.put(key, average);  
  }
  
  //去除脏值。
  for(String key : ks ){
//   double average=0.0;   
   List<Integer> ssidList=sacnedBleDevicesData.get(key);
   int size=sacnedBleDevicesData.get(key).size();
   if(size>leastNum){
    double averageTemp=sAverageSSID.get(key);
    while(--size!=-1){
     Log.e(TAG,key+"i: "+size);
     if(Math.abs(ssidList.get(size)-averageTemp) >  Math.abs(deviateBaseFlag*averageTemp)){
      Log.d(TAG,key+"移除 脏值索引 i: "+size);
      ssidList.remove(size);
     }
    } 
    sacnedBleDevicesData.put(key, ssidList);
   }
  }
  
 }
 
 /**
  * ssid 对用的权重分布[... , ...]
  * 
  * @return
  */
 public static Map<String,List<Double>> getWeghtCompensationMap(){
  Map<String,List<Double>> weghtCompensationDatas =  new HashMap<String,List<Double>>(); //权数分布
//  Map<String,Double>  weghtCompensationDecreasing; //权重递减因子
  //1.初始化权数分布
  Set<String> ks =sacnedBleDevicesData.keySet();
  for(String key : ks ){
   final int size=sacnedBleDevicesData.get(key).size();
   List<Double> weghtCompDataList=new ArrayList<Double>(size-1);
   if(size>leastNum){
    //1.权重分布的前半部分初始化
    for(int i=0;i<size;i++){
                 double decreasing=WCF-WCF*2*(i+1)/size;
     if(decreasing>0&&i<size/2){
      weghtCompDataList.add(i, decreasing);
     }else{
      weghtCompDataList.add(i, (double) 0);
     }
    }
    
    //2.权重分布的后半部分初始化
    for(int j=size-1;j>size/2;j--){
     double temp=weghtCompDataList.get(size-j-1);
     weghtCompDataList.set(j,temp);
    }
 
    //3.权重分布的 实际处理
    for(int i=0;i<size/2;i++){
     weghtCompDataList.set(i, (1-weghtCompDataList.get(i))/size);
    }
    for(int j=size-1;j>size/2;j--){
     weghtCompDataList.set(j, (1+weghtCompDataList.get(j))/size);
    }
    
    //4.权重分布的 中位数实际处理
    if(size%2==0){
     weghtCompDataList.set(size/2,1.0/size);
    }else{
     weghtCompDataList.set(size/2, 1.0/size);
     weghtCompDataList.set(size/2-1, 1.0/size);
    }
    
    //5.权重分布测试,相加应该无限接近    100/100=1
    double test=0;
    for(int s=0;s<size;s++){
     test=test+weghtCompDataList.get(s);
    }
    Log.e(TAG,key+" test 和: "+test);
   }else{
    for(int k=0;k<size;k++){
     weghtCompDataList.add(k, 1.0/size);
    }
   }
   weghtCompensationDatas.put(key, weghtCompDataList);
  }//权重分布完成

 /**
  * 时间越后,权重越大
  * 
  * @param sacnedBleDD
  * @return
  */
 public static Map<String,Double> getDeviceState(final Map<String,List<Integer>> sacnedBleDD){
  sacnedBleDevicesData=sacnedBleDD;
  clearDigest(); //去除脏值
  
  Map<String,List<Double>> weightCompensation=getWeghtCompensationMap();      //权数分布因子 
  Log.e(TAG,"  "+weightCompensation);
  
  Map<String,Double> averageSSID=new HashMap<>();
  Set<String> ks =sacnedBleDevicesData.keySet();
  for(String key : ks ){
   double average=0.0;
   List<Double> weghtCompDataList=weightCompensation.get(key);
   List<Integer> ssidList=sacnedBleDevicesData.get(key);
   for(int i=0;i<weghtCompDataList.size();i++){
    average=average+weghtCompDataList.get(i)*ssidList.get(i);
   }   
   
   Log.d(TAG,key+" 加权算术平均:"+average);
   averageSSID.put(key, average);  
   //bingo.
  }
  return averageSSID;
 }
}

 

Activity

package com.bipbip.ble;

import com.bipbip.main.BaseActivity;

/**
 * 
 * 
 * @author anylife.zlb@gmail.com
 */
@SuppressLint("NewApi")
public class BlePreventLostActivity extends BaseActivity implements OnClickListener{
    private String TAG=BlePreventLostActivity.class.getSimpleName();
    private TextView instruction_tips; 
    private ListView bleListView;
    
    //已经扫描出来的设备列表集Set,自定义obj是否重复
    private List<BleDevice> findedBleDevicesList = new ArrayList<BleDevice>(); 
    
    private List<BleDevice> sacnedBleDevicesList = new ArrayList<BleDevice>();              

    //已经扫描出来的设备(MAC作为Key)数据集合(List<?>   ?需要是BleDevice?不如integer  )
    private Map<String,List<Integer>> sacnedBleDevicesData = new HashMap<String,List<Integer>>();

    private BleDeviceListAdapter mDeviceListAdapter;
    private BluetoothAdapter mBluetoothAdapter;
    private boolean mFindDevice=true;  //true : 刚刚进入蓝牙防丢的设备扫描阶段;    false:对扫描出来的设备进行蓝牙防丢
    private boolean mLiveDevice=false;  //true : 刚刚进入蓝牙防丢的设备扫描阶段;    false:对扫描出来的设备进行蓝牙防丢

    private Handler mHandler;
    private DeviceLiveThread deviceLiveThread;
    private static final int REQUEST_ENABLE_BT = 1;
    // Stops scanning after 10 seconds.
    private static final long SCAN_PERIOD = 1000*10;       //点击开始扫描后的10秒停止扫描
    private static final long LIVE_PERIOD = 1000*7;       //点击开始扫描后的10秒停止扫描

    @Override
    public void onCreate(Bundle savedInstanceState) {    //
        super.onCreate(savedInstanceState);
        setContentView(R.layout.ble_prevent_lost);
        instruction_tips=(TextView) findViewById(R.id.instruction_tips);
        instruction_tips.setOnClickListener(this);
        bleListView=(ListView) findViewById(R.id.ble_device_list);
   
        // Initializes list view adapter.
        mDeviceListAdapter =new BleDeviceListAdapter(this,findedBleDevicesList);// new mDeviceListAdapter(this);
        bleListView.setAdapter(mDeviceListAdapter);

        mHandler = new Handler();
        // Use this check to determine whether BLE is supported on the device.  Then you can
        // selectively disable BLE-related features.
        
        //如果手机不支持蓝牙4.0,api>18. 那么直接退出
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
            finish();
        }
   
        // Initializes a Bluetooth adapter.  For API level 18 and above, get a reference to
        // BluetoothAdapter through BluetoothManager.
        final BluetoothManager bluetoothManager =(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();

        // Checks if Bluetooth is supported on the device.
        if (mBluetoothAdapter == null) {
            Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
            finish();
            return;
        }
        
        Log.e(TAG," test"+BleDeviceState.UNKNOW.getStateName());
        
        findDevice(true);
            
    }
     
    
    /**
     * 扫描设备蓝牙设备
     * 
     * 
     * @param enable  
     *        true:扫描设备,并在SCAN_PERIOD后停止         
     *        false:不扫描,直接就停止了(在onPause中停止了)
     */
    private void findDevice(final boolean enable) {
        if (enable) {
            
            setInstructStytle(true);
            mFindDevice = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);   //开始扫描设备    
            mDeviceListAdapter.notifyDataSetChanged();

            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    Log.d(TAG,"\n\n设备个数:"+findedBleDevicesList.size()+"\n设备信息 :\n"+findedBleDevicesList.toString());
                    if(mFindDevice){
                        if(findedBleDevicesList!=null&&findedBleDevicesList.size()>0){
                            instruction_tips.setVisibility(View.VISIBLE);
//                            findedBleDevicesList -->  sacnedBleDevicesData
                            
                            for(int i=0;i<findedBleDevicesList.size();i++){
                                List<Integer> tempList=new ArrayList<Integer>();
                                tempList.add(findedBleDevicesList.get(i).getSsid());
                                sacnedBleDevicesData.put(findedBleDevicesList.get(i).getMacAddr(), tempList);
                            }
                          
                        }              
                    }
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);

                    mFindDevice=false;
                    invalidateOptionsMenu();
                    
                }
            }, SCAN_PERIOD);

        } else {
            setInstructStytle(false);
            mFindDevice = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
          
      invalidateOptionsMenu();

    }
    
    /** 
     * Device scan callback.
     * 
     * if you call  [mBluetoothAdapter.startLeScan(mLeScanCallback);]this will call back.
     * 
     */
    private BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Log.e(TAG,"设备"+device.getName()+"    Rssi强度:"+rssi+" "+device.getAddress());
                    BleDevice bleDevice=new BleDevice(0, device.getName(), device.getAddress(), "uuid", rssi, device.getBondState(), device.getType());                    
                    
                    if(mFindDevice){ //处于第一次的发现设备阶段
                        if(findedBleDevicesList!=null&&!findedBleDevicesList.contains(bleDevice)){
                            findedBleDevicesList.add(bleDevice);
                            mDeviceListAdapter.notifyDataSetChanged();
                        }
                    }else{
                           
                        //
                        if(sacnedBleDevicesList!=null&&!sacnedBleDevicesList.contains(bleDevice)){
                            sacnedBleDevicesList.add(bleDevice);
//                            mDeviceListAdapter.notifyDataSetChanged();
                        }
                        
                        //2. 向MAC 地址对应的List<Integer> 添加一个值。
                        List<Integer> bleDevices=sacnedBleDevicesData.get(bleDevice.getMacAddr());
                        if(null==bleDevices){
                            bleDevices=new ArrayList<Integer>();
                        }
                        bleDevices.add(rssi);
                        sacnedBleDevicesData.put(bleDevice.getMacAddr(), bleDevices);    
                    }
                  }
            });
        }
    };

    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // User chose not to enable Bluetooth.
        if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
            finish();
            return;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }
    
    /**
     * 设置一些样式
     * 
     * @param needReScan
     */
    private void setInstructStytle(boolean needReScan){
        
        if(needReScan){
            instruction_tips.setVisibility(View.INVISIBLE);
            mDeviceListAdapter.clear(); 
        }else{//要那种慢慢显示出来的效果
            instruction_tips.setVisibility(View.VISIBLE);
        }
            
    }
    
    
    
    public class DeviceLiveThread extends Thread {
        private boolean isRunning=true;
        
        private void stopThread(){
            isRunning=false;
        }
        
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while(isRunning){
                if(mLiveDevice){
                    Log.d(TAG,"统计结果,采集数据为==================\n");
                    Set<String> keyset=sacnedBleDevicesData.keySet();
                    for(String key:keyset){
                        Log.e(TAG,"size="+sacnedBleDevicesData.get(key).size()+" "+sacnedBleDevicesData.get(key));
                    }
                    Log.d(TAG,"统计结束,清除本次统计*****************************************************\n");
                    
                    if(null!=sacnedBleDevicesData&&sacnedBleDevicesData.size()>0){
                        Map<String,Double> rssiValueList=BlePreventLostCore.getDeviceState(sacnedBleDevicesData); //根据换回的结果统计
                        displayLiveResult(rssiValueList);
                        rssiValueList.clear();
                        sacnedBleDevicesData.clear();//一次分析完后,清除
                    } else{  //上一次分析完后再也没有采集到数据,全部丢失
                        displayLiveResult(null);
                    }

                    try {
                        Thread.sleep(LIVE_PERIOD);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }else{
                    try {
                        Thread.sleep(LIVE_PERIOD);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }

            }    
        }
    }
    
    
    
    /**
     * 开始进入
     * 
     */
    private void startDeviceLiving(){
        if(deviceLiveThread==null){
            deviceLiveThread=new DeviceLiveThread();
            deviceLiveThread.start();
        }
    }
    
    /**
     * 更新数据显示的结果
     * 
     * @param rssiValueList
     */
    private void displayLiveResult(Map<String,Double> rssiValueList){
        
        //0.处理异常的情况
        if(null==rssiValueList||rssiValueList.size()==0){
            int size=findedBleDevicesList.size();
            for(int i=0;i<size;i++){
                findedBleDevicesList.get(i).setSsid(0);
            }
        }else{
            
            //1.处理返回来的适配数据
            Set<String> keySet=rssiValueList.keySet();
            int size=findedBleDevicesList.size();
            for(int i=0;i<size;i++){
                String findedDeviceKey=findedBleDevicesList.get(i).getMacAddr(); //
                if(keySet.contains(findedDeviceKey)){ //假如以前扫描到的设备在监听阶段还是存在,那么重新赋值RSSI,否则丢失了
                    findedBleDevicesList.get(i).setSsid(rssiValueList.get(findedDeviceKey).intValue());
                }else{  //置设备状态为丢失,震动报警
                    findedBleDevicesList.get(i).setSsid(0);
                }
            }
        }
        
        //2.更新显示数据。
        runOnUiThread(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                mDeviceListAdapter.notifyDataSetChanged();
            }
        });
    }

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch (v.getId()) {
        case R.id.instruction_tips:
            mLiveDevice=!mLiveDevice;
            invalidateOptionsMenu();
                                        
            if(mLiveDevice){
                instruction_tips.setText(R.string.device_live_stop);
                mBluetoothAdapter.startLeScan(mLeScanCallback);   //开始扫描设备    
                startDeviceLiving();
            }else{
                instruction_tips.setText(R.string.device_live_start);
                mBluetoothAdapter.stopLeScan(mLeScanCallback);   //开始扫描设备    
                stopDeviceLiving();
            }

            break;
        default:
            break;
        }
    }

      
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.blemenu, menu);
        if(mLiveDevice){
            menu.findItem(R.id.menu_stop).setVisible(false);
            menu.findItem(R.id.menu_scan).setVisible(false);
            menu.findItem(R.id.menu_refresh).setActionView(null);
        }else if (!mFindDevice) {
            menu.findItem(R.id.menu_stop).setVisible(false);
            menu.findItem(R.id.menu_scan).setVisible(true);
            menu.findItem(R.id.menu_refresh).setActionView(null);
        } else {
            menu.findItem(R.id.menu_stop).setVisible(true);
            menu.findItem(R.id.menu_scan).setVisible(false);
            menu.findItem(R.id.menu_refresh).setActionView(
                    R.layout.actionbar_indeterminate_progress);
        }
        return true;
    }
             
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_scan:
                findDevice(true);
                break;
            case R.id.menu_stop:
                findDevice(false);
                break;
        }
        return true;
    }
    
    private long exitTime = 0;
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if(keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN){
            if((System.currentTimeMillis()-exitTime) > 2000){
                Toast.makeText(getApplicationContext(), R.string.double_return_exit, Toast.LENGTH_SHORT).show();   
                exitTime = System.currentTimeMillis();
            }else{
                BlePreventLostActivity.this.finish();
            }
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
       
    
    
    //========================Activity life cycle=======================================================
    
      @Override
      protected void onStart(){
          super.onStart();        
          Log.e(TAG,"*************onStart");
        // Ensures Bluetooth is enabled on the device.  If Bluetooth is not currently enabled,
        // fire an intent to display a dialog asking the user to grant permission to enable it.
        if (!mBluetoothAdapter.isEnabled()) {
            if (!mBluetoothAdapter.isEnabled()) {
                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
            }  
        }
      }

      @Override
      protected void onRestart(){
          super.onRestart();

      }
    
    @Override
    protected void onResume() {
        super.onResume();

    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.e(TAG,"onPause");
    }

      @Override
      protected void onStop() {
          super.onStop();
          Log.e(TAG,"*************onStop");
      }

      @Override
      protected void onDestroy(){
          super.onDestroy();
          Log.e(TAG,"*************onDestroy");
        if(deviceLiveThread!=null){
            deviceLiveThread.stopThread();
        }
          mLiveDevice=false;
          findDevice(false);
        sacnedBleDevicesList.clear();
        findedBleDevicesList.clear();
        sacnedBleDevicesData.clear();
        mDeviceListAdapter.notifyDataSetChanged();
      }   
}

 

 

© 著作权归作者所有

共有 人打赏支持
iSnowFlake
粉丝 34
博文 93
码字总数 53328
作品 0
深圳
高级程序员
私信 提问
Android 蓝牙防丢实现(一)

今天看见朋友分享的一个项目:TrackR bravo 一个声称是同类产品中最轻薄小巧的蓝牙物品防丢器,金属材质的外壳,厚度为3.5mm,直径34mm,仅有一元硬币大小。可以系在钥匙串、钱包、手机、平板...

iSnowFlake
2015/10/15
529
0
Android深入浅出系列之Bluetooth—蓝牙操作(一)

  一:什么是蓝牙     1:Bluetooth是目前使用最广泛的无线通讯协议,近距离无线通讯的标准。传说瑞典有个国王特别爱吃蓝莓导致自己的牙齿天天都是蓝色的,在他执政期间这位国王非常善...

芋头被人用掉了
2015/05/19
0
0
微信智能跑鞋技术解密

微信硬件平台官方最近发布了一款接入微信的智能跑鞋,借力微信社交进行品牌营销。相比之前运动手环一般通过微信精简协议来接入微信运动,智能跑鞋是使用微信蓝牙airsync协议实现接入,其支持...

yueqian_scut
2016/07/01
0
0
Android 蓝牙 API 之 BluetoothAdapter 类

使用BluetoothAdapter类,你能够在Android设备上查找周边的蓝牙设备然后配对(绑定),蓝牙通讯是基于唯一地址MAC来相互传输的,考虑到安全问题Bluetooth通讯时需要先配对。然后开始相互连接,...

无鸯
2011/09/06
2.6K
1
Google Play和基于功能的过滤(二)

基于暗示功能的过滤 一个暗示的功能是为了让应用程序正确运行所需的功能,但是,这个功能不在清单的元素中声明。严格的说,应用程序应用始终声明它所使用和需要的所有功能,因此对于应用程序...

长平狐
2012/10/16
202
0

没有更多内容

加载失败,请刷新页面

加载更多

Kafka+Flink 实现准实时异常检测系统

1.背景介绍 异常检测可以定义为“基于行动者(人或机器)的行为是否正常作出决策”,这项技术可以应用于非常多的行业中,比如金融场景中做交易检测、贷款检测;工业场景中做生产线预警;安防...

架构师springboot
12分钟前
1
0
DecimalFormat 类基本使用

/* * DecimalFormat 类主要靠 # 和 0 两种占位符号来指定数字长度 * 0 表示如果位数不足则以 0 填充 * # 表示只要有可能就把数字拉上这个位置 * */ public static void main(String[] args){...

嘴角轻扬30
29分钟前
3
0
This APT has Super Cow Powers.

在Debian/Ubuntu上,apt包管理器内嵌着一个彩蛋. 如果你在命令行界面输入 apt help 在最后一行能找到This APT has Super Cow Powers. 说明该apt具有超级牛力 牛力是个什么梗? 则说明你的系统...

taadis
46分钟前
2
0
起薪2万的爬虫工程师,Python需要学到什么程度才可以就业?

爬虫工程师的的薪资为20K起,当然,因为大数据,薪资也将一路上扬。那么,Python需要学到什么程度呢?今天我们来看看3位前辈的回答。 1、前段时间快要毕业,而我又不想找自己的老本行Java开发...

糖宝lsh
55分钟前
6
0
携手开发者共建云生态 首届腾讯云+社区开发者大会在京举办

本文由云+社区发表 北京时间12月15日,由腾讯云主办,极客邦科技、微信、腾讯TEG协办的首届腾讯云+社区开发者大会在北京朝阳悠唐皇冠假日酒店举办。在会上,腾讯云发布了重磅产品开发者平台以...

腾讯云加社区
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部