本示例演示了如何对蓝牙进行基本操作, 传统蓝牙本机管理: 主要是针对蓝牙本机的基本操作,包括:打开和关闭蓝牙、设置和获取本机蓝牙名称、扫描和取消扫描周边蓝牙设备、获取本机蓝牙profile对其他设备的连接状态、获取本机蓝牙已配对的蓝牙设备列表。 传统蓝牙远端管理操作: 主要是针对远端蓝牙设备的基本操作,包括:获取远端蓝牙设备地址、类型、名称和配对状态,以及向远端设备发起配对。 传统蓝牙和BLE的的概念请参考 蓝牙开发概述 还有一个小知识点, 调用蓝牙的打开接口需要ohos.permission.USE_BLUETOOTH权限, 调用蓝牙扫描接口需要ohos.permission.LOCATION权限和ohos.permission.DISCOVER_BLUETOOTH权限。

2.搭建环境

安装DevEco Studio,详情请参考DevEco Studio下载。 设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:

如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。 如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。 下载源码后,使用DevEco Studio 打开项目,模拟器运行即可。 真机运行需要将config.json中的buddleName修改为自己的,如果没有请到AGC上进行配置,参见 使用模拟器进行调试 。

3.代码结构

harmonyos 2.0有什么功能(HarmonyOSSample之)(1)

4.实例讲解4.1.界面布局

harmonyos 2.0有什么功能(HarmonyOSSample之)(2)

harmonyos 2.0有什么功能(HarmonyOSSample之)(3)

4.2.后台代码4.2.1 涉及的相关类

SDK提供的核心类: BluetoothHost.java//蓝牙主机,可以管理蓝牙,提供了蓝牙的基本操作,打开/关闭/获取状态等。 BluetoothRemoteDevice.java//蓝牙对象,用于建立与对端设备的连接并查询名称、设备类型和配对状态等信息。

自定义的类: BluetoothItemProvider.java//蓝牙设备列表项提供程序 BluetoothEventListener.java//蓝牙事件监听接口 BluetoothPlugin.java//蓝牙插件 BluetoothDevice.java//蓝牙对象简易模型

4.2.2 BluetoothPlugin.java 蓝牙插件提供的功能

a.初始化BluetoothHost蓝牙主机对象getDefaultHost

/** * 初始化蓝牙主机对象和监听器 * Initializes the Bluetooth Host on device. * @param eventListener interface to update the Bluwettoth events */ public void initializeBluetooth(BluetoothEventListener eventListener) { bluetoothEventListener = eventListener; btHost = BluetoothHost.getDefaultHost(mainSliceContext); LogUtil.info(TAG, "initializeBluetooth, btHost:" btHost); }

b.开启/关闭蓝牙/获取蓝牙状态enableBt/disableBt/getBtState

/** * 开启蓝牙 * 低功耗蓝牙(BLE ,Bluetooth Low Energy),LE是2010年才提出的 * 经典蓝牙(classic Bluetooth),包括BR,EDR和HS(AMP)三种模式 * Enables the Bluetooth on device. */ public void enableBluetooth() { LogUtil.info("enableBluetooth", "getBtState:" btHost.getBtState()); //获取蓝牙主机的状态 if (btHost.getBtState() == STATE_OFF || btHost.getBtState() == STATE_TURNING_OFF || btHost.getBtState() ==STATE_BLE_ON) { LogUtil.info("enableBluetooth", "enableBt:" btHost.getBtState()); //开启蓝牙 btHost.enableBt(); } //事件通知蓝牙状态发生改变 bluetoothEventListener.notifyBluetoothStatusChanged(btHost.getBtState()); } /** * 关闭蓝牙 * Disables the Bluetooth on device. */ public void disableBluetooth() { if (btHost.getBtState() == STATE_ON || btHost.getBtState() == STATE_TURNING_ON) { btHost.disableBt(); } bluetoothEventListener.notifyBluetoothStatusChanged(btHost.getBtState()); } /** * 获取蓝牙状态 * Obtains the status of the Bluetooth on device. * @return status of Bluetooth on device */ public int getBluetoothStatus() { LogUtil.info("getBluetoothStatus", "getBluetoothStatus:" btHost.getBtState()); //获取蓝牙状态 return btHost.getBtState(); }

c.开始蓝牙发现startBtDiscovery

还要注意的是蓝牙发现操作需要申请位置权限。

/** * 开始蓝牙发现 * Scans the currently available bluetooth devices */ public void startBtDiscovery() { if (!btHost.isBtDiscovering()) { //开始发现设备,大约需要12.8s btHost.startBtDiscovery(); } } /** *判断是否有权限 */ private boolean hasPermission() { return mainSliceContext.verifySelfPermission(Constants.PERM_LOCATION) == IBundleManager.PERMISSION_GRANTED; } /** * 启动蓝牙扫描 * Scans the currently available bluetooth devices */ public void startBtScan() { LogUtil.info("startBtScan", "getBtState:" btHost.getBtState()); int btStatus = btHost.getBtState(); if (btStatus == STATE_ON) { if (hasPermission()) { startBtDiscovery(); } else { requestPermission(); } } }

d.蓝牙设备配对及获取已配对的设备列表startPair/getPairedDevices

/** * 启动与给定地址的蓝牙设备配对。 * initiate pairing with bluetooth device of given address. * @param pairAddress address of the bluetooth device */ public void startPair(String pairAddress) { Optional<BluetoothRemoteDevice> optBluetoothDevice = getSelectedDevice(pairAddress); optBluetoothDevice.ifPresent(BluetoothRemoteDevice::startPair); } /** * 获取要配对的设备 * @param pairAddress * @return */ private Optional<BluetoothRemoteDevice> getSelectedDevice(String pairAddress) { if (pairAddress != null && !pairAddress.isEmpty()) { for (BluetoothRemoteDevice device : availableDevices) { if (device.getDeviceAddr().equals(pairAddress)) { return Optional.ofNullable(device); } } } return Optional.empty(); } /** * 获取已配对的蓝牙设备列表 * Obtains the paired Bluetooth device list. * @return paired Bluetooth devices */ public List<BluetoothDevice> getPairedDevices() { //btHost.getPairedDevices() Set<BluetoothRemoteDevice> pairedDevices = new HashSet<>(btHost.getPairedDevices()); return getBluetoothDevices(pairedDevices); }

e.蓝牙事件的订阅/取消 及 相关事件的处理

在处理蓝牙事件的同时,通过BluetoothEventListener通知MainAbilitySlice。

/** * 订阅蓝牙事件 * Subscribe for Events of Bluetooth using CommonEvents */ public void subscribeBluetoothEvents() { MatchingSkills matchingSkills = new MatchingSkills(); //表示蓝牙状态改变时上报的事件。 matchingSkills.addEvent(BluetoothHost.EVENT_HOST_STATE_UPDATE); //指示蓝牙扫描开始时报告的事件。 matchingSkills.addEvent(BluetoothHost.EVENT_HOST_DISCOVERY_STARTED); //指示蓝牙扫描完成时报告的事件。 matchingSkills.addEvent(BluetoothHost.EVENT_HOST_DISCOVERY_FINISHED); //表示发现远程蓝牙设备时上报的事件。 matchingSkills.addEvent(BluetoothRemoteDevice.EVENT_DEVICE_DISCOVERED); //远程蓝牙设备配对时上报的事件。 matchingSkills.addEvent(BluetoothRemoteDevice.EVENT_DEVICE_PAIR_STATE); //用于创建 CommonEventSubscriber 实例并传递 subscribeInfo 参数的构造函数。 CommonEventSubscribeInfo subscribeInfo = new CommonEventSubscribeInfo(matchingSkills); //订阅者 commonEventSubscriber = new CommonEventSubscriber(subscribeInfo) { @Override public void onReceiveEvent(CommonEventData commonEventData) { Intent intent = commonEventData.getIntent(); handleIntent(intent); } }; try { //完成订阅 CommonEventManager.subscribeCommonEvent(commonEventSubscriber); } catch (RemoteException e) { LogUtil.error(TAG, "RemoteException while subscribe bluetooth events."); } } /** * 取消订阅蓝牙事件 * UnSubscribe for Bluetooth Events */ public void unSubscribeBluetoothEvents() { if (commonEventSubscriber != null) { try { CommonEventManager.unsubscribeCommonEvent(commonEventSubscriber); } catch (RemoteException e) { LogUtil.error(TAG, "RemoteException while unsubscribing bluetooth events."); } commonEventSubscriber = null; } } private void handleIntent(Intent intent) { if (intent == null) { return; } String action = intent.getAction(); switch (action) { //状态更新 case BluetoothHost.EVENT_HOST_STATE_UPDATE: handleHostStateUpdate(); break; //扫描开始 case BluetoothHost.EVENT_HOST_DISCOVERY_STARTED: handleDeviceDiscoveryState(true); break; //表示发现远程蓝牙设备时上报的事件。 case BluetoothRemoteDevice.EVENT_DEVICE_DISCOVERED: handleBluetoothDeviceDiscovered(intent); break; // 扫描完成 case BluetoothHost.EVENT_HOST_DISCOVERY_FINISHED: handleDeviceDiscoveryState(false); break; //表示远程蓝牙设备配对时上报的事件。 case BluetoothRemoteDevice.EVENT_DEVICE_PAIR_STATE: handleDevicePairState(intent); break; default: LogUtil.info(TAG, "Action not handled : " action); } } private void handleDevicePairState(Intent intent) { BluetoothRemoteDevice btRemoteDevice = intent.getSequenceableParam(BluetoothRemoteDevice.REMOTE_DEVICE_PARAM_DEVICE); if (btRemoteDevice.getPairState() == BluetoothRemoteDevice.PAIR_STATE_PAIRED) { //更新2个设备列表 updateAvailableDeviceList(btRemoteDevice); updatePairedDeviceList(); } } private void handleDeviceDiscoveryState(boolean isStarted) { //处理扫描状态变化事件通知 bluetoothEventListener.notifyDiscoveryState(isStarted); } /** * 处理蓝牙状态变化通知 */ private void handleHostStateUpdate() { int status = getBluetoothStatus(); bluetoothEventListener.notifyBluetoothStatusChanged(status); } /** * 处理蓝牙发现事件 * @param intent */ private void handleBluetoothDeviceDiscovered(Intent intent) { BluetoothRemoteDevice btRemoteDevice = intent.getSequenceableParam(BluetoothRemoteDevice.REMOTE_DEVICE_PARAM_DEVICE); //未配对的设备 if (btRemoteDevice.getPairState() != BluetoothRemoteDevice.PAIR_STATE_PAIRED) { //发现后添加到可用的蓝牙设备 availableDevices.add(btRemoteDevice); } bluetoothEventListener.updateAvailableDevices(getAvailableDevices()); } /** * 更新可用设备列表 * @param remoteDevice */ private void updateAvailableDeviceList(BluetoothRemoteDevice remoteDevice) { //移除以配对的蓝牙 availableDevices.removeIf(device -> device.getDeviceAddr().equals(remoteDevice.getDeviceAddr())); bluetoothEventListener.updateAvailableDevices(getAvailableDevices()); } private void updatePairedDeviceList() { //刷新已配对的蓝牙列表 bluetoothEventListener.updatePairedDevices(getPairedDevices()); } public List<BluetoothDevice> getAvailableDevices() { return getBluetoothDevices(availableDevices); } /** * 获取已配对的蓝牙设备列表 * Obtains the paired Bluetooth device list. * @return paired Bluetooth devices */ public List<BluetoothDevice> getPairedDevices() { //btHost.getPairedDevices() Set<BluetoothRemoteDevice> pairedDevices = new HashSet<>(btHost.getPairedDevices()); return getBluetoothDevices(pairedDevices); } private List<BluetoothDevice> getBluetoothDevices(Set<BluetoothRemoteDevice> remoteDeviceList) { List<BluetoothDevice> btDevicesList = new ArrayList<>(); if (remoteDeviceList != null) { // btDevicesList = remoteDeviceList.stream().map(BluetoothDevice::new).collect(Collectors.toList()); } return btDevicesList; }

4.2.3 BluetoothEventListener.java 自定义的蓝牙事件监听器接口

public interface BluetoothEventListener { void updateAvailableDevices(List<BluetoothDevice> bluetoothDevice); void updatePairedDevices(List<BluetoothDevice> bluetoothDevice); void notifyBluetoothStatusChanged(int bluetoothStatus); void notifyDiscoveryState(boolean isStarted); }

4.2.4 BluetoothItemProvider.java 蓝牙设备列表提供程序

这个可以作为一个标准件了,每个ListContainer都可以用它,包括了列表的通用操作,像数据更新,点击事件等。

public class BluetoothItemProvider extends BaseItemProvider { private final AbilityContext context; private List<BluetoothDevice> bluetoothDeviceList; public BluetoothItemProvider(AbilityContext context, List<BluetoothDevice> itemList) { this.context = context; bluetoothDeviceList = itemList; } @Override public int getCount() { return bluetoothDeviceList.size(); } @Override public Object getItem(int position) { return bluetoothDeviceList.get(position); } @Override public long getItemId(int position) { return position; } @Override public Component getComponent(int position, Component component, ComponentContainer componentContainer) { return getRootComponent(position); } private Component getRootComponent(int position) { //List item 布局组件 Component rootComponent = LayoutScatter.getInstance(context) .parse(ResourceTable.Layout_list_item, null, false); Text deviceName = (Text) rootComponent.findComponentById(ResourceTable.Id_bluetooth_device_name); //蓝牙设备名称 BluetoothDevice bluetoothDevice = bluetoothDeviceList.get(position); deviceName.setText(bluetoothDevice.getName()); //设置点击监听事件,开始配对 rootComponent.setClickedListener(component -> { LogUtil.info("BluetoothItemProvider", "startPair:" bluetoothDevice.getAddress()); //表示对端设备未配对。 if (bluetoothDevice.getPairState() == BluetoothRemoteDevice.PAIR_STATE_NONE) { //启动与给定地址的蓝牙设备配对。 BluetoothPlugin.getInstance(context).startPair(bluetoothDevice.getAddress()); } }); return rootComponent; } /** * 更新蓝牙设备列表 * updates available Bluetooth devices in UI * * @param devices list of Bluetooth devices */ public void updateDeviceList(List<BluetoothDevice> devices) { bluetoothDeviceList = devices; notifyDataChanged(); } }

4.2.5 MainAbilitySlice.java 主能力页面操作

a.首先是实现了几个相关的接口 Component.ClickedListener 用于实现按钮的点击事件 BluetoothEventListener 用于实现蓝牙事件通知的处理 CheckedStateChangedListener 用于实现switch开关的处理

public class MainAbilitySlice extends AbilitySlice implements Component.ClickedListener, BluetoothEventListener, AbsButton.CheckedStateChangedListener {

b.初始化工作

在onActive生命周期函数中

初始化蓝牙插件/初始化容器列表/初始化组件/订阅蓝牙事件

/** * 初始化蓝牙插件 */ private void initializeBluetoothHost() { // BluetoothPlugin.getInstance(this).initializeBluetooth(this); } private void initComponents() { //'开始发现' 按钮,用来搜索附件的蓝牙设备 Button btnStartDiscovery = (Button) findComponentById(ResourceTable.Id_btn_start_discovery); //因为implements Component.ClickedListener 所以可以这样写 btnStartDiscovery.setClickedListener(this); initListContainer(); //蓝牙状态文本组件 textBluetoothStatus = (Text) findComponentById(ResourceTable.Id_bluetooth_status); //蓝牙开关组件 bluetoothSwitch = (Switch) findComponentById(ResourceTable.Id_bt_switch); //设置状态监听事件 bluetoothSwitch.setCheckedStateChangedListener(this); //根据系统蓝牙状态设置开关 updateBluetoothStatus(BluetoothPlugin.getInstance(this).getBluetoothStatus()); //附件的蓝牙设备容器列表 containerLists = (DirectionalLayout) findComponentById(ResourceTable.Id_container_lists); //根据蓝牙状态控制是否显示附件的蓝牙设备容器列表 containerLists.setVisibility(isBluetoothEnabled() ? Component.VISIBLE : Component.HIDE); //环形进度条组件 progressBar = (ProgressBar) findComponentById(ResourceTable.Id_progressbar); } /** * 订阅蓝牙事件 */ private void subscribeBluetoothEvents() { // BluetoothPlugin.getInstance(this).subscribeBluetoothEvents(); } /** * 初始化容器列表 */ private void initListContainer() { ListContainer availableDevicesContainer = (ListContainer) findComponentById(ResourceTable.Id_list_available_devices); //1.获取可用的蓝牙设备 availableDevicesItemProvider = new BluetoothItemProvider(this, BluetoothPlugin.getInstance(this).getAvailableDevices()); //设置提供程序 availableDevicesContainer.setItemProvider(availableDevicesItemProvider); //2.获取已配对的蓝牙设备 ListContainer pairedDevicesContainer = (ListContainer) findComponentById(ResourceTable.Id_list_paired_devices); pairedDevicesItemProvider = new BluetoothItemProvider(this, BluetoothPlugin.getInstance(this).getPairedDevices()); //设置提供程序 pairedDevicesContainer.setItemProvider(pairedDevicesItemProvider); } /** * 更新蓝牙状态开关和文本 * @param bluetoothStatus */ private void updateBluetoothStatus(int bluetoothStatus) { LogUtil.info("MainAbilitySlice", "updateBluetoothStatus:" bluetoothStatus); //开关 bluetoothSwitch.setChecked(isBluetoothEnabled()); //状态文本 textBluetoothStatus.setText(getBluetoothStatusString(bluetoothStatus)); } /** * 显示环形进度条 * 用定时器实现了一个进度条,遗憾的是有一定的卡顿 * @param isShow */ private void showProgressBar(boolean isShow) { LogUtil.info("MainAbilitySlice", "isShow:" isShow); LogUtil.info("MainAbilitySlice", "timer=" timer); if(isShow){ //显示进度条 progressBar.setVisibility(Component.VISIBLE); if(timer==null){ timer = new Timer(); } if(timerTask==null){ timerTask= new TimerTask() { @Override public void run() { if(percent==10){ percent=1; progressBar.setProgressValue(0); }else{ percent ; } LogUtil.info("MainAbilitySlice", "percent:" percent); getContext().getUITaskDispatcher().asyncDispatch(new Runnable() { @Override public void run() { progressBar.setProgressValue(percent*10); } }); } }; // timer.schedule(timerTask, 0, 1000); } }else { //隐藏进度条 progressBar.setProgressValue(0); progressBar.setVisibility(Component.HIDE); if(timer!=null){ LogUtil.info("MainAbilitySlice", "timer set null"); timer.cancel(); timerTask.cancel(); timer=null; timerTask=null; } } } /** * 获取蓝牙状态 * @return */ private boolean isBluetoothEnabled() { int status = BluetoothPlugin.getInstance(this).getBluetoothStatus(); LogUtil.info("isBluetoothEnabled", "isBluetoothEnabled:" status); return status == BluetoothHost.STATE_ON; } private String getBluetoothStatusString(int bluetoothStatus) { LogUtil.info("bluetoothStatus", "bluetoothStatus:" bluetoothStatus); switch (bluetoothStatus) { case BluetoothHost.STATE_OFF: case BluetoothHost.STATE_BLE_TURNING_OFF: //disabled 不可用 return Constants.BT_DISABLED; case BluetoothHost.STATE_TURNING_ON: //turning on 开启 return Constants.BT_TURNING_ON; case BluetoothHost.STATE_ON: //enabled 可用的 return Constants.BT_ENABLED; case BluetoothHost.STATE_TURNING_OFF: //turning off 关闭 return Constants.BT_TURNING_OFF; default: //undefined 未定义 return Constants.BT_UNDEFINED; } }

c.实现BluetoothEventListener接口相关函数

@Override public void updateAvailableDevices(List<BluetoothDevice> list) { //implements BluetoothEventListener //更新容器数据 availableDevicesItemProvider.updateDeviceList(list); } @Override public void updatePairedDevices(List<BluetoothDevice> list) { //implements BluetoothEventListener //更新容器数据 pairedDevicesItemProvider.updateDeviceList(list); } @Override public void notifyBluetoothStatusChanged(int bluetoothStatus) { LogUtil.info("notifyBluetoothStatusChanged", "bluetoothStatus:" bluetoothStatus); //蓝牙状态改变事件通知 updateBluetoothStatus(bluetoothStatus); } @Override public void notifyDiscoveryState(boolean isStarted) { //蓝牙发现状态的事件通知 showProgressBar(isStarted); }

d.实现CheckedStateChangedListener接口相关函数

@Override public void onCheckedChanged(AbsButton absButton, boolean isChecked) { //开关状态改变事件触发 if (absButton.getId() == ResourceTable.Id_bt_switch && containerLists != null) { if (isChecked) { LogUtil.info("onCheckedChanged", "enableBluetooth"); //开启蓝牙 BluetoothPlugin.getInstance(this).enableBluetooth(); containerLists.setVisibility(Component.VISIBLE); } else { //关闭蓝牙 BluetoothPlugin.getInstance(this).disableBluetooth(); containerLists.setVisibility(Component.HIDE); } } }

e.实现ClickedListener接口相关函数,开始发现蓝牙

@Override public void onClick(Component component) { LogUtil.info("MainAbilitySlice", "startBtScan..."); //开始发现 扫描蓝牙设备 if (component.getId() == ResourceTable.Id_btn_start_discovery) { LogUtil.info("MainAbilitySlice", "startBtScan..."); BluetoothPlugin.getInstance(this).startBtScan(); } }

——————

原创:老王丨鸿蒙hms开发者高级认证持证人!提供鸿蒙关键技术解析及软件开发相关技术干货~【公众号:鸿蒙开发者老王】

,