【HarmonyOS】鸿蒙应用低功耗蓝牙BLE的使用心得 (二)

【HarmonyOS】鸿蒙应用低功耗蓝牙BLE的使用心得 (二)

一、前言

目前鸿蒙应用的实现逻辑,基本都是参考和移植Android端来实现。针对BLE低功耗蓝牙来说,在鸿蒙化的实现过程中。我们发现了,鸿蒙独有的优秀点,也发现了不一致带来的问题。

并且因为鸿蒙系统还在迭代中,难免有一些bug的存在。目前BLE低功耗蓝牙主流程环节是没有问题的。

在这里插入图片描述

鸿蒙整体的实现流程与Android基本是一致。但是在BLE中有一个点需要特别注意。

当周边设备(外围设备)开启广播时,其实没有前置条件,不需要先开启GATT服务器和注册相关服务。但是有些业务逻辑是如此,导致我们开发的时候,惯性思维。实际上鸿蒙BLE在广播流程设计上,并不需要前置条件。

当周边设备持续发送广播,例如智能腕表发送广播,这时候中心设备(中央设备),例如手机才能通过BLE扫描去去识别到该设备。然后才是连接和信息传递的过程。

二、目前已知鸿蒙和Android不一致的实现点梳理:

1.安卓中设置低功耗蓝牙广播,其中有参数可以设置超时时间:
AdvertiseSettings.Builder.SetTimeout()。
但是对标鸿蒙中设置广播,文档中并没有该参数。鸿蒙的低功耗蓝牙广播,无法设置超时时间。

2.鸿蒙中的低功耗蓝牙广播设置对象,ble.AdvertiseSetting中只能设置txPower广播强度。设置广播的模式,对标Android的三个广播模式:

在均衡电源模式下执行蓝牙LE广播:
AdvertiseSettings#ADVERTISE_MODE_BALANCED

在低延迟,高功率模式下执行蓝牙LE广播:
AdvertiseSettings#ADVERTISE_MODE_LOW_LATENCY

在低功耗模式下执行蓝牙LE广播
:AdvertiseSettings#ADVERTISE_MODE_LOW_POWER

鸿蒙这边可以使用interval参数来设置广播频率
ADVERTISE_MODE_LOW_LATENCY(低延迟,广播间隔100ms)对应interval的值为 160
ADVERTISE_MODE_BALANCED(均衡模式,广播间隔250ms)对应interval的值为 400
ADVERTISE_MODE_LOW_POWER(低功耗,广播间隔1s)对应interval的值为 1600

三、BLE低功耗蓝牙DEMO项目示例参考:

此项目实际上是从官方文档基础上进行了扩展。文档所示例的样本代码比较割裂,只是针对API功能的讲解,在不知道BLE完成业务流程的基础上,我们是不清楚如何调用的。所以我们开玩笑的角度说,基本上当你会这个技术点了,官方文档也就能看懂了。

项目整个框架采用管理对象进行BLE功能的封装:

GattServermanager
作为GATT服务器(周边设备)的管理类,实现服务器的初始化和服务的注册。

GattClientManager
作为周边设备客户端管理类,实现链接和数据读写传递。

BleScanManager
作为BLE扫描管理类,实现扫描和结果的处理。

BleAdvertisingManager
作为BLE广播类,实现广播的处理,特征值和描述符的生成。

BLEMgr
作为总管理类,业务逻辑的感知层,业务直接调用BLEMgr实现低功耗蓝牙接口的调用。

在这里插入图片描述
Index.ets 启动页

import { CommonTextModifier } from '../common/CommonTextModifier'
import { BLEMgr } from '../mgr/BLEMgr';
import { promptAction } from '@kit.ArkUI';
import { PermissionsUtil } from '../utils/PermissionsUtil';

struct Index { isOpenBluetooth: boolean = false;private mBLEMgr: BLEMgr = new BLEMgr();txtModifier: CommonTextModifier = new CommonTextModifier()async aboutToAppear() {let isHave: boolean = await PermissionsUtil.requestPermission();if(isHave){this.isOpenBluetooth = this.mBLEMgr.getBluetoothState();}else{this.toSysSettingPage();}}private toSysSettingPage(){globalThis.sysContext.startAbility({bundleName: 'com.huawei.hmos.settings',abilityName: 'com.huawei.hmos.settings.MainAbility',// com.huawei.hmos.settings.AppInfoAbilityuri: 'application_info_entry', //application_settings   application_info_entryparameters: {pushParams: globalThis.sysContext.abilityInfo.bundleName // 应用包名com.example.tosettingdemo  'uiAbilityContext.abilityInfo.bundleName'}});}onClickStart = async ()=>{let isHave: boolean = await PermissionsUtil.requestPermission();if(isHave){this.mBLEMgr.startBluetooth((str: string)=>{let content: string = "";if (str == 'STATE_ON') {content = "蓝牙已开启";}else{content = "开启错误:" + str;}promptAction.showToast({message: content});});}else{this.toSysSettingPage();}}onClickClose = async ()=>{let isHave: boolean = await PermissionsUtil.requestPermission();if(isHave){this.mBLEMgr.closeBluetooth((str: string)=>{let content: string = "";if (str == 'STATE_OFF') {content = "蓝牙已关闭";}else{content = "关闭错误:" + str;}promptAction.showToast({message: content});});}else{this.toSysSettingPage();}}onClickStartAdv = ()=>{this.mBLEMgr.startAdvertising((advState: string)=>{let content: string = "";if(advState == "STARTED"){content = "广播已开启";}else{content = "广播错误:" + advState;}promptAction.showToast({message: content});});}onClickCloseAdv = ()=>{this.mBLEMgr.stopAdvertising((str: string)=>{promptAction.showToast({message: str});});}onClickStartScan = ()=>{this.mBLEMgr.startScan();}onClickCloseScan = ()=>{this.mBLEMgr.stopScan();}onClickStartServer = ()=>{this.mBLEMgr.registerServer((res: string)=>{promptAction.showToast({message: res});});}onClickCloseServer = ()=>{this.mBLEMgr.unRegisterServer((res: string)=>{promptAction.showToast({message: res});});} LineView(){Line().width("100%").height(px2vp(2)).backgroundColor(Color.Black).margin({top: px2vp(100)})}build() {Column() {Column(){Text(this.isOpenBluetooth ? "蓝牙状态: 已开启" : "蓝牙状态: 已关闭")Text("蓝牙设备名:" + this.mBLEMgr.getCurrentDeviceName())}Text("开启蓝牙").attributeModifier(this.txtModifier).onClick(this.onClickStart)Text("关闭蓝牙").attributeModifier(this.txtModifier).onClick(this.onClickClose)this.LineView()Text("启动服务").attributeModifier(this.txtModifier).onClick(this.onClickStartServer)Text("关闭服务").attributeModifier(this.txtModifier).onClick(this.onClickCloseServer)Text("开启广播").attributeModifier(this.txtModifier).onClick(this.onClickStartAdv)Text("关闭广播").attributeModifier(this.txtModifier).onClick(this.onClickCloseAdv)this.LineView()Text("开启扫描").attributeModifier(this.txtModifier).onClick(this.onClickStartScan)Text("关闭扫描").attributeModifier(this.txtModifier).onClick(this.onClickCloseScan)}.height('100%').width('100%')}
}

BLEMgr.ets

import bleAdvertisingManager from "./BleAdvertisingManager";
import bleScanManager from "./BleScanManager";
import { access } from '@kit.ConnectivityKit';
import gattServerManager from "./GattServerManager";
import { connection } from '@kit.ConnectivityKit';
import { BusinessError } from "@kit.BasicServicesKit";const TAG: string = "BLEMgr";export class BLEMgr {public getBluetoothState(): boolean {let state = access.getState();return this.getStateName(state) == "STATE_ON" ? true : false;}/*** 当前设备蓝牙设备名称*/public getCurrentDeviceName(){let localName: string = "";try {localName = connection.getLocalName();console.info(TAG, 'getCurrentDeviceName localName: ' + localName);} catch (err) {console.error(TAG, 'getCurrentDeviceName errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}return localName;}/*** 蓝牙广播状态* @param state* @returns*/public getAdvState(state: number){switch (state) {case 1:// 首次启动广播后的状态。return 'STARTED';case 2:// 临时启动广播后的状态。return 'ENABLED';case 3:// 临时停止广播后的状态。return 'DISABLED';case 4:// 完全停止广播后的状态。return 'STOPPED';default:return 'unknown status';}}/*** 蓝牙开启状态* @param state* @returns*/private getStateName(state: number): string {switch (state) {case 0:// 蓝牙已关闭。return 'STATE_OFF'; ;case 1:// 蓝牙正在打开。return 'STATE_TURNING_ON';case 2:// 蓝牙已打开。return 'STATE_ON';case 3:// 蓝牙正在关闭。return 'STATE_TURNING_OFF';case 4:// 蓝牙正在打开LE-only模式。return 'STATE_BLE_TURNING_ON';case 5:// 蓝牙正处于LE-only模式。return 'STATE_BLE_ON';case 6:// 蓝牙正在关闭LE-only模式。return 'STATE_BLE_TURNING_OFF';default:return 'unknown status';}}/*** 开启蓝牙*/public startBluetooth(callback: (str: string)=> void){try {access.enableBluetooth();} catch (err) {let errStr: string = JSON.stringify(err);console.info(TAG, 'startBluetooth enableBluetooth err: ' + errStr);callback(errStr);}access.on('stateChange', (data) => {let btStateMessage = this.getStateName(data);callback(btStateMessage);if (btStateMessage == 'STATE_ON') {access.off('stateChange');}console.info('bluetooth statues: ' + btStateMessage);});}/*** 关闭蓝牙*/public closeBluetooth(callback: (str: string)=> void){access.disableBluetooth();access.on('stateChange', (data) => {let btStateMessage = this.getStateName(data);callback(btStateMessage);if (btStateMessage == 'STATE_OFF') {access.off('stateChange');}console.info("bluetooth statues: " + btStateMessage);})}/*** 创建GATT服务器,注册服务*/public registerServer(callBack: (str: string)=> void){gattServerManager.registerServer(callBack);}/*** 删除服务,关闭GATT服务器*/public unRegisterServer(callBack: (str: string)=> void){gattServerManager.unRegisterServer(callBack);}/*** 开启广播*/public async startAdvertising(callBack: (state: string)=> void) {await bleAdvertisingManager.startAdvertising((state: number)=>{let advState: string = this.getAdvState(state);callBack(advState);});}/*** 关闭广播*/public async stopAdvertising(callBack: (str: string)=> void) {await bleAdvertisingManager.stopAdvertising(callBack);}/*** 开始扫描*/public startScan() {bleScanManager.startScan();}/*** 关闭扫描*/public stopScan() {bleScanManager.stopScan();}}

GattServermanager.ets

import { ble } from '@kit.ConnectivityKit';
import { constant } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';const TAG: string = 'GattServerManager';export class GattServerManager {gattServer: ble.GattServer | undefined = undefined;connectState: ble.ProfileConnectionState = constant.ProfileConnectionState.STATE_DISCONNECTED;myServiceUuid: string = '00001810-0000-1000-8000-00805F9B34FB';myCharacteristicUuid: string = '00001820-0000-1000-8000-00805F9B34FB';myFirstDescriptorUuid: string = '00002902-0000-1000-8000-00805F9B34FB'; // 2902一般用于notification或者indicationmySecondDescriptorUuid: string = '00002903-0000-1000-8000-00805F9B34FB';// 构造BLEDescriptorprivate initDescriptor(des: string, value: ArrayBuffer): ble.BLEDescriptor {let descriptor: ble.BLEDescriptor = {serviceUuid: this.myServiceUuid,characteristicUuid: this.myCharacteristicUuid,descriptorUuid: des,descriptorValue: value};return descriptor;}// 构造BLECharacteristicprivate initCharacteristic(): ble.BLECharacteristic {let descriptors: Array<ble.BLEDescriptor> = [];let descBuffer = new ArrayBuffer(2);let descValue = new Uint8Array(descBuffer);descValue[0] = 31;descValue[1] = 32;descriptors[0] = this.initDescriptor(this.myFirstDescriptorUuid, new ArrayBuffer(2));descriptors[1] = this.initDescriptor(this.mySecondDescriptorUuid, descBuffer);let charBuffer = new ArrayBuffer(2);let charValue = new Uint8Array(charBuffer);charValue[0] = 21;charValue[1] = 22;let characteristic: ble.BLECharacteristic = {serviceUuid: this.myServiceUuid,characteristicUuid: this.myCharacteristicUuid,characteristicValue: charBuffer,descriptors: descriptors};return characteristic;}// 1. 订阅连接状态变化事件public onGattServerStateChange() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}try {this.gattServer.on('connectionStateChange', (stateInfo: ble.BLEConnectionChangeState) => {let state = '';switch (stateInfo.state) {case 0:state = 'DISCONNECTED';break;case 1:state = 'CONNECTING';break;case 2:state = 'CONNECTED';break;case 3:state = 'DISCONNECTING';break;default:state = 'undefined';break;}console.info(TAG, 'onGattServerStateChange: device=' + stateInfo.deviceId + ', state=' + state);});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 2. server端注册服务时调用public registerServer(callBack: (str: string)=> void) {let characteristics: Array<ble.BLECharacteristic> = [];let characteristic = this.initCharacteristic();characteristics.push(characteristic);let gattService: ble.GattService = {serviceUuid: this.myServiceUuid,isPrimary: true,characteristics: characteristics};console.info(TAG, 'registerServer ' + this.myServiceUuid);try {this.gattServer = ble.createGattServer(); // 2.1 构造gattServer,后续的交互都需要使用该实例this.onGattServerStateChange(); // 2.2 订阅连接状态this.gattServer.addService(gattService);callBack("服务成功");} catch (err) {callBack("服务失败:" + JSON.stringify(err));console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 3. 订阅来自gattClient的读取特征值请求时调用public onCharacteristicRead() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}console.info(TAG, 'onCharacteristicRead');try {this.gattServer.on('characteristicRead', (charReq: ble.CharacteristicReadRequest) => {let deviceId: string = charReq.deviceId;let transId: number = charReq.transId;let offset: number = charReq.offset;console.info(TAG, 'receive characteristicRead');let rspBuffer = new ArrayBuffer(2);let rspValue = new Uint8Array(rspBuffer);rspValue[0] = 21;rspValue[1] = 22;let serverResponse: ble.ServerResponse = {deviceId: deviceId,transId: transId,status: 0, // 0表示成功offset: offset,value: rspBuffer};try {this.gattServer?.sendResponse(serverResponse);} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 4. 订阅来自gattClient的写入特征值请求时调用public onCharacteristicWrite() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}console.info(TAG, 'onCharacteristicWrite');try {this.gattServer.on('characteristicWrite', (charReq: ble.CharacteristicWriteRequest) => {let deviceId: string = charReq.deviceId;let transId: number = charReq.transId;let offset: number = charReq.offset;console.info(TAG, 'receive characteristicWrite: needRsp=' + charReq.needRsp);if (!charReq.needRsp) {return;}let rspBuffer = new ArrayBuffer(0);let serverResponse: ble.ServerResponse = {deviceId: deviceId,transId: transId,status: 0, // 0表示成功offset: offset,value: rspBuffer};try {this.gattServer?.sendResponse(serverResponse);} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 5. 订阅来自gattClient的读取描述符请求时调用public onDescriptorRead() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}console.info(TAG, 'onDescriptorRead');try {this.gattServer.on('descriptorRead', (desReq: ble.DescriptorReadRequest) => {let deviceId: string = desReq.deviceId;let transId: number = desReq.transId;let offset: number = desReq.offset;console.info(TAG, 'receive descriptorRead');let rspBuffer = new ArrayBuffer(2);let rspValue = new Uint8Array(rspBuffer);rspValue[0] = 31;rspValue[1] = 32;let serverResponse: ble.ServerResponse = {deviceId: deviceId,transId: transId,status: 0, // 0表示成功offset: offset,value: rspBuffer};try {this.gattServer?.sendResponse(serverResponse);} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 6. 订阅来自gattClient的写入描述符请求时调用public onDescriptorWrite() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}console.info(TAG, 'onDescriptorWrite');try {this.gattServer.on('descriptorWrite', (desReq: ble.DescriptorWriteRequest) => {let deviceId: string = desReq.deviceId;let transId: number = desReq.transId;let offset: number = desReq.offset;console.info(TAG, 'receive descriptorWrite: needRsp=' + desReq.needRsp);if (!desReq.needRsp) {return;}let rspBuffer = new ArrayBuffer(0);let serverResponse: ble.ServerResponse = {deviceId: deviceId,transId: transId,status: 0, // 0表示成功offset: offset,value: rspBuffer};try {this.gattServer?.sendResponse(serverResponse);} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 7. server端删除服务,不再使用时调用public unRegisterServer(callBack: (str: string)=> void) {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}console.info(TAG, 'unRegisterServer ' + this.myServiceUuid);try {this.gattServer.removeService(this.myServiceUuid); // 7.1 删除服务callBack("关闭服务");this.gattServer.off('connectionStateChange', (stateInfo: ble.BLEConnectionChangeState) => { // 7.2 取消订阅连接状态});this.gattServer.close() // 7.3 如果不再使用此gattServer,则需要close} catch (err) {callBack("关闭失败:" + JSON.stringify(err));console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}
}let gattServerManager = new GattServerManager();
export default gattServerManager as GattServerManager;

其余GattClientManager BleScanManager BleAdvertisingManager 类可以参考官方文档。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/467158.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第六十三周周报 GCN-CNNGA

文章目录 week 63 GCN-CNNGA摘要Abstract1. 题目2. Abstract3. 文献解读3.1 Introduction3.2 创新点 4. 网络结构4.1 数据分析4.2 混合深度学习框架的发展4.3 Mul4.4 CNN block4.5 GCN block4.6 GRU block4.7 注意力机制4.8 模型评估标准 5. 实验结果5.1 不同邻接矩阵的性能评价…

geoserver+postgis 最短路径规划常见问题记录

一、说明 具体实现步骤可参考其他博文&#xff0c;下面的这个博主写的很详细&#xff0c;步骤很清晰&#xff0c;注释也很全。geoserverpostgis 最短路径规划_geoserver 最短路径 存储过程-CSDN博客 本次文章&#xff0c;仅记录过程中需要注意的方面。 二、数据预处理 目标&a…

石油安全理论知识题库 考试宝在线刷题

一、单选题&#xff08;每题有4个选项&#xff0c;其中只有1个是正确的&#xff0c;将正确的选项号填入括号内&#xff09; 1.新修订的《中华人民共和国安全生产法》于&#xff08; &#xff09;正式实施。 A、2014年1月1日 B、2014年12月1日 C、2015年1月1日 D、2015年…

航空标志灯技术革新:提升夜间飞行安全

航空标志灯 随着低空飞行活动的增多和新型飞行器&#xff08;如无人机、热气球和直升机&#xff09;的普及&#xff0c;地面重要设施的安全面临前所未有的挑战。因此&#xff0c;航空标志灯的安装变得尤为重要。它们通过提升城市天际线、广袤乡村、跨河桥梁及电力网络等复杂地…

前后端交互接口(三)

前后端交互接口&#xff08;三&#xff09; 前言 前两集我们先做了前后端交互接口的约定以及浅浅的阅读了一些proto代码。那么这一集我们就来看看一些重要的proto代码&#xff0c;之后把protobuffer给引入我们的项目当中&#xff01; gateway.proto 我们来看一眼我们的网关…

机器学习—sigmoid的替代品

Z状结肠激活函数&#xff0c;在隐藏层中&#xff0c;在输出层&#xff0c;因为用逻辑回归建立神经网络&#xff0c;创造了大量的逻辑回归单元&#xff0c;但是如果你使用其他激活函数&#xff0c;神经网络可以变得更加强大。 以需求预测为例&#xff0c;给定价格&#xff0c;航…

数据分析-44-时间序列预测之深度学习方法TCN

文章目录 1 TCN简介1.1 网络示意图1.2 TCN优点2 模拟应用2.1 模拟数据2.2 预处理创建滞后特征2.3 划分训练集和测试集2.4 创建TCN模型2.5 模型训练2.6 模型预测3 自定义my_TCN模型3.1 my_TCN()函数3.2 训练模型3.3 模型预测3.4 改进4 参考附录1 TCN简介 时间卷积网络(TCN)是…

2024最新AI绘画系统软件(Midjourney)+GPT4文档分析总结,多模态识图理解,AI文生图/图生图/混图生图(图像混合)

一、前言 人工智能的快速发展已成为全球关注的焦点&#xff0c;其应用领域广泛&#xff0c;涵盖绘图、语言处理、视频编辑等。前沿技术不仅推动科技创新&#xff0c;还在艺术创作、内容生产和商业实践等方面展示出巨大潜力。例如&#xff0c;AI语言模型显著提升了内容自动生成、…

input file检验成功之后才可以点击

input file检验成功之后才可以点击 需求 在上传发票前需要先填写发票号&#xff0c;然后点击选择文件直接完成上传功能 实现思路 在没有输入发票号之前&#xff0c;file按钮不可用不能点击&#xff0c;输入之后&#xff0c;按钮可用&#xff0c;点击之后选择文件&#xff…

每日OJ题_牛客_AB31活动安排_区间贪心_C++_Java

目录 牛客_AB31活动安排_区间贪心 题目解析 C代码 Java代码 牛客_AB31活动安排_区间贪心 活动安排_牛客题霸_牛客网 描述&#xff1a; 给定n个活动&#xff0c;每个活动安排的时间为[ai,bi)。求最多可以选择多少个活动&#xff0c;满足选择的活动时间两两之间没有重合。 …

购物车-多元素组合动画css

学习 渡一课程 多元素组合动画 练习。 在我们开发购物车功能时&#xff0c;经常会有点击添加按钮&#xff0c;就会有一个小圆点掉进购物车的动画&#xff0c;如下图所示&#xff0c;今天我们通过css来实现。 首先实现多元素组合动画 直接上代码&#xff0c;可以复制到本地使用…

深度学习:bert模型

multi-headed机制 1、通过不同的head得到多个特征表达&#xff0c;一般8个head 2、将所有特征拼接在一起 3、降维&#xff0c;将Z0~Z7连接一个FC全连接实现降维 多层堆叠 位置编码 如何实现位置编码&#xff1f; &#xff08;1&#xff09;为每个时间步添加一个0-1范围内的数…

Android Glide动态apply centerCropTransform(),transition withCrossFade动画,Kotlin

Android Glide动态apply centerCropTransform(),transition withCrossFade动画,Kotlin import android.graphics.Bitmap import android.os.Bundle import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import com.bumptech.glide.Glide import …

Vue 组件通信-自定义事件(七)

一、组件自定事件概念 自己定义的事件&#xff0c;包含事件名&#xff0c;事件回调等&#xff0c;定义好之后去给组件使用。也是一种组件的通信方式&#xff0c;适用于子组件传递给父组件。 二、 组件自定义事件实现子传父 1、在父组件中给子组件绑定一个自定义事件 在子组件标…

计算机的错误计算(一百四十八)

摘要 本节探讨 MATLAB 中 附近数的正割函数与 附近数的余割函数的计算精度问题。 例1. 已知 计算 直接贴图吧&#xff1a; 另外&#xff0c;16位的正确值分别为 0.4105556037464873e9、0.3670813182326778e13、-0.2549029285657875e8 与 -0.1248777628817462e12&am…

《XGBoost算法的原理推导》12-14决策树复杂度的正则化项 公式解析

本文是将文章《XGBoost算法的原理推导》中的公式单独拿出来做一个详细的解析&#xff0c;便于初学者更好的理解。 我们定义一颗树的复杂度 Ω Ω Ω&#xff0c;它由两部分组成&#xff1a; 叶子结点的数量&#xff1b;叶子结点权重向量的 L 2 L2 L2范数&#xff1b; 公式(…

算法练习:1004. 最大连续1的个数 III

题目链接&#xff1a;1004. 最大连续1的个数 III。 题目要求&#xff0c;给定一个数组&#xff0c;这个数组里面只有0或1&#xff0c;然后计算有多少个连续的1的最大长度&#xff0c;同时给了一个条件就是&#xff0c;可以把k个0变成1&#xff0c;然后来计算长度。 暴力解法&a…

CCS下载安装(以12.3.0版本为例)

Code Composer Studio 是一个集成开发环境 (IDE)&#xff0c;简称CCS软件。支持 TI 的微控制器和嵌入式处理器产品的开发。Code Composer Studio 包含一整套用于开发和调试嵌入式应用程序的工具。 CCS9.3.0及以上版本不需要License文件&#xff0c;但是CCS旧版本比如CCS5.5.0需…

串口接收,不定长数据接收

###1.CUBE-MX配置串口 2.我采用串口中断接收&#xff0c;打开中断接口 3.时钟同样8倍频&#xff0c;1分频&#xff0c;使用内部时钟 打开串口中断 main() { __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 启用空闲中断__HAL_UART_ENABLE_IT(&huart1, UART_IT_R…

密码学知识点整理二:常见的加密算法

常用的加密算法包括对称加密算法、非对称加密算法和散列算法。 对称加密算法 AES&#xff1a;高级加密标准&#xff0c;是目前使用最广泛的对称加密算法之一&#xff0c;支持多种密钥长度&#xff08;128位、192位、256位&#xff09;&#xff0c;安全性高&#xff0c;加密效率…