2017-06-23 15:52:28 geanwen 阅读数 8652
  • 探秘龙板的GPS定位、蓝牙和Wi-Fi连接

    无论是各大IT巨头都看好的智能家居,还是极客们开发的无人机,都离不开GPS定位、蓝牙和Wi-Fi链接等功能实现。如何使用DragonBoard410c开发板,如何完全其中的GPS、蓝牙、Wi-Fi等模块,如何开启龙板蕴藏的强大能力?

    10143 人正在学习 去看看 CSDN讲师

项目的一个功能,蓝牙多连接。应用同时连接多个设备,等待接收数据,处理后显示图表。

在这之前我没有搞过蓝牙方面的东西,只能从基础开始边看边干。


最开始不知道,看的是传统蓝牙的连接与传输,几天过后,发现与低功耗蓝牙不一样啊,又针对低功耗蓝牙开始找资料。


低功耗蓝牙支持的api最低是18。

基本思路:低功耗蓝牙连接分两种,一种是作为周边设备,一种是作为中心设备。因为需求是多连接,那我们就按照创建一个中心设备的做法来处理。


下面先记录一下,一个低功耗蓝牙的连接:

注:因为程序与硬件是一套设备,所以我不需要扫瞄附近的设备,然后选择连接,直接拿地址进行连接。


蓝牙需要的权限:

 <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />


如果你想你的应用只支持低功耗蓝牙的话,在加一个权限:

 <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>


接下来在activity中,不管周边设备还是中心设备,我们都需要拿到:BluetoothManager和BluetoohAdapter:

//获取BluetoothManager
        BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        //获取BluetoothAdapter
        BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();

        //如果蓝牙没有打开 打开蓝牙
        if (!mBluetoothAdapter.isEnabled()) {
            mBluetoothAdapter.enable();
        }


然后就是通过上面拿到的adapter得到BluetoothDevice对象:

BluetoothDevice bluetoothDeviceOne = mBluetoothAdapter.getRemoteDevice("D8:B0:4C:BC:C0:83");

注:1.传入的参数就是你蓝牙的地址。

2.假如你需要多连接,那么这里就要获得多个BluetoothDevice对象,通过不同的地址。


然后就是BluetoothGatt对象:

private BluetoothGatt mBluetoothGattOne;

在拿到上面两个对象后,先校验gatt对象是否在运行:

//如果Gatt在运行,将其关闭
        if (mBluetoothGattOne != null) {
            mBluetoothGattOne.close();
            mBluetoothGattOne = null;
        }

然后就是连接设备,并设置连接的回调:

//连接蓝牙设备并获取Gatt对象
        mBluetoothGattOne = bluetoothDeviceOne.connectGatt(MainActivity.this, true, bluetoothGattCallbackOne);

其中,bluetoothGattCallbackOne就是回调:

/**
     * 第一个设备  蓝牙返回数据函数
     */
    private BluetoothGattCallback bluetoothGattCallbackOne = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    setText("设备一连接成功");

                    //搜索Service
                    gatt.discoverServices();
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    setText("设备一连接断开");
                }
            }
            super.onConnectionStateChange(gatt, status, newState);
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            //根据UUID获取Service中的Characteristic,并传入Gatt中
            BluetoothGattService bluetoothGattService = gatt.getService(UUID_SERVICE);
            BluetoothGattCharacteristic bluetoothGattCharacteristic = bluetoothGattService.getCharacteristic(UUID_NOTIFY);

            boolean isConnect = gatt.setCharacteristicNotification(bluetoothGattCharacteristic, true);
            if (isConnect){

            }else {
                Log.i("geanwen", "onServicesDiscovered: 设备一连接notify失败");
            }
            super.onServicesDiscovered(gatt, status);
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {//数据改变
            super.onCharacteristicChanged(gatt, characteristic);
            String data = new String(characteristic.getValue());
            Log.i("geanwen", "onCharacteristicChanged: " + data);
        }
    };

注:1.我的需求就是接收是蓝牙发送过来的数据,假如你需要写入数据,或者其他的功能,在上面的callback中,可以复写对应的方法。

2.上面onConectionStateChange方法就是连接状态改变时回调的方法,在其中判断连接是否成功。

3.上面onServicesDiscovered方法就是在onConectionStateChange连接成功之后,发现该蓝牙的服务。

在这里你可以设置你需要的服务,每个蓝牙连接中会有不同的服务,设置你需要的即可。

4.上面我设置了接收数据的服务,其中传入的几个常量就是我们低功耗蓝牙的服务的固定码,下面贴个例子:

//蓝牙设备的Service的UUID
    public final static UUID UUID_SERVICE = UUID.fromString("0003cdd0-0000-1000-8000-00805f9b0131");

    //蓝牙设备的notify的UUID
    public final static UUID UUID_NOTIFY = UUID.fromString("0003cdd1-0000-1000-8000-00805f9b0131");

大家仔细看好,两个码是不一样的,一开始我以为是一样的,结果调了好久。

因为我这个需求是不能先搜索,在连接,然后选择对应服务的,所以我将这些这些操作都省略,即我提前拿到了这几个服务的码。

5.上面回调方法onCharacteristicChanged方法就是来数据了,接收的方法。

连接一个低功耗蓝牙并接收数据上面就介绍完了,下面说下连接两个或者多个。


到这里是后期来修改的,原来简单的通过上面的连接一个,多次创建对应的对象来实现多连接。

那样第一个代码量有点大,重复工作做的多。性能还不是很好。


下面我在github找到一个框架,大神封装的模块,clone到本地看他的代码,学习一番,

下面贴出地址:

点击打开链接


具体使用,他的文档中记录的很详细了

学习思路之后自己也封装一下。


实现多连接过程中遇到了几个问题,这里记录下。

当你的业务需求需要连接多个设备的时候,

需要考验你的手机/平板的设备了,(价钱高一些性能好一些)

我试用的华为6.0的平板,同时连接三个是相对稳定一些,一次搜索并连接三个,第四个设备需要重新搜索才能成功。

小米的7.0系统的平板,连接四个很快的。


部分手机/平板是国内厂商定制的,不能搜索到蓝牙设备或者不能连接设备,可能的原因是硬件设备没有对这些厂商进行兼容。






2012-06-27 16:58:45 lgb861127 阅读数 893
  • 探秘龙板的GPS定位、蓝牙和Wi-Fi连接

    无论是各大IT巨头都看好的智能家居,还是极客们开发的无人机,都离不开GPS定位、蓝牙和Wi-Fi链接等功能实现。如何使用DragonBoard410c开发板,如何完全其中的GPS、蓝牙、Wi-Fi等模块,如何开启龙板蕴藏的强大能力?

    10143 人正在学习 去看看 CSDN讲师
通讯流程:设备有测量数据后,通知打开设备上的蓝牙模块,蓝牙开始广播(在没有连接的情况下,连续广播一分钟),此时手机蓝牙可以搜索到设备,之后进行配对连接,然后根据通讯协议来通讯;当产品测量完数据后通知手机与其断开连接。一次通讯完毕! 如果设备一直没有连接到手机蓝牙,设备的测量数据一直存储在设备里,待下一次与手机蓝牙通讯时,一起发送。
2017-09-20 09:56:07 wdmxzf 阅读数 6870
  • 探秘龙板的GPS定位、蓝牙和Wi-Fi连接

    无论是各大IT巨头都看好的智能家居,还是极客们开发的无人机,都离不开GPS定位、蓝牙和Wi-Fi链接等功能实现。如何使用DragonBoard410c开发板,如何完全其中的GPS、蓝牙、Wi-Fi等模块,如何开启龙板蕴藏的强大能力?

    10143 人正在学习 去看看 CSDN讲师

前言

经过一个多月的时间蓝牙多设备连接的重构终于告一段落了,这次的重构不止是代码方面的完善,还结合了一些用户的使用场景,另外增加一些离线操作,使手机端对蓝牙的操作更加的便捷,对蓝牙设备的管理更加统一。

场景分析

支持的场景

  • 多设备连接(一个血压计和一个厨房秤)—— 在饭点,使用同一个手机连接血压计和厨房秤,病人在测量血压,家人在做饭称量菜品。
  • 把连接到的设备信息保存到本地——方便下次自动连接。
  • 把蓝牙返回结果保存本地——方便在其他页面获取数据显示UI。
  • 同种设备只能连接一个——保持连接设备的唯一性,防止找不到连接的设备。
  • 可以在蓝牙列表扫描到所支持的设备,并连接、断开和删除蓝牙——对蓝牙设备进行管理,查看自己扫描到的设备,并查看蓝牙的连接状态。
  • 启动 App 可以自动连接上一次已经连接过的设备——可以快速的使用设备。

暂不支持的场景

  • 同时扫描多个同种设备——可能导致连接到了设备,而不知道连接的是哪个设备。
  • 新旧设备替换——不能直接进行替换,需要在设备列表页面删除旧设备,之后关掉旧设备,重新扫描新设备。

场景实现

效果展示

蓝牙多设备连接效果图.gif

多设备连接

现在一台手机可以连接多个设备,例如连接蓝牙耳机,智能手环等。既然手机可以连接多个设备,那么移动应用也是可以连接多个设备的(血压计、心率计等),下面就是移动应用 App 实现多设备连接的思路方法。

实现思路

关于蓝牙连接,主要是 BluetoothGatt 这个类型,每个蓝牙的连接都需要用独立且唯一的 BluetoothGatt 。开始的想法是每个蓝牙都重新创建一个 Service, 在新的 Service 内使用 BluetoothGatt 进行连接,然而这个方法是可以实现多设备连接,但是创建多个 Service 对手机消耗比较大。之后,想到把 BluetoothGatt 保存起来不就可以了么,那用什么保存呢,既可以临时保存多个,又可以按照需要获取相对应的 BluetoothGatt 。在 java 里面有个类型 Map(String, Object) ,它是以 key-value 的形式存储到 Map 中。可以根据当时的 Key 来取相应的 Value 值,而且在关掉进程时相应的变量也就释放了。

代码实现

    private Map<String, BluetoothGatt> mBluetoothGattMap = new HashMap<>(); //临时保存 BluetoothGatt
    private Map<String, BluetoothGattCharacteristic> mGattCharacteristicMap = new HashMap<>();// 临时保存蓝牙的特征值 Characteristic
    private Map<String, BluetoothInfo> mBluetoothInfoMap = new HashMap<>();// 临时保存自己设置的蓝牙信息(deviceName、deviceType、startCMD、stopCMD 等)
    private Map<BluetoothGatt, String> mDeviceTypeMap = new HashMap<>();// 临时存储 deviceType 
    private Map<String, GGBLEDeviceEntity> mConnectModelMap = new HashMap<>();// 临时存储 已连接的设备
//...
/**
     * 连接设备
     *
     * @param deviceType 设备类型
     * @return true 连接成功,false连接失败
     */
    boolean connectBluetooth(Context context, String deviceType, String deviceAddress) {
       //...
        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(deviceAddress);//根据 mac 地址获取蓝牙设备
      //...
        BluetoothGatt bluetoothGatt = device.connectGatt(this, false, mGattCallback);// 对蓝牙进行连接操作
      //...
        mBluetoothGattMap.put(deviceType, bluetoothGatt);//把 BluetoothGatt 已 key-value 的形式临时保存起来

        return true;

    }

设备自动连接

要有良好的用户操作体验,我们应该避免对一些无关的操作重复进行。例如我们第一次打开应用连接了蓝牙设备,以后再打开 App 不需要重复操作连接过程,用户就可以少打开一个页面,少点击两次按钮。减少用户重复操作,让用户直接进入正题,提高主功能的使用率。

实现思路

在 Android 中连接蓝牙的方法是

public BluetoothGatt connectGatt(Context context, boolean autoConnect,
                                     BluetoothGattCallback callback) {
        return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO));
    }

其中 BluetoothGatt 是每个连接成功的蓝牙返回唯一的属性。当蓝牙设备连接成功后会返回唯一的 BluetoothGatt ,并用它进行对蓝牙的命令操作;autoConnect 是设置是否为自动连接的一个属性,然而根据我自己的测试,当 autoConnect 的属性设置为 true 时,是有可能自动连接的,但是有时也会失效,所以不采用;BluetoothGattCallback 是蓝牙连接、命令操作,数据返回等成功时的回调。
因为 autoConnect 的设置具有不确定性,所以我们采取另一种方式:当我们第一次连接蓝牙成功的时候,把蓝牙的 Mac 地址存储起来;在第二次启动 App 的时候,先把蓝牙的 Mac 地址从 SharedPreferences 中取出来,用 Mac 地址进行连接,如果连接失败(可能结果是设备不对或者设备没有打开),我们就开启蓝牙扫描功能,进行重新扫描设备,打开设备进行连接。

代码实现

/**
  *保存 mac 地址到 SharedPreferences
  */
public void saveMac(Context context, String macAddress){
if (null != context) {
            SharedPreferences sharedPreferences = context.getSharedPreferences(BLUETOOTH_MAC_TABLE, Context.MODE_PRIVATE);
            SharedPreferences.Editor editor = sharedPreferences.edit();
            editor.putString(deviceType, bluetoothAddress);
            editor.apply();
            GGLog.i(TAG, "save success");
        }
}

void autoConnect(Context context, final String deviceType) {
        /*防止蓝牙 adapter 为空,程序崩溃*/
        if (null == mBluetoothAdapter) {
            GGLog.e(TAG, "method discoveryBluetooth \n mBluetoothAdapter is null");
            return;
        }
        //根据 deviceType 获取 蓝牙 mac 地址
        SharedPreferences sharedPreferences = context.getSharedPreferences(BLUETOOTH_MAC_TABLE, Context.MODE_PRIVATE);
        String deviceAddress = sharedPreferences.getString(deviceType, "");
        if (!"".equals(deviceAddress)) {
            if (!deviceAddressList.contains(deviceAddress)) {
                deviceAddressList.add(deviceAddress);
            }
            connectBluetooth(context, deviceType, deviceAddress);// 连接设备
        }
    }

void connectBluetooth(Context context, String deviceType, String deviceAddress){
//...
// 连接失败 开启扫描功能
if (!bluetoothGatt.connect() || bluetoothGatt.getServices().size() == 0) {
            startScan();
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    stopScan();//十秒后停止扫描
                }
            }, 10000);
            return false;
        }
}

不同页面共享结果数据

在移动端我们很多时候,同一份数据要在多个页面上显示,又不想多次调用接口,我们只好把数据保存到本地,来达到数据共享的目的。

实现思路

在我这蓝牙的开发中,是使用 SQL 对数据进行保存。蓝牙测量中的数据因为是实时更新,我们不需要进行缓存,只要把测量的数据结果和蓝牙的信息存储下来就可以了。
使用 SQL 有一点不好的是使用 ContentValues 以 key-value 的形式进行存储,那么带来一个问题就是 key 我们手写很容易写错,所以可能一不小心就会万劫不复。在 Git 上有个轻量级的数据库第三方 Litepal (https://github.com/LitePalFramework/LitePal) ,它操作简单,不需要手写 key ,对一些常用的增删改查都进行了封装,而且还支持手写 SQL 语句,在 CSDN 上博客专家郭霖有详细的介绍 Litepal 的说明和使用方法。在这里我使用的是原生的 SQL ,防止与库外的第三方冲突。
蓝牙设备信息的保存,我存储了以下信息:

存储字段 类型 功能说明
deviceType String 设备类型,更具设备类型进行蓝牙操作
deviceAddress String 蓝牙的 mac 地址,用来连接蓝牙
deviceName String 蓝牙名称,扫描蓝牙时,进行校验
connectStatus int 蓝牙连接状态

对测量结果的存储内容是:

存储字段 类型 功能说明
deviceType String 设备类型,更具设备类型进行蓝牙操作
measuredData String 测量数据,把测量完成的数据进行保存,又 int[] 转成 String 进行保存
measuredTime long 测量时间
isUpdate boolean 上传状态,判断是否上传服务区,如果没有,则在有网络的情况下自动上传

代码实现

/**
     * 插入一条测量结果。因为数据库是封装在蓝牙库里面,我们获取不到 Application的 Context 所以传递一个 context
     */
    void insertComplete(Context context, CompleteModel entity) {
        SQLiteTemplate sqLiteTemplate = SQLiteTemplate.getInstance(context, instance.mBLEDBManager, false);
        ContentValues values = new ContentValues();
        values.put("deviceType", entity.getDeviceType()); //保存蓝牙设备类型
        values.put("measuredData", Arrays.toString(entity.getMeasuredData()));//保存测量结果
        values.put("measuredTime", entity.getMeasuredTime());// 保存测量时间
        values.put("isUpdate", entity.isUpdate());//保存是否已经上传完毕
        sqLiteTemplate.insert("ble_complete_table", values);// 数据插入表中
    }

这些都是移动端对数据库的简单操作,剩下的 更新、删除、查找的方法就不都一一列举了。对于蓝牙设备信息的存储与保存测量结果相似,也不列举了。

蓝牙搜索、连接、删除

既然我们有单独蓝牙列表页面,那么就要有对蓝牙的一些基本的操作。在列表页面我们可以对蓝牙进行搜索,发现周围打开的蓝牙设备;点击链接,连接我们需要使用的血压计、厨房秤等;长安断开链接并删除相应的蓝牙设备,当我们设备不再使用或者更换新设备的时候,我们可以删除多余的设备,使页面看起来更加简洁。

实现思路

首先,我们在BluetoothService里面对扫描到的蓝牙进行区分,检查是否是我们设定的的蓝牙设备(extends BaseBluetoothAdapter的类),根据设定的蓝牙名称(deviceName)筛选扫描到的设备,之后把设备通过 Listener 监听传递到 Activity 中,并添加到列表里显示出来。

代码实现

以下是 BluetoothService 中蓝牙扫描结果的处理

private void scanResult(BluetoothDevice device, int type) {
        if (null != device) {
            String name = device.getName();//获取扫描到的设备名称
            if (null != name) {
                //获取我们自己设定的蓝牙详情(deviceType、deviceName等)
                for (BluetoothInfo bluetoothInterface : mInterfaceList) {
                    String deviceName = bluetoothInterface.getDeviceName();
                    //判断设备名称是否与我们自己设定的名称相同
                    if (!deviceName.equals(name)) {
                        continue;
                    }
                    //判断设备是否已经添加到列表中
                    if (deviceNameList.contains(deviceName)) {
                        continue;
                    }
                    // 把设备名称添加到列表中
                    if (!deviceNameList.contains(deviceName)) {
                        deviceNameList.add(deviceName);
                    }
                    //如果扫描到的设备是上次连接过的设备,则自动连接。
                    if (null != deviceAddressList && deviceAddressList.size() > 0) {
                        for (String address : deviceAddressList) {
                            if (address.equals(device.getAddress())) {
                                connectBluetooth(null, bluetoothInterface.getDeviceType(), address);
                            }
                        }
                    }
                    GGBLEDeviceEntity entity = BLEDeviceToGGBLEEntity(bluetoothInterface.getDeviceType(), device);//把bluetoothDevice 转换成我们自定义的BLEEntity。
                    if (null != mResultListener) {
                        mResultListener.onScanResult(entity);//扫描结果监听赋值
                    } else {
                        setError(bluetoothInterface.getDeviceType(), HHCBluetoothProfile.ERROR_NULL, "mResultListener is null");
                    }
                }
            }
        }
    }

其他

  • 在多设备连接中,需要时刻获取到连接的设备是什么,才能获取到相应的数据。通过 onBluetoothConnectStatus 接口方法只能在连接状态发生改变的时候才会回调,不能在已进入页面的时候就获取到设备的连接状态,因此我们在 BluetoothService 中提供两个方法:
//根据设备类型获取连接的蓝牙设备
GGBLEDeviceEntity getConnectedDevice(String deviceType) {
        return mConnectModelMap.get(deviceType);
    }
//获取所有的连接设备
List<GGBLEDeviceEntity> getAllConnectedDevice() {
        List<GGBLEDeviceEntity> connectModelList = new ArrayList<>();
        for (String type : mConnectModelMap.keySet()) {
            connectModelList.add(mConnectModelMap.get(type));
        }
        return connectModelList;
    }
  • 在 service 中不能持久化 Context ,如果定义了 Context 会发生内存溢出,所以在我们所提供的方法中都会传入 context 用于承接上下文。
  • 我把 service 内对外开放的方法都集中在一个类里,方便调用和管理。
  • 蓝牙扫描的方法有三种:
    1、在5.0之前使用的是:mBluetoothAdapter.startLeScan(mLeScanCallback);;
    对应的停止扫描为:mBluetoothAdapter.stopLeScan(mLeScanCallback);
    2、在 Android 5.0之后 使用的是:mBluetoothAdapter.getBluetoothLeScanner().startScan(mScanCallback);;
    对应的停止扫描为:mBluetoothAdapter.getBluetoothLeScanner().stopScan(mScanCallback);
    3、一种通用的扫描方法:mBluetoothAdapter.startDiscovery();
    对应的停止扫描为:mBluetoothAdapter.cancelDiscovery();

总结

  • 在这次的开发中,主要面临的问题是场景方面的考虑,开始只是单一的想要进行多个设备的连接,没有考虑现实中的场景,做出的第一个版本,只能在同一个 Activity 中进行多设备连接,而不能各自在不同的页面进行操作,数据也不能共享;之后和 leader 多次讨论,想到很多场景,结合这些场景,罗列出开发功能,并调整代码结构,让使用者使用起来更加方便。总之,一切的功能开发都是需要场景来支撑。
  • 在数据存储这方面还有待改进,手写的 key-value 形式很容易出现书写错误,即使写错也不容易找到错误出处。改进方向是数据存储直接存储对象而不是一个一个的 key-value 进行存储。

* demo 下载地址:https://github.com/wdmxzf/BluetoothDemo

2017-01-12 18:15:20 ami_daqi 阅读数 544
  • 探秘龙板的GPS定位、蓝牙和Wi-Fi连接

    无论是各大IT巨头都看好的智能家居,还是极客们开发的无人机,都离不开GPS定位、蓝牙和Wi-Fi链接等功能实现。如何使用DragonBoard410c开发板,如何完全其中的GPS、蓝牙、Wi-Fi等模块,如何开启龙板蕴藏的强大能力?

    10143 人正在学习 去看看 CSDN讲师

蓝牙(BlueTooth)是一种无线技术标准,可实现固定设备,移动设备和楼宇个人域网之间的短距离数据通信,蓝牙设备最多可以同时和7个其他蓝牙设备进行连接,进行通信
蓝牙的功能:
1.扫描其他蓝牙设备
2.为可配对的蓝牙设备查询蓝牙适配器
3.建立RFCOMM通道
4.通过服务搜索来连接其他设备
5.与其他设备进行数据传输
6.管理多个连接
蓝牙建立连接必须要求:
1.打开蓝牙
2.查找附近已配对或可用设备
3.连接设备
4.设备间的数据交换
常用蓝牙API:
1.BluetoothAdapter:代表本地蓝牙适配器(蓝牙无线电),BluetoothAdapter是所有蓝牙交互的入口,使用这个你可以发现其他蓝牙设备,查询已配对的设备列表,使用一个已知的MAC地址来实例化一个BluetoothDevice,以及创建一个BluetoothServerSocekt来监听与其他设备的通信
2.BlueDevice代表一个远程蓝牙设备,使用这个来请求一个与远程设备的BluetoothSocket连接,或者查询关于设备名称,地址,类和连接状态等设备信息
3.BluetoothSocket代表一个蓝牙socket的接口,这是一个连接点,他允许一个应用与其他蓝牙设备通过InputStream和OutuptStream交换数据
4.BluetoothServerSocket代表一个开放的服务器Socket,它监听接受的请求,为了连接两台Android设备,一个设备必须使用这个类开启一个服务器Socket.当一个远程蓝牙设备开始一个和该设备的连接请求,BluetoothServerSocket将会返回一个已连接的BluetoothSocket,接受该连接

使用蓝牙需要配置权限:

<uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

第一个权限是在得到默认蓝牙适配器时需要,即BluetoothAdapter
第二个权限是在蓝牙适配器bluetoothAdapter.enable()或者bluetoothAdapter.disable()时需要使用到

蓝牙搜素功能的实现:

2016-01-21 12:49:54 zhangphil 阅读数 12437
  • 探秘龙板的GPS定位、蓝牙和Wi-Fi连接

    无论是各大IT巨头都看好的智能家居,还是极客们开发的无人机,都离不开GPS定位、蓝牙和Wi-Fi链接等功能实现。如何使用DragonBoard410c开发板,如何完全其中的GPS、蓝牙、Wi-Fi等模块,如何开启龙板蕴藏的强大能力?

    10143 人正在学习 去看看 CSDN讲师


Android Bluetooth蓝牙开发:Bluetooth蓝牙设备之间的连接建立(3)

Android Bluetooth蓝牙设备的连接编程模型和Java socket网络连接编程模型类型。Android不同设备间的蓝牙连接,首先在编程模型上分为“服务器端(server)”和“客户端(client)”。

一,Android Bluetooth蓝牙设备的“服务器端”。
在蓝牙的服务端,类似Java网络编程中的ServerSocket,Android的蓝牙服务端叫做BluetoothServerSocket,蓝牙的BluetoothServerSocket作为服务器端,在一个线程中,绑定一个熟知的UUID(注意,此UUID是类似Java编程中的熟知端口号:80),然后等待客户端的连接请求(listenUsingRfcommWithServiceRecord),当BluetoothServerSocket接受客户端的连接请求后(accept),意味不同蓝牙设备间的数据传输连接建立,此时返回一个类似Java 网络编程的套接字,Android Bluetooth蓝牙的套接字:BluetoothSocket。
综上,Android Bluetooth蓝牙的服务器连接阶段的编程,大体上可以简化为三个步骤:
(a)Android Bluetooth蓝牙的BluetoothServerSocket首先需要监听:listenUsingRfcommWithServiceRecord 。注意,简单期间,可以绑定熟知的蓝牙特殊UUID:00001101-0000-1000-8000-00805F9B34FB
(b)然后就是静静的accept等待传入的蓝牙客户端连接请求。由于是作为服务器端,极可能要接收若干客户端连接,所以一般在while循环里面accept。蓝牙accept类似Java的accept。
(c)accept阻塞的等待传入的客户端连接请求,直到一个蓝牙连接请求传入,然后返回一个BluetoothSocket。此BluetoothSocket类似Java的socket。
(a)(b)(c)三阶段的伪代码核心摘要:

BluetoothServerSocket serverSocket;

serverSocket=mBluetoothAdapter.listenUsingRfcommWithServiceRecord(tag, UUID);

   while (true) {
     BluetoothSocket socket = serverSocket.accept();
     if (socket.isConnected()) {
      Log.d(tag, "已建立与客户连接.");
     }
   }
  

由于服务器端代码是在阻塞性的等待、接受客户端连接,所以需要把服务接收客户连接的代码放入单独的线程中处理。

完整的服务器端代码:

package zhangphil.bluetooth;

import java.util.UUID;

import android.app.ListActivity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends ListActivity {

	private BluetoothAdapter mBluetoothAdapter;

	private final String tag = "zhangphil";
	private final String MY_UUID = "00001101-0000-1000-8000-00805F9B34FB";

	private class ServerThread extends Thread {

		private BluetoothServerSocket serverSocket;

		@Override
		public void run() {

			try {
				serverSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(tag, UUID.fromString(MY_UUID));
			} catch (Exception e) {
				e.printStackTrace();
			}

			Log.d(tag, "等待客户连接...");
			while (true) {
				try {
					BluetoothSocket socket = serverSocket.accept();
					
					BluetoothDevice device = socket.getRemoteDevice();
					Log.d(tag, "接受客户连接 , 远端设备名字:" + device.getName() + " , 远端设备地址:" + device.getAddress());

					if (socket.isConnected()) {
						Log.d(tag, "已建立与客户连接.");
					}

				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
		if (mBluetoothAdapter != null)
			new Thread(new ServerThread()).start();
	}
}


二,Android Bluetooth蓝牙的“客户端”。

Android Bluetooth蓝牙“客户端”发起对服务器端的连接,首先要获得服务器蓝牙设备BluetoothDevice,该BluetoothDevice代表着服务器端的蓝牙设备,所以客户端可以先启动蓝牙扫描,扫描获得目标蓝牙设备BluetoothDevice,然后通过BluetoothDevice的createRfcommSocketToServiceRecord( UUID )返回BluetoothSocket,调用BluetoothSocket的连接方法connect,connect就是对蓝牙服务器的连接,当connect正常返回后,也就意味着两个点到点的不同蓝牙设备数据传输连接建立了。
Android Bluetooth蓝牙的“客户端”对“服务器端”的连接请求建立,大体上可以简化为三个过程:
(a)首先要获得蓝牙“服务器端”的BluetoothDevice,至于如何获得服务器蓝牙设备BluetoothDevice,可以是附录文章1那样的扫描获得,也可以是附录文章2中的配对获得。
(b)得到蓝牙服务器端的BluetoothDevice后,调用createRfcommSocketToServiceRecord,这个过程可以简单的理解为创建一个类似Java的socket套接字对象一样,为后续的网络建立建立做准备。简单期间,此处也绑定一个蓝牙连接的熟知UUID:00001101-0000-1000-8000-00805F9B34FB
(c)接下来就是重点的connect。connect也是阻塞性质的蓝牙连接,直到和蓝牙服务器端设备的连接建立为止(如果发生连接异常则将跳出)。
(a)(b)(c)三阶段的伪代码核心摘要:


BluetoothDevice device;

BluetoothSocket socket;

socket = device.createRfcommSocketToServiceRecord(UUID);

socket.connect();
    

完整的Android Bluetooth蓝牙客户端代码:

package zhangphil.client;

import java.util.UUID;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {

	private BluetoothAdapter mBluetoothAdapter;

	private final String tag = "zhangphil";
	private final String MY_UUID = "00001101-0000-1000-8000-00805F9B34FB";

	// 广播接收发现蓝牙设备
	private BroadcastReceiver mReceiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			if (BluetoothDevice.ACTION_FOUND.equals(action)) {

				BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

				String name = device.getName();
				if (name != null)
					Log.d(tag, "发现设备:" + name);

				if (name != null && name.equals("phil-pad")) {
					Log.d(tag, "发现目标设备,开始线程连接!");

					// 蓝牙搜索是非常消耗系统资源开销的过程,一旦发现了目标感兴趣的设备,可以考虑关闭扫描。
					mBluetoothAdapter.cancelDiscovery();

					new Thread(new ClientThread(device)).start();
				}
			}
		}
	};

	private class ClientThread extends Thread {

		private BluetoothDevice device;

		public ClientThread(BluetoothDevice device) {
			this.device = device;
		}

		@Override
		public void run() {

			BluetoothSocket socket = null;

			try {
				socket = device.createRfcommSocketToServiceRecord(UUID.fromString(MY_UUID));

				Log.d(tag, "连接服务端...");
				socket.connect();
				Log.d(tag, "连接建立.");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

		// 注册广播接收器。接收蓝牙发现讯息
		IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
		registerReceiver(mReceiver, filter);

		if (mBluetoothAdapter.startDiscovery()) {
			Log.d(tag, "启动蓝牙扫描设备...");
		}
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		unregisterReceiver(mReceiver);
	}
}


三,Android Bluetooth蓝牙设备服务器端与客户端连接的过程。
(a)首先要准备两个不同的Android设备,当然,这两个Android设备必须都有蓝牙。然后将服务器端安装在一个Android设备上,客户端则安装在另外一个不同的Android设备上。
(b)注意设备的名字,在我写的这个Android Bluetooth蓝牙连接的例子中,根据服务器端蓝牙设备的名字进行过滤和匹配。我把服务器端的蓝牙设备名字设置成:phil-pad。在不同的蓝牙设备作为服务器端时候这个名字不同,必须做出相应的调整,否则匹配不到,将无法连接。
(c)最好先运行服务器端代码,然后再启动客户端代码。代码为了更简洁的说明Android Bluetooth蓝牙连接功能,未做繁复的异常检测。启动代码前,请务必打开蓝牙。最好将两台设备进行配对好。


蓝牙权限:

 <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>


附录文章:

1,《Android Bluetooth蓝牙开发:发现Bluetooth蓝牙设备(1)》链接地址:http://blog.csdn.net/zhangphil/article/details/50524809
2,《Android Bluetooth蓝牙开发:Bluetooth蓝牙设备配对Paired Bluetooth Devices(2)》链接地址:http://blog.csdn.net/zhangphil/article/details/50537796
3,Android Bluetooth蓝牙开发Google官方文档链接地址:http://developer.android.com/intl/zh-cn/guide/topics/connectivity/bluetooth.html

蓝牙设备连接电脑

阅读数 296

没有更多推荐了,返回首页