HarmonyOS鸿蒙Next中uniapp-BLE checkPermissionsCode is 401 at uni_modules/Ble-network/utssdk/app-harmony/index.uts:110

HarmonyOS鸿蒙Next中uniapp-BLE checkPermissionsCode is 401 at uni_modules/Ble-network/utssdk/app-harmony/index.uts:110 【问题描述】:uniapp开发工具,蓝牙ble问题,运行报错:uniapp-BLE checkPermissionsCode is 401  at uni_modules/Ble-network/utssdk/app-harmony/index.uts:110

【问题现象】:

cke_2644.png

【版本信息】:

cke_3199.png

【复现代码】:

// 导入模块
import abilityAccessCtrl, { Permissions, PermissionRequestResult } from '@ohos.abilityAccessCtrl'
import common from '@ohos.app.ability.common';
import { bundleManager } from '@kit.AbilityKit'
import { access, ble } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';

// 常量定义
const TAG = "uniapp-BLE";
const permissions : Permissions[] = ['ohos.permission.ACCESS_BLUETOOTH'];

/**
 * 设置蓝牙设备上报事件回调
 */
export function setDeviceFindListener(fn : () => void) {
  if (typeof fn === 'function') {
    ble.on("BLEDeviceFind", fn);
	console.log("BLEDeviceFind", fn)
  }
}

/**
 * 开始蓝牙扫描
 */
export function startScan(filters : ble.ScanFilter[]) : void {
  try {
    ble.startBLEScan(filters);
  } catch (err) {
    console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
  }
}

/**
 * 停止蓝牙扫描
 */
export function stopScan() : void {
  ble.stopBLEScan();
}

// 校验当前是否已经授权
async function checkAccessToken(permission : Permissions) : Promise<abilityAccessCtrl.GrantStatus> {
  let atManager : abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  let grantStatus : abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;

  // 获取应用程序的accessTokenID
  let tokenId : number = 0;
  try {
    let bundleInfo : bundleManager.BundleInfo =
      await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
    let appInfo : bundleManager.ApplicationInfo = bundleInfo.appInfo
    tokenId = appInfo.accessTokenId;
  } catch (error) {
    const err : BusinessError = error as BusinessError;
    console.log(TAG, `Failed to get bundle info for self. Code is ${err.code}`);
  }

  // 校验应用是否被授予权限
  try {
    grantStatus = await atManager.checkAccessToken(tokenId, permission);
  } catch (error) {
    const err : BusinessError = error as BusinessError;
    console.log(TAG, `Failed to check access token. Code is ${err.code}`);
  }
  return grantStatus
}

async function requestPermissionsFromUser() {
  const context = getContext();  // 获取上下文
  const atManager = abilityAccessCtrl.createAtManager();

  try {
    const result = await atManager.requestPermissionsFromUser(context, permissions);
    if (result.code === 0) {  // 请求权限成功
      console.log('请求权限成功');
    }
  } catch (error) {
    console.error('请求权限失败', error);
  }
}

/**
 * 检查权限
 */
export async function checkPermissions() : Promise<boolean> {
	console.log(1111)
  let result : boolean = false;
  try {
	  console.log(2222)
    let grantStatus : abilityAccessCtrl.GrantStatus = await checkAccessToken(permissions[0]);
    if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
		console.log('蓝牙权限已授予');
      result = true;
    } else {
    console.log('蓝牙权限未授予');
    return false;
  }
    return result;
  } catch (error) {
    const err : BusinessError = error as BusinessError;
    console.log(TAG, 'checkPermissions' + `Code is ${err.code}`);
    return result;
  }
}

/**
 * 申请蓝牙权限(处理首次申请/已拒绝场景)
 * @returns boolean 权限是否授予
 */
async function requestBluetoothPermission(): Promise<boolean> {
  if (!context) {
    console.error(TAG, '上下文未初始化,请先调用setContext');
    return false;
  }

  try {
    // 第一步:申请权限(系统弹窗)
    const requestResult = await abilityAccessCtrl.createAtManager().requestPermissionsFromUser(
      context,
      permissions
    );

    // 解析申请结果
    const permissionItem = requestResult.authResults.find(item => item.permissionName === permissions[0]);
    if (permissionItem?.grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
      console.log('用户同意蓝牙权限');
      return true;
    }

    // 第二步:用户拒绝后,检查是否为"不再询问"状态
    const isNeverAskAgain = await checkNeverAskAgain(permissions[0]);
    if (isNeverAskAgain) {
      console.log('用户已拒绝并勾选不再询问,引导至设置页');
      // 跳转到应用权限设置页(鸿蒙4.0+支持)
      await openPermissionSettingPage();
      return false;
    } else {
      console.log('用户临时拒绝权限');
      return false;
    }
  } catch (error) {
    const err: BusinessError = error as BusinessError;
    console.log(TAG, `requestBluetoothPermission error: ${err.code}`);
    return false;
  }
}

async function openPermissionSettingPage() {
  try {
    // 获取当前应用包名
    const bundleName = context.applicationInfo.bundleName;
    // 跳转到系统设置的应用权限页面(鸿蒙4.0+ API)
    await bundleManager.startAbility(
      {
        want: {
          action: 'action.settings.APPLICATION_DETAILS_SETTINGS',
          uri: `bundle://${bundleName}`,
        },
        context: context
      }
    );
  } catch (error) {
    console.error(TAG, '打开设置页失败:', error);
    // 降级处理:提示用户手动打开设置
    prompt.showToast({ message: '请前往设置>应用>本应用>权限中开启蓝牙权限' });
  }
}


/**
 * 向用户请求BLE权限
 */
export async function reqPermissionsFromUser() {
  const context : common.UIAbilityContext = getContext() as common.UIAbilityContext;
  let atManager : abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  atManager.requestPermissionsFromUser(context, permissions).then((data : PermissionRequestResult) => {
    console.log('request Permissions success,请求权限成功');
  }).catch((error : BusinessError) => {
    console.log(TAG, `Failed to request permissions from user. Code is请求权限失败 ${error.code}`);
  });
}

// 将函数挂载到对象后导出对象
export const myBLE = {
  checkPermissions,
  reqPermissionsFromUser,
  setDeviceFindListener,
  startScan,
  stopScan
};

更多关于HarmonyOS鸿蒙Next中uniapp-BLE checkPermissionsCode is 401 at uni_modules/Ble-network/utssdk/app-harmony/index.uts:110的实战教程也可以访问 https://www.itying.com/category-93-b0.html

7 回复

开发者你好,

可参考如下uni-app开发HarmonyOS应用,调用BLE。

【背景知识】

【解决方案】

  1. 选中项目的uni_modules文件夹,右键新建uni_modules插件,选择uts插件-API插件,插件名称为:my-ble(名称可自定义)。
  2. 选中插件目录:my-ble/utssdk,右键新建app-harmony目录,在app-harmony中新建uts文件。
  3. 在目录my-ble/utssdk/app-harmony/index.uts中编写uts插件代码,以下为封装的调用低功耗蓝牙模块的uts插件代码示例。
// 导入模块
import abilityAccessCtrl, { Permissions, PermissionRequestResult } from '@ohos.abilityAccessCtrl'
import common from '@ohos.app.ability.common';
import { bundleManager } from '@kit.AbilityKit'
import { access, ble } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';

// 常量定义
const TAG = "uniapp-BLE";
const permissions : Permissions[] = ['ohos.permission.ACCESS_BLUETOOTH'];

/**
 * 设置蓝牙设备上报事件回调
 */
export function setDeviceFindListener(fn : () => void) {
  if (typeof fn === 'function') {
    ble.on("BLEDeviceFind", fn);
  }
}

/**
 * 开始蓝牙扫描
 */
export function startScan(filters : ble.ScanFilter[]) : void {
  try {
    ble.startBLEScan(filters);
  } catch (err) {
    console.error(`errCode: ${(err as BusinessError).code}, errMessage: ${(err as BusinessError).message}`);
  }
}

/**
 * 停止蓝牙扫描
 */
export function stopScan() : void {
  ble.stopBLEScan();
}

// 校验当前是否已经授权
async function checkAccessToken(permission : Permissions) : Promise<abilityAccessCtrl.GrantStatus> {
  let atManager : abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  let grantStatus : abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;

  // 获取应用程序的accessTokenID
  let tokenId : number = 0;
  try {
    let bundleInfo : bundleManager.BundleInfo =
      await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
    let appInfo : bundleManager.ApplicationInfo = bundleInfo.appInfo
    tokenId = appInfo.accessTokenId;
  } catch (error) {
    const err : BusinessError = error as BusinessError;
    console.error(TAG, `Failed to get bundle info for self. Code is ${err.code}`);
  }

  // 校验应用是否被授予权限
  try {
    grantStatus = await atManager.checkAccessToken(tokenId, permission);
  } catch (error) {
    const err : BusinessError = error as BusinessError;
    console.error(TAG, `Failed to check access token. Code is ${err.code}`);
  }
  return grantStatus
}

/**
 * 检查权限
 */
export async function checkPermissions() : Promise<boolean> {
  let result : boolean = false;
  try {
    let grantStatus : abilityAccessCtrl.GrantStatus = await checkAccessToken(permissions[0]);
    if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
      result = true;
    }
    return result;
  } catch (error) {
    const err : BusinessError = error as BusinessError;
    console.error(TAG, `checkPermissions Code is ${err.code}`);
    return result;
  }
}

/**
 * 向用户请求BLE权限
 */
export async function reqPermissionsFromUser() {
  const context : common.UIAbilityContext = getContext() as common.UIAbilityContext;
  let atManager : abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  atManager.requestPermissionsFromUser(context, permissions).then((data : PermissionRequestResult) => {
    console.info('request Permissions success');
  }).catch((error : BusinessError) => {
    console.error(TAG, `Failed to request permissions from user. Code is ${error.code}`);
  });
}

// 将函数挂载到对象后导出对象
export const myBLE = {
  checkPermissions,
  reqPermissionsFromUser,
  setDeviceFindListener,
  startScan,
  stopScan
};
  1. 在uni-app的vue文件中导入插件导出的myBLE对象,调用插件的方法使用系统蓝牙API,代码示例如下:
<template>
  <view class="content">
    <view class="btn-area">
      <button @click="requestBlePermission" type="default">申请BLE蓝牙权限</button>
      <button type="primary" @click="startBLEScan">使用BLE扫描设备</button>
      <button type="warn" @click="stopBLEScan">停止BLE扫描</button>

      <view v-if="devices.length > 0" class="device-list">
        <view class="device-title">发现的设备:</view>
        <view v-for="(device, index) in devices" :key="index" class="device-item" @click="connectToDevice(device)">
          <text class="device-name">{{device.name || '未知设备'}} ({{device.deviceId}})</text>
          <text class="device-rssi">RSSI: {{device.RSSI || '-'}}</text>
        </view>
      </view>
    </view>
  </view>
</template>
<script>
  import {
    myBLE,
  } from "../../uni_modules/my-ble";

  export default {
    data() {
      return {
        devices: [],
        isScanning: false
      }
    },
    methods: {
      // 请求蓝牙权限
      async requestBlePermission() {
        let hasPermission = await myBLE.checkPermissions();
        if (!hasPermission) {
          myBLE.reqPermissionsFromUser();
        }
      },
      // 设置BLE回调
      async startBLEScan() {
        const hasPermission = await myBLE.checkPermissions()
        if (hasPermission) {
          // 设置发现蓝牙设备时的回调
          myBLE.setDeviceFindListener((data) => {
            data.forEach((item) => {
              if (!this.devices.find(f => f.deviceId === item.deviceId)) {
                this.devices.push(item);
              }
            });
          })
          // 设置过滤条件
          let filters = null;
          // 开始扫描
          myBLE.startScan(filters);
        }
      },
      stopBLEScan() {
        myBLE.stopScan();
      }
    },
  }
</script>
<style>
  .content {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding-bottom: 50px;
  }

  .logo {
    height: 100rpx;
    width: 100rpx;
    margin-top: 100rpx;
    margin-left: auto;
    margin-right: auto;
    margin-bottom: 25rpx;
  }

  .text-area {
    display: flex;
    justify-content: center;
    margin-bottom: 20px;
  }

  .title {
    font-size: 36rpx;
    color: #8f8f94;
  }

  .btn-area {
    width: 100%;
    padding: 0 30px;
    margin-top: 20px;
  }

  .btn-area button {
    margin-bottom: 15px;
  }

  .result {
    margin-top: 15px;
    font-size: 14px;
    color: #333;
    text-align: center;
  }

  .device-list {
    width: 100%;
    margin-top: 15px;
    margin-bottom: 15px;
    border: 1px solid #eee;
    border-radius: 5px;
    padding: 10px;
  }

  .device-title {
    font-size: 16px;
    font-weight: bold;
    padding-bottom: 5px;
    border-bottom: 1px solid #eee;
    margin-bottom: 5px;
  }

  .device-item {
    padding: 10px;
    border-bottom: 1px solid #f5f5f5;
    display: flex;
    flex-direction: column;
  }

  .device-item:active {
    background-color: #f0f0f0;
  }

  .device-name {
    font-size: 14px;
    color: #333;
  }

  .device-rssi {
    font-size: 12px;
    color: #999;
    margin-top: 5px;
  }
</style>

更多关于HarmonyOS鸿蒙Next中uniapp-BLE checkPermissionsCode is 401 at uni_modules/Ble-network/utssdk/app-harmony/index.uts:110的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


老师,checkPermissions就是这个权限一些调不起来,不知道什么原因,能拉会给指导一下吗,

如何解决“此电脑”右键菜单中“显示更多选项”卡顿问题

问题描述

在 Windows 11 系统中,右键点击“此电脑”时,菜单会先显示“显示更多选项”,点击后才会出现完整菜单,这个过程有时会出现卡顿。

解决方法

方法一:修改注册表(推荐)

  1. Win + R 打开运行窗口,输入 regedit 并回车。
  2. 导航到以下路径:
    HKEY_CURRENT_USER\Software\Classes\CLSID
    
  3. 右键点击 CLSID,选择“新建” -> “项”,命名为:
    {86ca1aa0-34aa-4e8b-a509-50c905bae2a2}
    
  4. 在新建的项下,再次右键选择“新建” -> “项”,命名为:
    InprocServer32
    
  5. 完成后直接重启电脑,无需设置数值数据。

方法二:使用注册表脚本

  1. 新建一个文本文档,将以下内容复制进去:
    Windows Registry Editor Version 5.00
    
    [HKEY_CURRENT_USER\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32]
    @=""
    
  2. 将文件另存为 .reg 格式(例如:fix_context_menu.reg)。
  3. 双击运行该文件,确认添加注册表信息。
  4. 重启电脑生效。

方法三:恢复默认设置

如果需要恢复 Windows 11 默认的右键菜单,请执行以下步骤:

  1. 新建一个文本文档,复制以下内容:
    Windows Registry Editor Version 5.00
    
    [-HKEY_CURRENT_USER\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}]
    
  2. 将文件另存为 .reg 格式(例如:restore_default.reg)。
  3. 双击运行该文件,确认删除注册表项。
  4. 重启电脑生效。

注意事项

  • 修改注册表前建议先备份注册表或创建系统还原点。
  • 操作完成后必须重启电脑才能使更改生效。
  • 此方法适用于 Windows 11 系统,其他系统版本可能不适用。

最终开发者是权限未配置,问题解决,感谢,

检查在my-ble/utssdk/app-harmony/module.json5文件中,声明蓝牙权限。如下:

{
  "module": {
    "requestPermissions": [
      {
        // 蓝牙权限
        "name": "ohos.permission.ACCESS_BLUETOOTH",
        "reason": "$string:permission_ACCESS_BLUETOOTH",
        "usedScene": {
        }
      },
    ]  
  }
}

在HarmonyOS Next中,uni-app的BLE模块返回401权限错误。该错误表示应用缺少蓝牙相关权限配置。需在AppScope/app.json5的module字段中,正确声明ohos.permission.USE_BLUETOOTH和ohos.permission.DISCOVER_BLUETOOTH权限。同时,确保在module.json5中请求了这些权限。权限配置完成后,需重新编译应用。

这个错误码401表示权限检查失败。从你的代码来看,checkAccessToken函数在获取accessTokenId时可能返回了0,导致后续权限检查失败。

问题出现在checkAccessToken函数中:

  1. 当获取bundleInfo失败时,tokenId保持为初始值0
  2. 使用tokenId=0调用checkAccessToken会返回401错误

建议修改checkAccessToken函数,在获取tokenId失败时直接返回拒绝状态:

async function checkAccessToken(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> {
  let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  
  // 获取应用程序的accessTokenID
  let tokenId: number = 0;
  try {
    let bundleInfo: bundleManager.BundleInfo =
      await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
    let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo
    tokenId = appInfo.accessTokenId;
  } catch (error) {
    const err: BusinessError = error as BusinessError;
    console.log(TAG, `Failed to get bundle info for self. Code is ${err.code}`);
    // 获取tokenId失败,直接返回权限拒绝
    return abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;
  }

  // 如果tokenId为0,直接返回拒绝
  if (tokenId === 0) {
    console.log(TAG, 'Invalid tokenId (0)');
    return abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;
  }

  // 校验应用是否被授予权限
  try {
    return await atManager.checkAccessToken(tokenId, permission);
  } catch (error) {
    const err: BusinessError = error as BusinessError;
    console.log(TAG, `Failed to check access token. Code is ${err.code}`);
    return abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;
  }
}

另外,确保在调用蓝牙相关功能前,已经正确获取了上下文并申请了权限。错误码401通常表示参数无效,在这个场景下就是无效的tokenId。

回到顶部