zoukankan      html  css  js  c++  java
  • 蓝牙通信第1篇:搜索蓝牙设备

    一:注意事项

          1:android6.0使用蓝牙时,需要开启gps定位权限,不然无法搜索其它蓝牙设备。

    二:权限

          1:权限配置

    <!--允许程序连接到已配对的蓝牙设备-->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <!-- 允许程序发现和配对蓝牙设备 -->
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
        <!--android 6.0 涉及到的权限-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    
    <!-- 在SDCard中创建与删除文件的权限 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <!-- 往SDCard写入数据的权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

        2:动态权限代码

        由于需要用到存储卡,定位等,android6.0以上需要代码动态设置。

       a)获取定位设置

    if (Build.VERSION.SDK_INT >= 23) {
         boolean isLocat = isLocationOpen(getApplicationContext());
         Toast.makeText(mContext, "isLo:" + isLocat, Toast.LENGTH_LONG).show();
                //开启位置服务,支持获取ble蓝牙扫描结果
         if (!isLocat) {
            Intent enableLocate = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            startActivityForResult(enableLocate, 1);
         }
    }
    
     /**
         * 判断位置信息是否开启
         *
         * @param context
         * @return
         */
        private static boolean isLocationOpen(final Context context) {
            LocationManager manager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
            //gps定位
            boolean isGpsProvider = manager.isProviderEnabled(LocationManager.GPS_PROVIDER);
            //网络定位
            boolean isNetWorkProvider = manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
            return isGpsProvider || isNetWorkProvider;
        }

    b)存储卡权限设置

    if (Build.VERSION.SDK_INT >= 23) {
                int write = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
                int read = checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
                //动态请求读写sd卡权限
                if (write != PackageManager.PERMISSION_GRANTED || read != PackageManager.PERMISSION_GRANTED) {
                    requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, SD_CARD);
                }
    }

    然后通过onRequestPermissionsResult()方法获取动态权限的结果:

     @Override
        public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
            switch (requestCode){
                case SD_CARD:
                    if(grantResults.length>0&&grantResults[0] == PackageManager.PERMISSION_GRANTED){
                      //允许访问
    
                    }else{
                        Toast.makeText(mContext,"您拒绝了程序访问存储卡",Toast.LENGTH_LONG).show();
                    }
                    break;
                case COARES_LOCATION:
                    break;
            }
        }

     三:蓝牙搜索

    android.bluetooth.BluetoothAdapter 是蓝牙开发用得比较多,并且比较重要的一个类,可以设备蓝牙名称,打开,关闭,搜索等常规操作。

      1 蓝牙打开,以及搜索

         蓝牙打开和关闭信息使用BluetoothAdapter.ACTION_STATE_CHANGED去接收广播

    BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    mBluetoothAdapter.setName("blueTestPhone"); 
    //判断蓝牙是否打开
    boolean originalBluetooth = (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled());
    if (originalBluetooth) {
    mBluetoothAdapter.startDiscovery();
    } else if (originalBluetooth == false) {
    mBluetoothAdapter.enable();
    }

    蓝牙打开后,我们可以获取设备的蓝牙信息

     StringBuilder sb = new StringBuilder();
     //获取本机蓝牙名称
     String name = mBluetoothAdapter.getName();
    //获取本机蓝牙地址
     String address = mBluetoothAdapter.getAddress();

    搜索完成后,通过BluetoothDevice.ACTION_FOUND广播去接收结果,广播代码如下(注意:可能出现设备搜索不到的情况,设备需要开启允许周围设备搜索,或者通过程序来控制允许搜索的时间范围)

      /*确保蓝牙被发现,在荣耀8手机上,设置了还是默认的2分钟,所以以下几句代码程序中没有,*/
    Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    //设置可见状态的持续时间为300秒,但是最多是300秒
    discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
    startActivityForResult(discoverableIntent, REQUEST_DISCOVERABLE_BLUETOOTH);

    private
    void initSearchBroadcast() { IntentFilter intentFilter = new IntentFilter(); //发现设备 intentFilter.addAction(BluetoothDevice.ACTION_FOUND); //设备配对状态改变 intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); //蓝牙设备状态改变 intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); //开始扫描 intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); //结束扫描 intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); //其它设备请求配对 intentFilter.addAction(ACTION_PAIRING_REQUEST); //intentFilter.addAction(BluetoothAdapter.CONNECTION_STATE_CHANGED); registerReceiver(bluetoothReceiver, intentFilter); } private BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Logger.e(TAG + "mBluetoothReceiver action =" + action); try { if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {//开始扫描 setProgressBarIndeterminateVisibility(true); log1.setText("正在扫描设备,请稍候..."); } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {//结束扫描 Logger.e(TAG + "设备搜索完毕"); setProgressBarIndeterminateVisibility(false); log1.setText("扫描完成"); bondAdapter.notifyDataSetChanged(); unbondAdapter.notifyDataSetChanged(); scanStatus = false; } else if (BluetoothDevice.ACTION_FOUND.equals(action)) {//发现设备 findDevice(intent); } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {//蓝牙配对状态的广播 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); Logger.e(TAG + device.getName() + "蓝牙配对广播:" + device.getBondState()); switch (device.getBondState()) { case BluetoothDevice.BOND_BONDING: Logger.e(TAG + device.getName() + "蓝牙配对广播 正在配对......"); break; case BluetoothDevice.BOND_BONDED: Logger.e(TAG + device.getName() + "蓝牙配对广播 完成配对,本机自动配对"); bondDevices.add(device); unbondDevices.remove(device); bondAdapter.notifyDataSetChanged(); unbondAdapter.notifyDataSetChanged(); break; case BluetoothDevice.BOND_NONE: Logger.e(TAG + device.getName() + "蓝牙配对广播 取消配对"); unbondDevices.add(device); bondDevices.remove(device); unbondAdapter.notifyDataSetChanged(); bondAdapter.notifyDataSetChanged(); default: break; } } else if (action.equals(ACTION_PAIRING_REQUEST)) {//其它设备蓝牙配对请求 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); //当前的配对的状态 try { String path = Environment.getExternalStorageDirectory() + "/blueTest/"; String deviceName = btDevice.getName(); Logger.e(TAG + "蓝牙 匹配信息:" + deviceName + "," + btDevice.getAddress() + ",state:" + state); //1.确认配对,高版本无效,蓝牙配对不是zuk的问题,而是安卓6.0的bug,凡是遇到蓝牙适配问题的,请同时打开蓝牙和定位,再去配对,基本90%都没有问题了。 Object object = ClsUtils.setPairingConfirmation(btDevice.getClass(), btDevice, true); //2.终止有序广播,如果没有将广播终止,则会出现一个一闪而过的配对框。 abortBroadcast(); //3.调用setPin方法进行配对... boolean ret = ClsUtils.setPin(btDevice.getClass(), btDevice, PWD); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); Toast.makeText(mContenxt, "error:" + btDevice + "," + state, Toast.LENGTH_LONG).show(); } } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {//蓝牙开关状态 // BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int statue = mBluetoothAdapter.getState(); switch (statue) { case BluetoothAdapter.STATE_OFF: Logger.e("蓝牙状态:,蓝牙关闭"); ClsUtils.closeDiscoverableTimeout(mBluetoothAdapter); break; case BluetoothAdapter.STATE_ON: Logger.e("蓝牙状态:,蓝牙打开"); ClsUtils.setDiscoverableTimeout(1000 * 60, mBluetoothAdapter); scanBluetooth(); break; case BluetoothAdapter.STATE_TURNING_OFF: Logger.e("蓝牙状态:,蓝牙正在关闭"); mBluetoothAdapter.cancelDiscovery(); break; case BluetoothAdapter.STATE_TURNING_ON: Logger.e("蓝牙状态:,蓝牙正在打开"); break; } } } catch (Exception e) { e.printStackTrace(); } } };

    //发现设备的代码如下
     private void findDevice(Intent intent) throws Exception{
    //获取到设备对象
    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    String str = device.getName() + "|" + device.getAddress();
    Logger.e("扫描到设备:" + str);
    if (device.getBondState() == BluetoothDevice.BOND_BONDED) {//判断当前设备地址下的device是否已经配对
    if (!bondDevices.contains(device)) {
    bondDevices.add(device);
    }
    } else {
    if (!unbondDevices.contains(device)) {
    unbondDevices.add(device);
    }
    if (device.getName().equals(TEST_DEVICE_NAME)) {
    boolean bondStatus = ClsUtils.createBond(device.getClass(), device);
    Logger.i(TAG + " bondStatus:" + bondStatus);
    }
    }
    Log.e("error", "搜索完毕,准备刷新!");
    bondAdapter.notifyDataSetChanged();
    unbondAdapter.notifyDataSetChanged();
    }


       

    四:蓝牙配对

       正常情况下,蓝牙匹配需要弹出一个匹配确认框,如下图,但我想实现的是,匹配其中一方,不能手动点击配对,因为发起蓝牙连接的设备是android设备,是不能触摸的,所以就要通过程序来解决这个问题,特别声明:(测试的android设备,版本为5.x,并且已经root,没有root的设备,或者不是android5.x不清楚能否实现自动匹配,因为我只有这个测试设备)。

    1 当我们搜索到目标手机的蓝牙后,android设备主动发起连接请求,代码如下

     if (device.getName().equals(TEST_DEVICE_NAME)) {
                    boolean bondStatus = ClsUtils.createBond(device.getClass(), device);
                    Logger.i(TAG + " bondStatus:" + bondStatus);
     }
    //发起蓝牙匹配请求 
    public boolean createBond(Class btClass, BluetoothDevice btDevice)
                throws Exception {
            Method createBondMethod = btClass.getMethod("createBond");
            Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
            return returnValue.booleanValue();
    }

    2 当被匹配方点击配对后,系统会通过BluetoothDevice.ACTION_BOND_STATE_CHANGED广播告诉android设备,此时android设备就可以自动确认,通过这个流程来完成整个蓝牙的配对,具体代码如下

     BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                        int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); //当前的配对的状态
                        try {
                            String path = Environment.getExternalStorageDirectory() + "/blueTest/";
                            String deviceName = btDevice.getName();
                            Logger.e(TAG + "蓝牙 匹配信息:" + deviceName + "," + btDevice.getAddress() + ",state:" + state); 
                            if(deviceName.equals(TEST_DEVICE_NAME)){//TEST_DEVICE_NAME  为被匹配蓝牙设备的名称,自己手动定义
                                Object object = ClsUtils.setPairingConfirmation(btDevice.getClass(), btDevice, true);
                                abortBroadcast();
                                boolean ret = ClsUtils.setPin(btDevice.getClass(), btDevice, PWD);
                            }
                        } catch (Exception e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                            Toast.makeText(mContenxt, "error:" + btDevice + "," + state, Toast.LENGTH_LONG).show();
                        }
    
    //确认配对
    public Object setPairingConfirmation(Class<?> btClass, BluetoothDevice device, boolean isConfirm) throws Exception {
            Method setPairingConfirmation = btClass.getDeclaredMethod("setPairingConfirmation", boolean.class);
           Object object =  setPairingConfirmation.invoke(device, isConfirm);
             return object;
        }
    
    //配对需要调用的方法
    public boolean setPin(Class<? extends BluetoothDevice> btClass, BluetoothDevice btDevice,
                                     String str) throws Exception {
            try {
                Method removeBondMethod = btClass.getDeclaredMethod("setPin",
                        new Class[]
                                {byte[].class});
                Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice,
                        new Object[]
                                {str.getBytes()});
                Log.e("returnValue", "" + returnValue);
            } catch (SecurityException e) {
                // throw new RuntimeException(e.getMessage());
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                // throw new RuntimeException(e.getMessage());
                e.printStackTrace();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return true;
    
        }                        

     到目前为止,蓝牙权限,以及动态权限,蓝牙的打开,关闭,搜索,以及自动配对(特别声明:自动配对的android设备,版本为5.x,并且已经root,没有root的设备,或者不是android5.x不清楚能否实现自动匹配,因为我只有这个测试设备)。)代码至此结束

    一章:蓝牙通信第2篇:建立通信和发送文字消息,文件消息
     
    demo代码下载:github




  • 相关阅读:
    Visual Studio for Mac 2017 初体验
    利用栈求表达式的值
    Problem C: ChongQueue
    Problem G: ZL's Prob.2
    Problem F: ZL's Prob.1
    Problem B: ChongBit
    HDU 5156
    SHUOJ 1771
    OpenGL学习整理------着色器
    OpenGL ES着色器语言----------------储存修饰符
  • 原文地址:https://www.cnblogs.com/cq-jiang/p/7606720.html
Copyright © 2011-2022 走看看