HarmonyOS Sample 之 Bluetooth 传统蓝牙的使用 原创 精华

Buty9147
发布于 2021-9-15 12:02
浏览
12收藏

@toc

HarmonyOS Sample 之 Bluetooth 传统蓝牙的使用

1.介绍

本示例演示了如何对蓝牙进行基本操作,
传统蓝牙本机管理:
主要是针对蓝牙本机的基本操作,包括:打开和关闭蓝牙、设置和获取本机蓝牙名称、扫描和取消扫描周边蓝牙设备、获取本机蓝牙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 Sample 之 Bluetooth 传统蓝牙的使用-鸿蒙开发者社区

4.实例讲解

4.1.界面布局

布局 效果
HarmonyOS Sample 之 Bluetooth 传统蓝牙的使用-鸿蒙开发者社区 HarmonyOS Sample 之 Bluetooth 传统蓝牙的使用-鸿蒙开发者社区

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();
  }
}

5.完整代码

附件直接下载

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
Bluetooth.zip 1.31M 256次下载
已于2021-9-15 12:07:46修改
17
收藏 12
回复
举报
6条回复
按时间正序
/
按时间倒序
mb609898e2cfb86
mb609898e2cfb86

很详细的讲解,感谢分享。

回复
2021-9-15 14:33:17
Anzia
Anzia

注释好评!案例加楼主的文章一起学,事半功倍呀

1
回复
2021-9-16 10:34:03
Der_带鱼
Der_带鱼

感谢分享~

回复
2021-9-16 16:48:55
wx6232e85dc8dd4
wx6232e85dc8dd4

求解!为什么用真机调试点击  start discovery  会闪退呢?

回复
2022-3-27 15:39:09
Buty9147
Buty9147 回复了 wx6232e85dc8dd4
求解!为什么用真机调试点击 start discovery 会闪退呢?

如果已经部署到真机的话,结合deveco studio 底部的Hilog和Fault Log 再分析分析,我这边P40 Pro没问题呢

回复
2022-4-8 17:27:15
wx6375d47ab1c7e
wx6375d47ab1c7e

你好, 我想问一下配对成功之后怎么交互信息


回复
2022-12-7 15:03:39
回复
    相关推荐