本示例演示了如何對藍牙進(jìn)行基本操作, 傳統藍牙本機管理: 主要是針對藍牙本機的基本操作,包括:打開(kāi)和關(guān)閉藍牙、設置和獲取本機藍牙名稱(chēng)、掃描和取消掃描周邊藍牙設備、獲取本機藍牙profile對其他設備的連接狀態(tài)、獲取本機藍牙已配對的藍牙設備列表。 傳統藍牙遠端管理操作: 主要是針對遠端藍牙設備的基本操作,包括:獲取遠端藍牙設備地址、類(lèi)型、名稱(chēng)和配對狀態(tài),以及向遠端設備發(fā)起配對。 傳統藍牙和BLE的的概念請參考 藍牙開(kāi)發(fā)概述 還有一個(gè)小知識點(diǎn), 調用藍牙的打開(kāi)接口需要ohos.permission.USE_BLUETOOTH權限, 調用藍牙掃描接口需要ohos.permission.LOCATION權限和ohos.permission.DISCOVER_BLUETOOTH權限。
2.搭建環(huán)境
安裝DevEco Studio,詳情請參考DevEco Studio下載。 設置DevEco Studio開(kāi)發(fā)環(huán)境,DevEco Studio開(kāi)發(fā)環(huán)境需要依賴(lài)于網(wǎng)絡(luò )環(huán)境,需要連接上網(wǎng)絡(luò )才能確保工具的正常使用,可以根據如下兩種情況來(lái)配置開(kāi)發(fā)環(huán)境:
如果可以直接訪(fǎng)問(wèn)Internet,只需進(jìn)行下載HarmonyOS SDK操作。 如果網(wǎng)絡(luò )不能直接訪(fǎng)問(wèn)Internet,需要通過(guò)代理服務(wù)器才可以訪(fǎng)問(wèn),請參考配置開(kāi)發(fā)環(huán)境。 下載源碼后,使用DevEco Studio 打開(kāi)項目,模擬器運行即可。 真機運行需要將config.json中的buddleName修改為自己的,如果沒(méi)有請到AGC上進(jìn)行配置,參見(jiàn) 使用模擬器進(jìn)行調試 。
3.代碼結構
4.實(shí)例講解
4.1.界面布局
4.2.后臺代碼
4.2.1 涉及的相關(guān)類(lèi)
SDK提供的核心類(lèi): BluetoothHost.java//藍牙主機,可以管理藍牙,提供了藍牙的基本操作,打開(kāi)/關(guān)閉/獲取狀態(tài)等。BluetoothRemoteDevice.java//藍牙對象,用于建立與對端設備的連接并查詢(xún)名稱(chēng)、設備類(lèi)型和配對狀態(tài)等信息。
自定義的類(lèi):BluetoothItemProvider.java//藍牙設備列表項提供程序BluetoothEventListener.java//藍牙事件監聽(tīng)接口 BluetoothPlugin.java//藍牙插件 BluetoothDevice.java//藍牙對象簡(jiǎn)易模型
4.2.2 BluetoothPlugin.java 藍牙插件提供的功能
a.初始化BluetoothHost藍牙主機對象getDefaultHost
/**
* 初始化藍牙主機對象和監聽(tīng)器
* Initializes the Bluetooth Host on device.
* @param eventListener interface to update the Bluwettoth events
*/publicvoidinitializeBluetooth(BluetoothEventListener eventListener){
bluetoothEventListener = eventListener;
btHost = BluetoothHost.getDefaultHost(mainSliceContext);
LogUtil.info(TAG,"initializeBluetooth, btHost:"+btHost);
}
b.開(kāi)啟/關(guān)閉藍牙/獲取藍牙狀態(tài)enableBt/disableBt/getBtState
/**
* 開(kāi)啟藍牙
* 低功耗藍牙(BLE ,Bluetooth Low Energy),LE是2010年才提出的
* 經(jīng)典藍牙(classic Bluetooth),包括BR,EDR和HS(AMP)三種模式
* Enables the Bluetooth on device.
*/publicvoidenableBluetooth() {LogUtil.info("enableBluetooth","getBtState:"+btHost.getBtState());//獲取藍牙主機的狀態(tài)if(btHost.getBtState() == STATE_OFF ||
btHost.getBtState() == STATE_TURNING_OFF ||
btHost.getBtState() ==STATE_BLE_ON) {LogUtil.info("enableBluetooth","enableBt:"+btHost.getBtState());//開(kāi)啟藍牙btHost.enableBt();
}//事件通知藍牙狀態(tài)發(fā)生改變bluetoothEventListener.notifyBluetoothStatusChanged(btHost.getBtState());
}/**
* 關(guān)閉藍牙
* Disables the Bluetooth on device.
*/publicvoiddisableBluetooth() {if(btHost.getBtState() == STATE_ON || btHost.getBtState() == STATE_TURNING_ON) {btHost.disableBt();
}bluetoothEventListener.notifyBluetoothStatusChanged(btHost.getBtState());
}/**
* 獲取藍牙狀態(tài)
* Obtains the status of the Bluetooth on device.
* @return status of Bluetooth on device
*/publicintgetBluetoothStatus() {LogUtil.info("getBluetoothStatus","getBluetoothStatus:"+btHost.getBtState());//獲取藍牙狀態(tài)returnbtHost.getBtState();
}
c.開(kāi)始藍牙發(fā)現startBtDiscovery
還要注意的是藍牙發(fā)現操作需要申請位置權限。
/**
* 開(kāi)始藍牙發(fā)現
* Scans the currently available bluetooth devices
*/publicvoidstartBtDiscovery(){if(!btHost.isBtDiscovering()) {//開(kāi)始發(fā)現設備,大約需要12.8sbtHost.startBtDiscovery();
}
}/**
*判斷是否有權限
*/privatebooleanhasPermission(){returnmainSliceContext.verifySelfPermission(Constants.PERM_LOCATION) == IBundleManager.PERMISSION_GRANTED;
}/**
* 啟動(dòng)藍牙掃描
* Scans the currently available bluetooth devices
*/publicvoidstartBtScan(){
LogUtil.info("startBtScan","getBtState:"+btHost.getBtState());intbtStatus = btHost.getBtState();if(btStatus == STATE_ON) {if(hasPermission()) {
startBtDiscovery();
}else{
requestPermission();
}
}
}
d.藍牙設備配對及獲取已配對的設備列表startPair/getPairedDevices
/**
* 啟動(dòng)與給定地址的藍牙設備配對。
* initiate pairing with bluetooth device of given address.
*@parampairAddress address of the bluetooth device
*/publicvoid startPair(String pairAddress) {
Optional optBluetoothDevice = getSelectedDevice(pairAddress);
optBluetoothDevice.ifPresent(BluetoothRemoteDevice::startPair);
}/**
* 獲取要配對的設備
*@parampairAddress
*@return*/privateOptional getSelectedDevice(String pairAddress) {if(pairAddress !=null&& !pairAddress.isEmpty()) {for(BluetoothRemoteDevice device : availableDevices) {if(device.getDeviceAddr().equals(pairAddress)) {returnOptional.ofNullable(device);
}
}
}returnOptional.empty();
}/**
* 獲取已配對的藍牙設備列表
* Obtains the paired Bluetooth device list.
*@returnpaired Bluetooth devices
*/publicList getPairedDevices() {//btHost.getPairedDevices()Set pairedDevices =newHashSet<>(btHost.getPairedDevices());returngetBluetoothDevices(pairedDevices);
}
e.藍牙事件的訂閱/取消 及 相關(guān)事件的處理
在處理藍牙事件的同時(shí),通過(guò)BluetoothEventListener通知MainAbilitySlice。
/**
* 訂閱藍牙事件
* Subscribe for Events of Bluetooth using CommonEvents
*/publicvoidsubscribeBluetoothEvents(){
MatchingSkills matchingSkills =newMatchingSkills();//表示藍牙狀態(tài)改變時(shí)上報的事件。matchingSkills.addEvent(BluetoothHost.EVENT_HOST_STATE_UPDATE);//指示藍牙掃描開(kāi)始時(shí)報告的事件。matchingSkills.addEvent(BluetoothHost.EVENT_HOST_DISCOVERY_STARTED);//指示藍牙掃描完成時(shí)報告的事件。matchingSkills.addEvent(BluetoothHost.EVENT_HOST_DISCOVERY_FINISHED);//表示發(fā)現遠程藍牙設備時(shí)上報的事件。matchingSkills.addEvent(BluetoothRemoteDevice.EVENT_DEVICE_DISCOVERED);//遠程藍牙設備配對時(shí)上報的事件。matchingSkills.addEvent(BluetoothRemoteDevice.EVENT_DEVICE_PAIR_STATE);//用于創(chuàng )建 CommonEventSubscriber 實(shí)例并傳遞 subscribeInfo 參數的構造函數。CommonEventSubscribeInfo subscribeInfo =newCommonEventSubscribeInfo(matchingSkills);//訂閱者commonEventSubscriber =newCommonEventSubscriber(subscribeInfo) {@OverridepublicvoidonReceiveEvent(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
*/publicvoidunSubscribeBluetoothEvents(){if(commonEventSubscriber !=null) {try{
CommonEventManager.unsubscribeCommonEvent(commonEventSubscriber);
}catch(RemoteException e) {
LogUtil.error(TAG,"RemoteException while unsubscribing bluetooth events.");
}
commonEventSubscriber =null;
}
}privatevoidhandleIntent(Intent intent){if(intent ==null) {return;
}
String action = intent.getAction();switch(action) {//狀態(tài)更新caseBluetoothHost.EVENT_HOST_STATE_UPDATE:
handleHostStateUpdate();break;//掃描開(kāi)始caseBluetoothHost.EVENT_HOST_DISCOVERY_STARTED:
handleDeviceDiscoveryState(true);break;//表示發(fā)現遠程藍牙設備時(shí)上報的事件。caseBluetoothRemoteDevice.EVENT_DEVICE_DISCOVERED:
handleBluetoothDeviceDiscovered(intent);break;// 掃描完成caseBluetoothHost.EVENT_HOST_DISCOVERY_FINISHED:
handleDeviceDiscoveryState(false);break;//表示遠程藍牙設備配對時(shí)上報的事件。caseBluetoothRemoteDevice.EVENT_DEVICE_PAIR_STATE:
handleDevicePairState(intent);break;default:
LogUtil.info(TAG,"Action not handled : "+ action);
}
}privatevoidhandleDevicePairState(Intent intent){
BluetoothRemoteDevice btRemoteDevice = intent.getSequenceableParam(BluetoothRemoteDevice.REMOTE_DEVICE_PARAM_DEVICE);if(btRemoteDevice.getPairState() == BluetoothRemoteDevice.PAIR_STATE_PAIRED) {//更新2個(gè)設備列表updateAvailableDeviceList(btRemoteDevice);
updatePairedDeviceList();
}
}privatevoidhandleDeviceDiscoveryState(booleanisStarted){//處理掃描狀態(tài)變化事件通知bluetoothEventListener.notifyDiscoveryState(isStarted);
}/**
* 處理藍牙狀態(tài)變化通知
*/privatevoidhandleHostStateUpdate(){intstatus = getBluetoothStatus();
bluetoothEventListener.notifyBluetoothStatusChanged(status);
}/**
* 處理藍牙發(fā)現事件
*@paramintent
*/privatevoidhandleBluetoothDeviceDiscovered(Intent intent){
BluetoothRemoteDevice btRemoteDevice =
intent.getSequenceableParam(BluetoothRemoteDevice.REMOTE_DEVICE_PARAM_DEVICE);//未配對的設備if(btRemoteDevice.getPairState() != BluetoothRemoteDevice.PAIR_STATE_PAIRED) {//發(fā)現后添加到可用的藍牙設備availableDevices.add(btRemoteDevice);
}
bluetoothEventListener.updateAvailableDevices(getAvailableDevices());
}/**
* 更新可用設備列表
*@paramremoteDevice
*/privatevoidupdateAvailableDeviceList(BluetoothRemoteDevice remoteDevice){//移除以配對的藍牙availableDevices.removeIf(device -> device.getDeviceAddr().equals(remoteDevice.getDeviceAddr()));
bluetoothEventListener.updateAvailableDevices(getAvailableDevices());
}privatevoidupdatePairedDeviceList(){//刷新已配對的藍牙列表bluetoothEventListener.updatePairedDevices(getPairedDevices());
}publicListgetAvailableDevices(){returngetBluetoothDevices(availableDevices);
}/**
* 獲取已配對的藍牙設備列表
* Obtains the paired Bluetooth device list.
*@returnpaired Bluetooth devices
*/publicListgetPairedDevices(){//btHost.getPairedDevices()Set pairedDevices =newHashSet<>(btHost.getPairedDevices());returngetBluetoothDevices(pairedDevices);
}privateListgetBluetoothDevices(Set remoteDeviceList){
List btDevicesList =newArrayList<>();if(remoteDeviceList !=null) {//btDevicesList = remoteDeviceList.stream().map(BluetoothDevice::new).collect(Collectors.toList());
}returnbtDevicesList;
}
4.2.3BluetoothEventListener.java 自定義的藍牙事件監聽(tīng)器接口
publicinterfaceBluetoothEventListener{voidupdateAvailableDevices(List bluetoothDevice);voidupdatePairedDevices(List bluetoothDevice);voidnotifyBluetoothStatusChanged(intbluetoothStatus);voidnotifyDiscoveryState(booleanisStarted);
}
4.2.4 BluetoothItemProvider.java 藍牙設備列表提供程序
這個(gè)可以作為一個(gè)標準件了,每個(gè)ListContainer都可以用它,包括了列表的通用操作,像數據更新,點(diǎn)擊事件等。
publicclassBluetoothItemProviderextendsBaseItemProvider{privatefinalAbilityContext context;privateList bluetoothDeviceList;publicBluetoothItemProvider(AbilityContext context, List itemList){this.context = context;
bluetoothDeviceList = itemList;
}@OverridepublicintgetCount(){returnbluetoothDeviceList.size();
}@OverridepublicObjectgetItem(intposition){returnbluetoothDeviceList.get(position);
}@OverridepubliclonggetItemId(intposition){returnposition;
}@OverridepublicComponentgetComponent(intposition, Component component, ComponentContainer componentContainer){returngetRootComponent(position);
}privateComponentgetRootComponent(intposition){//List item 布局組件Component rootComponent = LayoutScatter.getInstance(context)
.parse(ResourceTable.Layout_list_item,null,false);
Text deviceName = (Text) rootComponent.findComponentById(ResourceTable.Id_bluetooth_device_name);//藍牙設備名稱(chēng)BluetoothDevice bluetoothDevice = bluetoothDeviceList.get(position);
deviceName.setText(bluetoothDevice.getName());//設置點(diǎn)擊監聽(tīng)事件,開(kāi)始配對rootComponent.setClickedListener(component -> {
LogUtil.info("BluetoothItemProvider","startPair:"+ bluetoothDevice.getAddress());//表示對端設備未配對。if(bluetoothDevice.getPairState() == BluetoothRemoteDevice.PAIR_STATE_NONE) {//啟動(dòng)與給定地址的藍牙設備配對。BluetoothPlugin.getInstance(context).startPair(bluetoothDevice.getAddress());
}
});returnrootComponent;
}/**
* 更新藍牙設備列表
* updates available Bluetooth devices in UI
*
*@paramdevices list of Bluetooth devices
*/publicvoidupdateDeviceList(List devices){
bluetoothDeviceList = devices;
notifyDataChanged();
}
}
4.2.5 MainAbilitySlice.java 主能力頁(yè)面操作
a.首先是實(shí)現了幾個(gè)相關(guān)的接口Component.ClickedListener 用于實(shí)現按鈕的點(diǎn)擊事件 BluetoothEventListener 用于實(shí)現藍牙事件通知的處理CheckedStateChangedListener 用于實(shí)現switch開(kāi)關(guān)的處理
publicclassMainAbilitySliceextendsAbilitySliceimplementsComponent.ClickedListener,BluetoothEventListener,AbsButton.CheckedStateChangedListener{
b.初始化工作
在onActive生命周期函數中
初始化藍牙插件/初始化容器列表/初始化組件/訂閱藍牙事件
/**
* 初始化藍牙插件
*/privatevoidinitializeBluetoothHost(){//BluetoothPlugin.getInstance(this).initializeBluetooth(this);
}privatevoidinitComponents(){//開(kāi)始發(fā)現 按鈕,用來(lái)搜索附件的藍牙設備Button btnStartDiscovery = (Button) findComponentById(ResourceTable.Id_btn_start_discovery);//因為implements Component.ClickedListener 所以可以這樣寫(xiě)btnStartDiscovery.setClickedListener(this);
initListContainer();//藍牙狀態(tài)文本組件textBluetoothStatus = (Text) findComponentById(ResourceTable.Id_bluetooth_status);//藍牙開(kāi)關(guān)組件bluetoothSwitch = (Switch) findComponentById(ResourceTable.Id_bt_switch);//設置狀態(tài)監聽(tīng)事件bluetoothSwitch.setCheckedStateChangedListener(this);//根據系統藍牙狀態(tài)設置開(kāi)關(guān)updateBluetoothStatus(BluetoothPlugin.getInstance(this).getBluetoothStatus());//附件的藍牙設備容器列表containerLists = (DirectionalLayout) findComponentById(ResourceTable.Id_container_lists);//根據藍牙狀態(tài)控制是否顯示附件的藍牙設備容器列表containerLists.setVisibility(isBluetoothEnabled() ? Component.VISIBLE : Component.HIDE);//環(huán)形進(jìn)度條組件progressBar = (ProgressBar) findComponentById(ResourceTable.Id_progressbar);
}/**
* 訂閱藍牙事件
*/privatevoidsubscribeBluetoothEvents(){//BluetoothPlugin.getInstance(this).subscribeBluetoothEvents();
}/**
* 初始化容器列表
*/privatevoidinitListContainer(){
ListContainer availableDevicesContainer =
(ListContainer) findComponentById(ResourceTable.Id_list_available_devices);//1.獲取可用的藍牙設備availableDevicesItemProvider =newBluetoothItemProvider(this,
BluetoothPlugin.getInstance(this).getAvailableDevices());//設置提供程序availableDevicesContainer.setItemProvider(availableDevicesItemProvider);//2.獲取已配對的藍牙設備ListContainer pairedDevicesContainer = (ListContainer) findComponentById(ResourceTable.Id_list_paired_devices);
pairedDevicesItemProvider =newBluetoothItemProvider(this,
BluetoothPlugin.getInstance(this).getPairedDevices());//設置提供程序pairedDevicesContainer.setItemProvider(pairedDevicesItemProvider);
}/**
* 更新藍牙狀態(tài)開(kāi)關(guān)和文本
*@parambluetoothStatus
*/privatevoidupdateBluetoothStatus(intbluetoothStatus){
LogUtil.info("MainAbilitySlice","updateBluetoothStatus:"+ bluetoothStatus);//開(kāi)關(guān)bluetoothSwitch.setChecked(isBluetoothEnabled());//狀態(tài)文本textBluetoothStatus.setText(getBluetoothStatusString(bluetoothStatus));
}/**
* 顯示環(huán)形進(jìn)度條
* 用定時(shí)器實(shí)現了一個(gè)進(jìn)度條,遺憾的是有一定的卡頓
*@paramisShow
*/privatevoidshowProgressBar(booleanisShow){
LogUtil.info("MainAbilitySlice","isShow:"+ isShow);
LogUtil.info("MainAbilitySlice","timer="+ timer);if(isShow){//顯示進(jìn)度條progressBar.setVisibility(Component.VISIBLE);if(timer==null){
timer =newTimer();
}if(timerTask==null){
timerTask=newTimerTask() {@Overridepublicvoidrun(){if(percent==10){
percent=1;
progressBar.setProgressValue(0);
}else{
percent++;
}
LogUtil.info("MainAbilitySlice","percent:"+ percent);
getContext().getUITaskDispatcher().asyncDispatch(newRunnable() {@Overridepublicvoidrun(){
progressBar.setProgressValue(percent*10);
}
});
}
};//timer.schedule(timerTask,0,1000);
}
}else{//隱藏進(jìn)度條progressBar.setProgressValue(0);
progressBar.setVisibility(Component.HIDE);if(timer!=null){
LogUtil.info("MainAbilitySlice","timer set null");
timer.cancel();
timerTask.cancel();
timer=null;
timerTask=null;
}
}
}/**
* 獲取藍牙狀態(tài)
*@return*/privatebooleanisBluetoothEnabled(){intstatus = BluetoothPlugin.getInstance(this).getBluetoothStatus();
LogUtil.info("isBluetoothEnabled","isBluetoothEnabled:"+status);returnstatus == BluetoothHost.STATE_ON;
}privateStringgetBluetoothStatusString(intbluetoothStatus){
LogUtil.info("bluetoothStatus","bluetoothStatus:"+bluetoothStatus);switch(bluetoothStatus) {caseBluetoothHost.STATE_OFF:caseBluetoothHost.STATE_BLE_TURNING_OFF://disabled 不可用returnConstants.BT_DISABLED;caseBluetoothHost.STATE_TURNING_ON://turning on 開(kāi)啟returnConstants.BT_TURNING_ON;caseBluetoothHost.STATE_ON://enabled 可用的returnConstants.BT_ENABLED;caseBluetoothHost.STATE_TURNING_OFF://turning off 關(guān)閉returnConstants.BT_TURNING_OFF;default://undefined 未定義returnConstants.BT_UNDEFINED;
}
}
c.實(shí)現BluetoothEventListener接口相關(guān)函數
@OverridepublicvoidupdateAvailableDevices(List list){//implements BluetoothEventListener//更新容器數據availableDevicesItemProvider.updateDeviceList(list);
}@OverridepublicvoidupdatePairedDevices(List list){//implements BluetoothEventListener//更新容器數據pairedDevicesItemProvider.updateDeviceList(list);
}@OverridepublicvoidnotifyBluetoothStatusChanged(intbluetoothStatus){
LogUtil.info("notifyBluetoothStatusChanged","bluetoothStatus:"+bluetoothStatus);//藍牙狀態(tài)改變事件通知updateBluetoothStatus(bluetoothStatus);
}@OverridepublicvoidnotifyDiscoveryState(booleanisStarted){//藍牙發(fā)現狀態(tài)的事件通知showProgressBar(isStarted);
}
d.實(shí)現CheckedStateChangedListener接口相關(guān)函數
@Overridepublic void onCheckedChanged(AbsButton absButton, boolean isChecked) {//開(kāi)關(guān)狀態(tài)改變事件觸發(fā)if(absButton.getId() == ResourceTable.Id_bt_switch && containerLists != null) {if(isChecked) {LogUtil.info("onCheckedChanged","enableBluetooth");//開(kāi)啟藍牙BluetoothPlugin.getInstance(this).enableBluetooth();containerLists.setVisibility(Component.VISIBLE);
}else{//關(guān)閉藍牙BluetoothPlugin.getInstance(this).disableBluetooth();containerLists.setVisibility(Component.HIDE);
}
}
}
e.實(shí)現ClickedListener接口相關(guān)函數,開(kāi)始發(fā)現藍牙
@Overridepublic void onClick(Component component) {LogUtil.info("MainAbilitySlice","startBtScan...");//開(kāi)始發(fā)現 掃描藍牙設備if(component.getId() == ResourceTable.Id_btn_start_discovery) {LogUtil.info("MainAbilitySlice","startBtScan...");BluetoothPlugin.getInstance(this).startBtScan();
}
}
——————
原創(chuàng ):老王丨鴻蒙hms開(kāi)發(fā)者高級認證持證人!提供鴻蒙關(guān)鍵技術(shù)解析及軟件開(kāi)發(fā)相關(guān)技術(shù)干貨~【公眾號:鴻蒙開(kāi)發(fā)者老王】