MyException - 我的异常网
当前位置:我的异常网» Android » 蓝牙通信第1篇:搜寻蓝牙设备

蓝牙通信第1篇:搜寻蓝牙设备

www.MyException.Cn  网友分享于:2013-10-08  浏览:0次
蓝牙通信第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




文章评论

科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
 程序员的样子
程序员的样子
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
程序员必看的十大电影
程序员必看的十大电影
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
编程语言是女人
编程语言是女人
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
中美印日四国程序员比较
中美印日四国程序员比较
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
为什么程序员都是夜猫子
为什么程序员都是夜猫子
如何成为一名黑客
如何成为一名黑客
那些争议最大的编程观点
那些争议最大的编程观点
程序员和编码员之间的区别
程序员和编码员之间的区别
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
代码女神横空出世
代码女神横空出世
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
鲜为人知的编程真相
鲜为人知的编程真相
10个调试和排错的小建议
10个调试和排错的小建议
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
一个程序员的时间管理
一个程序员的时间管理
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
旅行,写作,编程
旅行,写作,编程
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
每天工作4小时的程序员
每天工作4小时的程序员
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
我是如何打败拖延症的
我是如何打败拖延症的
程序员都该阅读的书
程序员都该阅读的书
程序员的鄙视链
程序员的鄙视链
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
我的丈夫是个程序员
我的丈夫是个程序员
Java程序员必看电影
Java程序员必看电影
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有