Harmony Ble蓝牙App(二)连接与发现服务

Ble蓝牙App(二)连接与发现服务

  • 前言
  • 正文
    • 一、BlePeripheral回调
    • 二、连接和断连
    • 三、连接状态回调
    • 四、发现服务
    • 五、服务提供者
    • 六、显示服务
    • 七、源码

前言

  在上一篇中我们进行扫描设备的处理,本文中进行连接和发现服务的数据处理,运行效果图如下所示:
在这里插入图片描述

正文

  现在我们在ScanSlice扫描设备,选中一个设备进入MainAbilitySlice,下面要对选中的设备进行处理,首先我们来做连接。

一、BlePeripheral回调

  在之前我们写了一个BleCore,这里面是对扫描的封装,那么对于连接来说我们同样可以封装到这里,我们可以在BleCore中写一个BleDeviceCallback 类,继承自BlePeripheralCallback, 代码如下所示:

    private class BleDeviceCallback extends BlePeripheralCallback {/*** 连接状态改变* @param connectionState 状态码*/@Overridepublic void connectionStateChangeEvent(int connectionState) {}/*** 发现服务* @param status 状态*/@Overridepublic void servicesDiscoveredEvent(int status) {}}

  因为本文要做的事情是连接和发现服务,所以我们就先重写这两个方法,注意一点的是,蓝牙的操作都是在子线程中进行的,如果我们需要知道当前是否连接,则需要写一个接口用于回调到MainAbilitySlice中,在core包下新建一个BleCallback接口,代码如下所示:

public interface BleCallback {/*** 设备的所有信息** @param info 信息*/void deviceInfo(String info);/*** 连接状态** @param state true or false*/void connectionStateChange(boolean state);/*** 发现服务** @param services 服务列表*/void servicesDiscovered(List<GattService> services);
}

  接口中定义了三个方法,通过注释我们清晰的知道都是什么作用,这里着重介绍第一个函数,这个函数会显示设备各个时候的状态信息,从连接之后的所有动作,如果我们需要保存设备的操作日志的话,可以通过这个来进行处理保存。

然后回到BleCore,声明变量和设置接口回调的方法:

    private final BleDeviceCallback mBleDeviceCallback;private BleCallback bleCallback;private BlePeripheralDevice mDevice;private boolean mIsConnected;public BleCore(Context context) {...//蓝牙设备类mBleDeviceCallback = new BleDeviceCallback();}	public void setBleCallback(BleCallback bleCallback) {this.bleCallback = bleCallback;}private void deviceInfo(String info) {if (bleCallback != null) {bleCallback.deviceInfo(info);}}private void connectState(boolean state) {mIsConnected = state;if (bleCallback != null) {bleCallback.connectionStateChange(state);}}public void setDevice(BlePeripheralDevice device) {mDevice = device;}

这里就是对设备信息和连接做了一个处理,下面我们增加连接和断连的方法。

二、连接和断连

在BleCore中增加如下代码:

    public boolean isConnected() {return mIsConnected;}public void connect() {if (mDevice == null) return;deviceInfo("连接设备...");mDevice.connect(false, mBleDeviceCallback);}public void disconnect() {if (mDevice == null) return;deviceInfo("断开连接设备...");mDevice.disconnect();}

连接与断开连接,调用时会触发connectionStateChangeEvent()方法。

三、连接状态回调

下面修改这个方法的代码,如下所示:

        @Overridepublic void connectionStateChangeEvent(int connectionState) {String address = mDevice.getDeviceAddr();if (connectionState == ProfileBase.STATE_CONNECTED) {deviceInfo("连接成功:" + address);connectState(true);} else if (connectionState == ProfileBase.STATE_DISCONNECTED) {deviceInfo("断开连接成功:" + address);connectState(false);mDevice.close();}}

  在回调中,连接成功和断开连接都会有一个对应的状态码,通过状态回调到接口函数中,然后回到MainAbilitySlice中使用一下这个回调,首先我们修改一下ability_main.xml中的代码,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_parent"ohos:width="match_parent"ohos:alignment="center"ohos:background_element="$color:bg_color"ohos:orientation="vertical"><DirectionalLayoutohos:height="50vp"ohos:width="match_parent"ohos:alignment="vertical_center"ohos:background_element="$color:blue"ohos:orientation="horizontal"ohos:start_padding="12vp"><Textohos:height="match_content"ohos:width="match_content"ohos:text="操作设备"ohos:text_color="#FFF"ohos:text_font="HwChinese-medium"ohos:text_size="18fp"ohos:weight="1"/><Textohos:id="$+id:tx_disconnect"ohos:height="match_content"ohos:width="match_content"ohos:end_margin="6vp"ohos:padding="8vp"ohos:text="断开连接"ohos:text_color="#FFF"ohos:text_size="14fp"/></DirectionalLayout><Textohos:id="$+id:tx_device_info"ohos:height="match_content"ohos:width="match_parent"ohos:text="设备信息"ohos:padding="12vp"ohos:text_size="14fp"/><ListContainerohos:id="$+id:lc_service"ohos:height="match_parent"ohos:width="match_parent"/></DirectionalLayout>

  在XML中只增加了两个Text,分别用于断连和显示设备状态,下面我们写Slice之间的跳转,也就是从ScanSlice跳转到MainAbilitySlice。首先我们在MainAbility中增加action,代码如下所示:

public class MainAbility extends Ability {@Overridepublic void onStart(Intent intent) {...// add action for abilityaddActionRoute("action.main", MainAbilitySlice.class.getName());}
}

然后去config.json中增加配置,如下图所示:

在这里插入图片描述

然后我们回到ScanSlice中,在Item的点击事件中添加如下代码:

        //列表item点击监听lcDevice.setItemClickedListener((listContainer, component, position, id) -> {//设置设备bleCore.setDevice(mList.get(position).getDevice());Intent jumpIntent = new Intent();Operation operation = new Intent.OperationBuilder().withAction("action.main").withDeviceId("").withBundleName("com.llw.ble").withAbilityName("com.llw.ble.MainAbility").build();jumpIntent.setOperation(operation);startAbility(jumpIntent);});

  这里跳转的代码比较多,跳转之前设置了设备进去,这种方式也是官方推荐的方式。然后我们修改MainAbility中的代码,如下所示:

public class MainAbilitySlice extends AbilitySlice implements BleCallback {private static final String TAG = MainAbilitySlice.class.getSimpleName();private Text txDisconnect;private Text txDeviceInfo;private ListContainer lcService;private BleCore bleCore;@Overridepublic void onStart(Intent intent) {super.onStart(intent);super.setUIContent(ResourceTable.Layout_ability_main);txDisconnect = (Text) findComponentById(ResourceTable.Id_tx_disconnect);txDeviceInfo = (Text) findComponentById(ResourceTable.Id_tx_device_info);lcService = (ListContainer) findComponentById(ResourceTable.Id_lc_service);bleCore = BleApp.getBleCore();bleCore.setBleCallback(this);//连接设备bleCore.connect();txDisconnect.setClickedListener(component -> {if (bleCore.isConnected()) {bleCore.disconnect();} else {bleCore.connect();}});}@Overridepublic void deviceInfo(String info) {getUITaskDispatcher().asyncDispatch(() -> {LogUtils.LogD(TAG, info);txDeviceInfo.setText(info);});}@Overridepublic void connectionStateChange(boolean state) {getUITaskDispatcher().asyncDispatch(() -> txDisconnect.setText(state ? "断开连接" : "连接"));}@Overridepublic void servicesDiscovered(List<GattService> services) {}
}

  使用BleCoreconnect()方法进行连接设备,在onStart()方法中进行BleCore的赋值,然后设置Ble的回调,实现BleCallback接口,重写里面的函数,当连接成功之后会通过回调deviceInfo()得到设备状态,因为是子线程所以在ui线程中渲染UI。而connectionStateChange()函数,回调连接成功或者失败,如果成功则为ture,就显示txDisconnect控件,此时连接成功,点击这个txDisconnect就会断开连接,点击监听就在onStart()中写好了,下面我们运行一下看看效果。

在这里插入图片描述

从这个效果图来看,我们连接成功之后有状态,点击断开连接也会有状态改变,那么连接就写好了。

四、发现服务

  连接写好了,下面可以写发现服务了,我们可以在连接成功的处理中进行发现服务,下面我们修改一下BleDeviceCallback中的connectionStateChangeEvent()方法中的代码,如下图所示:

在这里插入图片描述

通过mDevice.discoverServices()进行发现服务的动作,在此之前通过deviceInfo()设置当前的动作状态,发现服务执行会触发servicesDiscoveredEvent()回调,在这个回调中我们可以回调到页面,修改代码如下所示:

        @Overridepublic void servicesDiscoveredEvent(int status) {if (status == BlePeripheralDevice.OPERATION_SUCC) {deviceInfo("发现" + mDevice.getServices().size() + "服务");if (bleCallback != null) {bleCallback.servicesDiscovered(mDevice.getServices());}}}

  在回调中设置发现服务的个数,然后回调,因为服务是多个的,那么下面我们就需要使用一个列表是装载服务,之前我们就已经在ability_main.xml中写好了。

五、服务提供者

  要显示服务列表数据,首先需要一个提供者,而提供者又需要一个item去渲染数据,下面我们在layout下创建一个item_service.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<DependentLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_content"ohos:width="match_parent"ohos:background_element="$color:white"ohos:bottom_margin="2vp"ohos:bottom_padding="8vp"ohos:end_padding="16vp"ohos:start_padding="16vp"ohos:top_padding="8vp"><Textohos:id="$+id:tx_service_name"ohos:height="match_content"ohos:width="match_content"ohos:text="服务"ohos:text_size="16fp"/><Buttonohos:id="$+id:tx_uuid_title"ohos:height="match_content"ohos:width="match_content"ohos:below="$id:tx_service_name"ohos:text="UUID:"ohos:text_color="$color:gray"ohos:text_size="16fp"ohos:top_margin="2vp"/><Buttonohos:id="$+id:tx_uuid"ohos:height="match_content"ohos:width="match_content"ohos:below="$id:tx_service_name"ohos:end_of="$id:tx_uuid_title"ohos:text="UUID"ohos:text_size="16fp"ohos:top_margin="2vp"/><Buttonohos:id="$+id:tx_service_info"ohos:height="match_content"ohos:width="match_content"ohos:below="$id:tx_uuid_title"ohos:text="PRIMARY SERVICE"ohos:text_color="$color:gray"ohos:text_size="16fp"ohos:top_margin="2vp"/></DependentLayout>

下面我们在ble包下新建一个BleUtils类,代码如下所示:

public class BleUtils {public static final String generic = "-0000-1000-8000-00805F9B34FB";public static String getServiceUUID(UUID uuid) {return "0x" + uuid.toString().substring(4, 8).toUpperCase();}/*** 获取蓝牙服务名称** @param uuid UUID*/public static String getServiceName(UUID uuid) {String targetUuid = getServiceUUID(uuid);switch (targetUuid) {case "0x1800":return "Generic Access service";case "0x1801":return "Generic Attribute service";case "0x1802":return "Immediate Alert service";case "0x1803":return "Link Loss service";case "0x1804":return "Tx Power service";case "0x1805":return "Current Time service";case "0x1806":return "Reference Time Update service";case "0x1807":return "Next DST Change service";case "0x1808":return "Glucose service";case "0x1809":return "Health Thermometer service";case "0x180A":return "Device Information service";case "0x180D":return "Heart Rate service";case "0x180E":return "Phone Alert Status service";case "0x180F":return "Battery service";case "0x1810":return "Blood Pressure service";case "0x1811":return "Alert Notification service";case "0x1812":return "Human Interface Device service";case "0x1813":return "Scan Parameters service";case "0x1814":return "Running Speed and Cadence service";case "0x1815":return "Automation IO service";case "0x1816":return "Cycling Speed and Cadence service";case "0x1818":return "Cycling Power service";case "0x1819":return "Location and Navigation service";case "0x181A":return "Environmental Sensing service";case "0x181B":return "Body Composition service";case "0x181C":return "User Data service";case "0x181D":return "Weight Scale service";case "0x181E":return "Bond Management service";case "0x181F":return "Continuous Glucose Monitoring service";case "0x1820":return "Internet Protocol Support service";case "0x1821":return "Indoor Positioning service";case "0x1822":return "Pulse Oximeter service";case "0x1823":return "HTTP Proxy service";case "0x1824":return "Transport Discovery service";case "0x1825":return "Object Transfer service";case "0x1826":return "Fitness Machine service";case "0x1827":return "Mesh Provisioning service";case "0x1828":return "Mesh Proxy service";case "0x1829":return "Reconnection Configuration service";case "0x183A":return "Insulin Delivery service";case "0x183B":return "Binary Sensor service";case "0x183C":return "Emergency Configuration service";case "0x183D":return "Authorization Control service";case "0x183E":return "Physical Activity Monitor service";case "0x183F":return "Elapsed Time service";case "0x1840":return "Generic Health Sensor service";case "0x1843":return "Audio Input Control service";case "0x1844":return "Volume Control service";case "0x1845":return "Volume Offset Control service";case "0x1846":return "Coordinated Set Identification service";case "0x1847":return "Device Time service";case "0x1848":return "Media Control service";case "0x1849":return "Generic Media Control service";case "0x184A":return "Constant Tone Extension service";case "0x184B":return "Telephone Bearer service";case "0x184C":return "Generic Telephone Bearer service";case "0x184D":return "Microphone Control service";case "0x184E":return "Audio Stream Control service";case "0x184F":return "Broadcast Audio Scan service";case "0x1850":return " Published Audio Capabilities service";case "0x1851":return "Basic Audio Announcement service";case "0x1852":return "Broadcast Audio Announcement service";case "0x1853":return "Common Audio service";case "0x1854":return "Hearing Access service";case "0x1855":return "Telephony and Media Audio service";case "0x1856":return "Public Broadcast Announcement service";case "0x1857":return "Electronic Shelf Label service";default:return "Unknown Service";}}
}

  这里需要说明一下蓝牙的UUID,蓝牙UUID(Universally Unique Identifier)是用于唯一标识蓝牙设备和服务的一种标识符。它是一个128位长的数字,在蓝牙通信中起到唯一标识的作用。蓝牙UUID按照标准分为两种类型:

  1. 16位UUID:这些UUID通常用于蓝牙标准定义的一些通用服务和特性。例如,设备名称服务的UUID是 00001800-0000-1000-8000-00805F9B34FB。

  2. 128位UUID:这些UUID通常用于自定义的服务和特性,以确保全球唯一性。可以自行生成一个128位的UUID作为自定义的服务或特性标识。例如,一个自定义的服务UUID可以是 0000XXXX-0000-1000-8000-00805F9B34FB,其中的 XXXX 部分可以是任意的16进制数字。

在蓝牙通信中,设备使用UUID来发布和查找服务以及识别特性。UUID是蓝牙设备之间进行通信时的重要标识,确保了设备和服务的唯一性。

那么getServiceName()中的键你就知道是什么意思了,0x1800就是16进制数字,而对应的值则是SIG定义的,可以参考这个文档:Assigned_Numbers.pdf。如果你的值找不到对应的,那说明它不是SIG规范的,你这个服务UUID就是自己公司自定义的。

下面我们写提供者,在provider包下新建一个ServiceProvider类,代码如下所示:

public class ServiceProvider extends BaseItemProvider {private final List<GattService> serviceList;private final AbilitySlice slice;public ServiceProvider(List<GattService> list, AbilitySlice slice) {this.serviceList = list;this.slice = slice;}@Overridepublic int getCount() {return serviceList == null ? 0 : serviceList.size();}@Overridepublic Object getItem(int position) {if (serviceList != null && position >= 0 && position < serviceList.size()) {return serviceList.get(position);}return null;}@Overridepublic long getItemId(int position) {return position;}@Overridepublic Component getComponent(int position, Component component, ComponentContainer componentContainer) {final Component cpt;ServiceHolder holder;GattService service = serviceList.get(position);if (component == null) {cpt = LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_item_service, null, false);holder = new ServiceHolder(cpt);//将获取到的子组件信息绑定到列表项的实例中cpt.setTag(holder);} else {cpt = component;// 从缓存中获取到列表项实例后,直接使用绑定的子组件信息进行数据填充。holder = (ServiceHolder) cpt.getTag();}holder.txServiceName.setText(BleUtils.getServiceName(service.getUuid()));holder.txUuid.setText(BleUtils.getServiceUUID(service.getUuid()));return cpt;}/*** 用于保存列表项的子组件信息*/public static class ServiceHolder {Text txServiceName;Text txUuid;public ServiceHolder(Component component) {txServiceName = (Text) component.findComponentById(ResourceTable.Id_tx_service_name);txUuid = (Text) component.findComponentById(ResourceTable.Id_tx_uuid);}}
}

这里的代码就是比较简单的,就是基本的写法,下面回到MainAbilitySlice中进行显示数据。

六、显示服务

首先声明变量:

    private final List<GattService> serviceList = new ArrayList<>();private ServiceProvider serviceProvider;

然后实现OnItemClickListener 接口

public class MainAbilitySlice extends AbilitySlice implements BleCallback, ListContainer.ItemClickedListener {

重写onItemClicked()方法,并且增加了一个showMsg,这个Toast的可定制化做的很多。

    @Overridepublic void onItemClicked(ListContainer listContainer, Component component, int position, long id) {showMsg(serviceList.get(position).getUuid().toString());}private void showMsg(String msg) {ToastDialog toastDialog = new ToastDialog(getContext());toastDialog.setSize(DirectionalLayout.LayoutConfig.MATCH_CONTENT, DirectionalLayout.LayoutConfig.MATCH_CONTENT);toastDialog.setDuration(2000);toastDialog.setText(msg);toastDialog.setAlignment(LayoutAlignment.CENTER);Text toastText = (Text) toastDialog.getComponent();if (toastText != null) {toastText.setMultipleLine(true);toastText.setTextSize(14, Text.TextSizeType.FP);toastText.setTextColor(Color.WHITE);toastText.setPadding(40, 20, 40, 20);ShapeElement toastBackground = new ShapeElement();toastBackground.setRgbColor(new RgbColor(24, 196, 124));toastBackground.setCornerRadius(60f);toastText.setBackground(toastBackground);}toastDialog.show();}

然后在onStart()方法中初始化服务提供者,代码如下所示:

    @Overridepublic void onStart(Intent intent) {...serviceProvider = new ServiceProvider(serviceList, this);lcService.setItemProvider(serviceProvider);lcService.setItemClickedListener(this);}

修改servicesDiscovered()方法,代码如下所示:

    @Overridepublic void servicesDiscovered(List<GattService> services) {getUITaskDispatcher().asyncDispatch(() -> {serviceList.clear();serviceList.addAll(services);serviceProvider.notifyDataChanged();});}

这里的写法其实和扫描设备哪里如出一辙,下面我们运行一下看看,什么效果。

在这里插入图片描述

七、源码

如果对你有所帮助的话,不妨 StarFork,山高水长,后会有期~

源码地址:HarmonyBle-Java

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

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

相关文章

Mysql数据库 18.Mysql SQL优化

SQL优化 一、插入优化 多条插入语句&#xff0c;影响执行效率 优化方案 1、批量插入&#xff1a; 在一条insert语句中多条数据&#xff0c;但是如果数据量过大&#xff0c;也不能完全使用一条语句语句&#xff0c;建议数据量为一次性插入1000条以下的数据 如果数据量多大&…

plantUML学习与实战

背景 在日常工作或者生活中&#xff0c;使用交互图来描述想法&#xff0c;往往相对于文字来说&#xff0c;可读性更高&#xff0c;同时一定程度上可以提高沟通效率&#xff0c;但是苦于&#xff0c;不想对一堆控件拖拖拉拉&#xff0c;本人就是一个很讨厌画图&#xff0c;但是…

【华为OD题库-036】跳格子2-java

题目 小明和朋友玩跳格子游戏&#xff0c;有n个连续格子组成的圆圈&#xff0c;每个格子有不同的分数&#xff0c;小朋友可以选择从任意格子起跳&#xff0c;但是不能跳连续的格子&#xff0c;不能回头跳&#xff0c;也不能超过一圈:给定一个代表每个格子得分的非负整数数组&am…

Python---把函数的返回值作为另外一个函数的参数

def test1():return 50def test2(num):print(num)# 1. 保存函数test1的返回值 result test1()# 2.将函数返回值所在变量作为参数传递到test2函数 test2(result) # 50

数据结构 栈和队列的应用

在昨天分享了有关栈和队列的基础知识和基本操作后&#xff0c;今天来分享一些有关栈和队列的应用 栈和队列的应用 删除字符串中的所有相邻重复项 #include <iostream> #include <stack> using namespace std; string remove(string S) {stack<char> charS…

MySql表中添加emoji表情

共五处需要修改。 语句执行修改&#xff1a; ALTER TABLE xxxxx CONVERT TO CHARACTER SET utf8mb4;

微型计算机原理MOOC题

一、8254 1.掉坑了&#xff0c;AL传到端口不意味着一定传到的是低位&#xff0c;要看控制字D5和D4&#xff0c;10是只写高位&#xff0c;所以是0A00.。。 2. 3. 4.待解决&#xff1a;

优化C++资源利用:探索高效内存管理技巧

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; &#x1f354;前言&#xff1a; 我们之前在C语言中学习过动态内存开辟&#xff0c;使用malloc、calloc与realloc进行开辟&#xff0c;使用free进行堆上内存的释放。进入C后对于动态内存开辟我们又有了新的内容new与dele…

CCC联盟——UWB MAC(一)

本文在前面已经介绍了相关UWB的PHY之后&#xff0c;重点介绍数字钥匙&#xff08;Digital Key&#xff09;中关于MAC层的相关实现规范。由于MAC层相应涉及内容比较多&#xff0c;本文首先从介绍UWB MAC的整体框架&#xff0c;后续陆续介绍相关的网络、协议等内容。 1、UWB MAC架…

真心的表扬与鼓励,胜过一万句说教

今天我想和大家分享一下&#xff0c;怎样跟孩子运用鼓励和表扬。我记得鲁道夫德雷克斯是阿德勒学派的心理学家&#xff0c;也是来自《孩子的挑战》一书的作者&#xff0c;他说孩子们需要鼓励&#xff0c;就像植物需要水&#xff0c;鼓励能让孩子知道自己做的事与自己是什么样的…

非自定义Bean注解开发Bean配置类的注解开发

目录 非自定义Bean注解开发 Bean配置类的注解开发 非自定义Bean注解开发 非自定义的Bean不能像自定义Bean使用Component进行管理&#xff0c;非自定义Bean要通过工厂的方式进行实例化&#xff0c;使用Bean标注方法即可&#xff0c;Bean的属性文beanName 如果Bean工厂方法需要参…

[23] 4K4D: Real-Time 4D View Synthesis at 4K Resolution

paper | proj | code 提出一种基于K-Planes的4D point cloud Representation&#xff1b;提出一种Hybrid appearance model&#xff0c;包含image blending model和SH model。其中&#xff0c;image blending model将3D点映射回原图中求得&#xff0c;SH model通过模型预测求得…

【工具栏】热部署不生效

目录 配置热部署&#xff1a; 解决热部署不生效&#xff1a; 首先检查&#xff1a; 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a; 配置热部署&#xff1a; https://blog.csdn.net/m0_67930426/article/details/133690559 解决热部署不…

Python中的解析器argparse

import argparse## 构造解析器 argparse.ArgumentParser() parse argparse.ArgumentParser(description"caculateing the area of rectangle")## 添加参数 .add_argument() parse.add_argument("--length",typeint,default20,helpThe length of rectangle…

【追求卓越09】算法--散列表(哈希表)

引导 通过前面几个章节的学习&#xff08;二分查找&#xff0c;跳表&#xff09;&#xff0c;我们发现想要快速查找某一个元素&#xff0c;首先需要将所有元素进行排序&#xff0c;再利用二分法思想进行查找&#xff0c;复杂度是O(logn)。有没有更快的查找方式呢&#xff1f; 本…

微软发布最新.NET 8长期支持版本,云计算、AI应用支持再强化

11 月 15 日开始的为期三天的 .NET Conf 在线活动的开幕日上&#xff0c;.NET 8作为微软的开源跨平台开发平台正式发布。.NET 团队着重强调云、性能、全栈 Blazor、AI 和 .NET MAUI 是.NET 8的主要亮点。.NET团队在 .NET Conf 2023 [1]活动开幕式上表示&#xff1a;“通过这个版…

nginx 模块相关配置及结构理解

文章目录 模块配置结构模块配置指令先看一下 ngx_command_t 结构一个模块配置的demo简单模块配置的案例演示 模块上下文结构模块的定义 模块配置结构 Nginx中每个模块都会提供一些指令&#xff0c;以便于用户通过配置去控制该模块的行为。 Nginx的配置信息分成了几个作用域(sc…

使用注解的AOP编程

使用注解的AOP编程 当注解没有参数时 当使用注解进行面向切面编程&#xff08;AOP&#xff09;时&#xff0c;你可以按照以下步骤来实现&#xff1a; 步骤&#xff1a; 1. 创建自定义注解&#xff1a; 首先&#xff0c;创建自定义的注解&#xff0c;以便在代码中标记需要进…

Excel换不了行怎么解决?

方法一: 使用Alt Enter键 在Excel中&#xff0c;输入文字时按下回车键&#xff0c;光标将会移到下一个单元格&#xff0c;如果想要换行&#xff0c;可以尝试使用Alt Enter键。具体操作如下: 1.在单元格中输入文字; 2.想要换行时&#xff0c;在需要换行的位置按下Alt Enter键; 3…

延时任务定时发布,基于 Redis 与 DB 实现

目录 1、什么是延时任务&#xff0c;分别可以使用哪些技术实现&#xff1f; 1.2 使用 Redis 和 DB 相结合的思路图以及分析 2、实现添加任务、删除任务、拉取任务 3、实现未来数据的定时更新 4、将数据库中的任务数据&#xff0c;同步到 Redis 中 1、什么是延时任务&#xff…