HarmonyOS - 实现多设备协同开发实战教程~

前言

现在随着个人设备越来越多,越来越需要多个设备之间相互感知和连接,设备和设备之间可以相互联动,形成互联互通的场景,而搭载HarmonyOS的设备恰好可以满足这一点 。下面通过开发一个HarmonyOS的多端分布式表白应用来实现设备之间的相互联动。

项目介绍

H5页面可以实现一些比较特殊的页面效果,所以选择在应用中集成H5页面。应用可以将页面直接投放到附近其他HarmonyOS设备上,实现多端设备分布式显示,同时应用可以跨端控制,更新应用页面,形成多设备协同的效果。

下面是效果展示:

多设备协同原理

HarmonyOS 给应用开发者提供了一套在多个设备不同应用之间进行任务流转的API接口,实现设备协同需要关注 流转任务管理服务分布式任务调度

流转任务管理服务:在流转发起端,接受用户应用程序注册,提供流转入口、状态显示、退出流转等管理能力。

分布式任务调度:提供远程服务启动、远程服务连接、远程迁移等能力,并通过不同能力组合,支撑用户应用程序完成跨端迁移或多端协同的业务体验。

分布式安全:提供E2E的加密通道,为用户应用程序提供安全的跨端传输机制,保证“正确的人,通过正确的设备,正确地使用数据”。

分布式软总线:使用基于手机、平板、智能穿戴、智慧屏等分布式设备的统一通信基座,为设备之间的互联互通提供统一的分布式通信能力。

任务流转流程:设备A和设备B登录相同的华为账号,在同一网络下。设备A向设备B发起协同,应用程序需先向系统的流转任务管理服务 注册回调,获取到设备B的 DeviceId 等设备信息,设备A初始化分布式任务调度,通过设备DeviceId指定设备发起协同,设备B接收到协同请求,初始化分布式任务调度,启动对应的应用程序,把流转的状态上报给流转任务管理服务,流转任务管理服务返回流转结果,完成一次设备A到设备B的任务流转。

理解分布式软总线

分布式:指的是一种运行方式,简单来说就是任务可以在一个设备上运行,也可以多个设备连接起来一起运行,在多个设备中没有一个绝对的中心。

总线:简单了解一下“总线”的概念,在计算机系统中,各个部件之间传送信息的通道叫总线,外部设备通过相应的接口与总线相连接,组成了整个计算机系统。

分布式软总线:所以不难理解分布式软总线其实就是实现多个设备之间的连接,传递消息,实现任务多端运行的一种技术。在HarmonyOS中,底层已经帮我们实现了设备之间的组网、发现和连接,所以并不需要关心设备怎么通信,只需要调用底层封装好的接口,实现多设备协同就可以了。

实现步骤

实现分布式多设备协同,需要实现跨端启动应用、后台PA服务、分布式数据同步的功能,具体实现流程如下

一、跨设备启动应用

多设备协同实现的前提,需要在多端安装相同的应用,而在现实使用环境中,在多个设备中安装一个相同的应用还是一个比较麻烦的事。而HarmonyOS的原子化服务则不需要用户手动安装,由系统程序框架后台安装后即可使用,在HarmonyOS的服务中心以服务卡片的形式展示。

应用由原子化服务平台(Huawei Ability Gallery)管理和分发,只需要上传到原子化服务平台(Huawei Ability Gallery)即可,在多设备协同中,当设备A的应用向设备B的应用发起多端协同,如果设备B上没有安装对应服务,HarmonyOS会自动下载相关原子化服务,和A端的应用一起进行多端协同。

跨设备启动应用,也就是设备A上的应用可以拉起设备B上的应用。因为原子化服务应用免安装的特性,所以不用关心应用在多设备上的安装,只需实现跨设备启动应用即可。

1. 创建原子化服务

以原子化服务的形式创建项目, 原子化服务的特点是支持免安装,没有应用图标,只在 HarmanoyOS 服务中心以卡片的形式展现,支持跨端迁移和多端协同。
在新建项目时选择Atomic Service,创建一个原子化服务,同时打开Show in service center 开关,自动创建服务卡片,去掉TV的勾选状态,目前服务卡片不支持TV设备。

2. 创建HarmonyOS IDL接口

选择项目的module目录,点击鼠标右键,选择New>Idl File,如下图:

IDL是HarmonyOS的接口描述语言,可以实现IPC跨进程间通信,接口的提供方是服务端,客户端绑定应用的服务来进行交互。

在IDL中定义服务端接口,代码如下。

登录后复制

**interface** com.wealchen.multipoint.IMultiPointIdl {*//启动服务*void serviceStart([in] int code);*//发送消息*void sendMsg([in] int code,[in]int extras);*//停止服务*int serviceStop();
}

创建的IDL接口通过编译会在build > generated > source > Idl> 目录 debugrelease 下自动生成对应的接口类、桩类和代理类,如下图。

3. 实现后台接口服务

在HarmonyOS中,客户端绑定服务端后,获取到序列化的IRemoteObject对象,通过IRemoteObject对象实现客户端与服务端的通信,IRemoteObject在编译生成的桩类和代理类中已经完成了对象的创建和消息发送的实现,具体可查看上图中自动生成的代码。

创建一个Service后台服务MultiPointService,提供给客户端连接,并实现IDL中定义的接口,在接口实现中启动FA页面,接收客户端发送的消息,具体代码如下:

登录后复制

**public** **class** MultiPointService **extends** Ability {*// DESCRIPTOR 保持与 MultiPointIdlStub 和 MultiPointIdlProxy中的一致***private** static final String DESCRIPTOR = "com.wealchen.multipoint.IMultiPointIdl";**private** static final String TAG = MultiPointService.class.getName();@Override**protected** void onStart(Intent intent) {**super**.onStart(intent);}@Override**protected** IRemoteObject onConnect(Intent intent) {**return** **new** MultiPointRemoteObject(DESCRIPTOR);}**private** **class** MultiPointRemoteObject **extends** MultiPointIdlStub {**public** MultiPointRemoteObject(String descriptor) {**super**(descriptor);}@Override**public** void serviceStart(int _code) **throws** RemoteException {*//启动WebViewAbility页面*Intent intent = **new** Intent();Operation operation = **new** Intent.OperationBuilder().withBundleName(getBundleName()).withAbilityName(WebViewAbility.class.getName()).build();intent.setOperation(operation);intent.setParam(Constants.INTENT_STAR_PARAM, _code);startAbility(intent);LogUtil.debug(TAG, "serviceStart : start WebViewAbility " + _code);}@Override**public** void sendMsg(int _code, int _extras) **throws** RemoteException {LogUtil.debug(TAG, "sendMsg  code: " + _code + " extras: " + _extras);}@Override**public** int serviceStop() **throws** RemoteException {**return** 0;}}
}
4. 实现跨端启动应用

启动跨端应用需指定设备的DeviceId,通过设备管理器DeviceManager,可获取到当前同一网络下所有不是待机状态的设备,拿到DeviceId,DeviceName等设备信息,具体代码如下:

登录后复制

 *//获取同一网络下的设备*List<DeviceInfo> deviceInfos = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);**if** (deviceInfos == **null** && deviceInfos.size() == 0) {**return**;}**for** (int i = 0; i < deviceInfos.size(); i++) {String devId = deviceInfos.get(i).getDeviceId();String devName = deviceInfos.get(i).getDeviceName();}

通过DeviceId可以与指定设备的后台服务MultiPointService连接,实现长期交互,系统提供了connectAbility方法,实现跨设备PA连接与断开连接的能力,通过AbilitySlice的connectAbility接口跨设备连接到后台服务MultiPointService,发送消息到指定设备,设备在接收到消息之后可以执行相应的任务,从而实现跨设备应用任务的调度,连接服务实现代码如下:

登录后复制

 *//启动远端设备的FA***private** void startAbilityFa(String devicesId, String event, int localExtras) {Intent intent = **new** Intent();Operation operation =**new** Intent.OperationBuilder().withDeviceId(devicesId).withBundleName(getBundleName()).withAbilityName(MultiPointService.class.getName()).withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE).build();intent.setOperation(operation);boolean connectFlag = connectAbility(intent, **new** IAbilityConnection() {@Override**public** void onAbilityConnectDone(ElementName elementName, IRemoteObject remoteObject, int extra) {LogUtil.debug(TAG, "onAbilityConnectDone extra:" + extra);multiPointIdl = MultiPointIdlStub.asInterface(remoteObject);**try** {**if** (multiPointIdl != **null**) {**switch** (event) {**case** "serviceStart":multiPointIdl.serviceStart(0);**break**;**case** "sendMsg":multiPointIdl.sendMsg(1001, localExtras);**break**;}}} **catch** (RemoteException e) {LogUtil.error(TAG, "connect successful,but have remote exception");}}@Override**public** void onAbilityDisconnectDone(ElementName elementName, int extra) {LogUtil.debug(TAG, "extra " + extra + " elementName " + elementName.getAbilityName());disconnectAbility(**this**);}});**if** (connectFlag) {Toast.toast(**this**, "transmit successful!", TOAST_DURATION);} **else** {Toast.toast(**this**, "transmit failed!Please try again later.", TOAST_DURATION);}}

二、多端设备协同

多设备协同可以实现对跨端设备的控制,使用HarmonyOS的分布式数据服务,不同设备之间的数据可以实时更新并显示在界面上。

1. 分布式数据服务介绍

分布式数据服务是HarmonyOS为应用程序提供不同设备间同步数据的能力,通过使用分布式数据接口,应用程序将数据保存到分布式数据库中,不同设备之间可以通过分布式数据服务互相访问,支持数据在相同帐号的多端设备之间相互同步。

HarmonyOS的分布式数据库是一种NoSQL类型数据库,其数据以键值对key-value的形式进行组织、索引和存储,分布式数据服务包含五部分:

服务接口:提供专门的数据库创建、数据访问、数据订阅等接口给应用程序调用,接口支持KV数据模型,支持常用的数据类型,同时确保接口的兼容性、易用性和可发布性。

服务组件:负责服务内元数据管理、权限管理、加密管理、备份和恢复管理以及多用户管理等、同时负责初始化底层分布式DB的存储组件、同步组件和通信适配层。

存储组件:负责数据的访问、数据的缩减、事务、快照、数据库加密,以及数据合并和冲突解决等特性。

同步组件:连结了存储组件与通信组件,其目标是保持在线设备间的数据库数据一致性,包括将本地产生的未同步数据同步给其他设备,接收来自其他设备发送过来的数据,并合并到本地设备中。

通信适配层:负责调用底层公共通信层的接口完成通信管道的创建、连接,接收设备上下线消息,维护已连接和断开设备列表的元数据,同时将设备上下线信息发送给上层同步组件,同步组件维护连接的设备列表,同步数据时根据该列表,调用通信适配层的接口将数据封装并发送给连接的设备。

2. 创建分布式数据库

创建分布式数据库管理器,设置是否开启加密,自动同步功能,定义数据存储和查询方法,使用手动同步数据的方法实现数据的同步,详细代码代码如下:

登录后复制

**public** **class** MyKvStoreManager {**private** static final String TAG = MyKvStoreManager.class.getName();**private** static MyKvStoreManager instance = **null**;**private** static SingleKvStore singleKvStore = **null**;**private** static KvManager kvManager;**private** MyKvStoreManager(Context context) {createKVManager(context);}*/**** 创建分布式数据管理器*** \*/***public** static void createKVManager(Context context) {KvManagerConfig config = **new** KvManagerConfig(context);kvManager = KvManagerFactory.getInstance().createKvManager(config);**try** {Options options = **new** Options();options.setCreateIfMissing(**true**).setAutoSync(**false**).setEncrypt(**false**).setKvStoreType(KvStoreType.SINGLE_VERSION);String storeId = "remoteData";singleKvStore = kvManager.getKvStore(options, storeId);} **catch** (KvStoreException e) {LogUtil.error(TAG, "getKvStore:" + e.getKvStoreErrorCode());}}*/**** 存储数据*** \*/***public** static void putKvStore(String key, String value) {**if** (singleKvStore == **null**) {**return**;}**try** {singleKvStore.putString(key, value);} **catch** (KvStoreException e) {LogUtil.debug(TAG, "putString:" + e.getKvStoreErrorCode());}}*/**** 查询数据*** \*/***public** static String getKvStore(String key) {String valueString = "";**try** {valueString = singleKvStore.getString(key);} **catch** (KvStoreException e) {LogUtil.debug(TAG, "getString:" + e.getKvStoreErrorCode());}**return** valueString;}*/**** 手动同步数据*** \*/***public** static void syncData() {List<DeviceInfo> deviceInfoList = kvManager.getConnectedDevicesInfo(DeviceFilterStrategy.NO_FILTER);List<String> deviceIdList = **new** ArrayList<>();**for** (DeviceInfo deviceInfo : deviceInfoList) {deviceIdList.add(deviceInfo.getId());LogUtil.debug(TAG,"syncData getId: "+deviceInfo.getId());}**if** (deviceIdList.isEmpty()){**return**;}singleKvStore.sync(deviceIdList, SyncMode.PUSH_PULL);}*/**** 订阅数据同步结果*** \*/***public** static void dataChangeObserver(KvStoreObserver storeObserver) {singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_REMOTE, storeObserver);}
}
3. 订阅分布式数据变化

订阅分布式数据变化客户端应用需要实现KvStoreObserver接口,KvStoreObserver接口回调中返回数据更新的结果,根据返回结果应用执行相应的任务,或者更新界面,从而实现多端设备的同步,注意:回调方法中不允许更新UI组件等阻塞动作。

更新UI可以通过getMainTaskDispatcher().asyncDispatch()切换到UI主线程更新UI的,详细代码如下:

登录后复制

MyKvStoreManager.dataChangeObserver(**new** KvStoreObserver() {@Override**public** void onChange(ChangeNotification changeNotification) {LogUtil.debug(TAG, "dataChangeObserver");List<Entry> updateEntries = changeNotification.getUpdateEntries();**for** (int i = 0; i < updateEntries.size(); i++) {String key = updateEntries.get(i).getKey();String value = updateEntries.get(i).getValue().getString();LogUtil.debug(TAG, "dataChangeObserver key:" + key + " value:" + value);getMainTaskDispatcher().asyncDispatch(**new** Runnable() {@Override**public** void run() {**switch** (key) {**case** JS_ADD_NAME:*//添加名字*setJsAddName(value);**break**;**case** JS_DEL_NAME:*//删除名字*setJsDelName(value);**break**;**default**:updateListView(key, value);**break**;}}});}}
});

三、WebView与JavaScript的交互

1. 在WebView调用H5页面的JavaScript方法

更新H5页面时,WebView需要调用JavaScript,通过WebView.executeJs()传入H5页面中对应的方法名称,实现应用调用页面内的JavaScript方法,实现的代码如下:

登录后复制

 **private** void setJsAddName(String addName) {LogUtil.debug(TAG, "addName:" + addName);String add_js = String.format("javascript:addName('%s')", addName);webView.executeJs(add_js, **new** AsyncCallback<String>() {@Override**public** void onReceive(String msg) {*// 在此确认返回结果*LogUtil.debug(TAG, "executeJs onReceive:" + msg);}});}H5页面定义的方法:**function** addName(name){**var** tpl = document.getElementById('name').innerText**var** str = name +'\n'document.getElementById('name').innerText = tpl.concat(str)**return** name
}
2. H5页面调用应用中的方法

WebView组件通过注入回调对象到页面内容,实现在H5页面中调用应用中的方法。

H5页面调用应用中的方法:

登录后复制

**function** callToApp() {**if** (window.showDeviceList && window.showDeviceList.call) {**var** result = showDeviceList.call("showDeviceList");}
}应用中定义的方法:webView.addJsCallback("showDeviceList", **new** JsCallback() {@Override**public** String onCallback(String s) {getMainTaskDispatcher().asyncDispatch(**new** Runnable() {@Override**public** void run() {**switch** (s) {**case** "showDeviceList":**if** (!bottomDialog.isShowing()) {bottomDialog.show();}**break**;}}});**return** method;}
});

总结

应用实现了包括原子化服务、任务流转、跨端启动应用、分布式数据服务、多端设备协同、WebView组件与JavaScript交互的功能,其中包含了HarmonyOS系统才具有的功能。以上只是简单介绍了HarmonyOS特有功能的实现,当然HarmonyOS的特性不止这些,更多的功能和实现还需要开发者去探索。

为了能让大家更好的学习鸿蒙 (OpenHarmony) 开发技术,这边特意整理了《鸿蒙 (OpenHarmony)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙 (OpenHarmony)开发学习手册》

入门必看:https://qr21.cn/FV7h05

  1. 应用开发导读(ArkTS)
  2. ……

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

如何快速入门?:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. 构建第一个JS应用
  4. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

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

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

相关文章

python coding with ChatGPT 打卡第21天| 二叉树:最近公共祖先

相关推荐 python coding with ChatGPT 打卡第12天| 二叉树&#xff1a;理论基础 python coding with ChatGPT 打卡第13天| 二叉树的深度优先遍历 python coding with ChatGPT 打卡第14天| 二叉树的广度优先遍历 python coding with ChatGPT 打卡第15天| 二叉树&#xff1a;翻转…

hope实验室预备役第4次测试题解

目录 1.Foreign Exchange 2.Takahashi Gets Lost 3.Sasha and the Beautiful Array 4.Sasha and the Drawing 5.Sasha and the Casino 6.Only one of two 7.村村通 8.传送门 1.Foreign Exchange 原题链接 Sample 1 InputcopyOutputcopy 4 5 7 0 3 2 2 4 3 5 25 Sample…

如何解决AI场景下的冯诺伊曼陷阱?

既然聊到冯诺伊曼陷阱在AI场景中的解决方案&#xff0c;那咱们就来个脑洞大开的比喻。假设我们正在构建一个超级智能的大脑&#xff08;AI系统&#xff09;&#xff0c;它需要处理海量的学习资料和数据——就像一位知识狂魔每天要消化成吨的信息。 传统的冯诺伊曼架构下&#x…

【AI绘画】Stable Diffusion简介_stable diffusion变现

手把手教你入门绘图超强的AI绘画&#xff0c;用户只需要输入一段图片的文字描述&#xff0c;即可生成精美的绘画。给大家带来了全新保姆级教程资料包 &#xff08;文末可获取&#xff09; Stable Diffusion是2022年发布的深度学习文本到图像生成模型&#xff0c;它主要用于根据…

ncnn之三(补充):window环境下vs2022安装ncnn+protobuf

启动VS2022 下面的 x64 Native Tools Command Prompt for VS2022 protobuf git clone gitgithub.com:protocolbuffers/protobuf.git# 或者 下载 https://github.com/google/protobuf/archive/v3.11.2.zip cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPERelease -D…

HTML的特殊字符

HTML的特殊字符 有些特殊的字符在 html 文件中是不能直接表示的&#xff0c;例如: 空格&#xff0c;小于号(<)&#xff0c;大于号(>)&#xff0c;按位与(&)。 空格 示例代码&#xff1a; 运行结果&#xff1a; 由于html 标签就是用 < > 表示的&#xff0…

【快速搞定Webpack5】修改输出文件目录及自动清理上次打包文件(五)

介绍 默认情况下webpack打包后&#xff0c;我们的图片和js等文件都会被打包到dist目录下&#xff0c;文件多了混淆在一起一方面不利于文件的查找和管理&#xff0c;另外一方面看上去也不美观。 所以今天我们学习的内容就是控制输出后的文件进入不同的目录。 一、配置 新增4…

Java根据byte[]内容获取文件类型

输出啊 1、添加pom依赖 <properties><java.version>1.8</java.version><tika.version>1.26</tika.version><jmimemagic.version>0.1.5</jmimemagic.version></properties> <!-- 文件类型检测 --><dependency…

cpptrace 库介绍

cpptrace 是一个C 开源库&#xff0c; 简单试了下&#xff0c;我的发现和结论&#xff1a; 还不能通过 brew install 安装最方便方式仍然是 git clone 源码后&#xff0c; CMakeLists.txt 里&#xff0c; add_subdirectory() 方式使用: add_subdirectory("/path/to/cppt…

BioTech - 大型蛋白质复合物的组装流程 (CombFold)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/136187314 CombFold是用于预测大型蛋白质复合物结构的组合和分层组装算法&#xff0c;利用AlphaFold2预测的亚基之间的成对相互作用。CombFold的组…

MES系统的功能有哪些?

阅读本文&#xff0c;你将了解&#xff1a;一、MES系统是什么&#xff1b;二、MES系统的功能&#xff1b;三、MES系统的使用场景与案例分析&#xff1b;四、如何更高效地利用MES系统。 这是我们公司正在使用的MES系统&#xff0c;已为大家搭建好模板了&#xff0c;无需下载&…

日常问题:解决远程服务调用后采用fastjson进行类型字段转换大小写匹配问题

摘要 在使用 JSON 序列化工具时。或者是通过远程服务调用的过程中接收到其他服务的响应体时&#xff0c;我们常常遇到一个问题&#xff1a;默认情况下&#xff0c;字段的首字母会被转换成小写。但在某些场景下&#xff0c;我们需要返回的 JSON 格式的参数字段首字母保持大写。本…

C#面:怎样理解静态变量

静态变量是在类中声明的一种特殊类型的变量&#xff0c;它与类的实例无关&#xff0c;而是与整个类相关联。 静态变量在整个程序运行期间只有一个实例&#xff0c;无论创建了多少个类的实例。 可以通过类名直接访问静态变量&#xff0c;而不需要创建类的实例。 静态变量的特…

美团外卖商超销量数据

字段内容&#xff1a; shop_id varchar(50) NOT NULL, shop_id_str varchar(50) NOT NULL, shop_name varchar(400) DEFAULT NULL, shop_min_price varchar(10) DEFAULT NULL, shop_score varchar(10) DEFAULT NULL, shop_wm_score varchar(10) DEFAULT NU…

入侵检测系统

目录 入侵检测系统 两种入侵检测方法 1.基于特征的 IDS 2.基于异常的 IDS 入侵检测系统 入侵检测系统 IDS (Intrusion Detection System) 能够在入侵已经开始&#xff0c;但还没有造成危害或在造成更大危害前&#xff0c;及时检测到入侵&#xff0c;以便尽快阻止入侵&#…

2023充电桩行业:驶入快充时代,智能化引领未来发展

一、引言 随着全球对可持续发展的追求以及对新能源汽车市场的不断扩大&#xff0c;充电桩行业作为支撑电动汽车发展的重要基础设施&#xff0c;正在以前所未有的速度发展。2023年&#xff0c;充电桩行业已经驶入快充时代&#xff0c;智能化技术正引领着行业的未来发展。 二、…

【Vuforia+Unity】AR02-长方体物体识别

1.创建模型 选择多维长方体图&#xff0c;这个长方体是生活中的真实物体的拍摄图&#xff0c;提前把6个面拍摄好并裁剪干净。 官网创建模型https://developer.vuforia.com/targetmanager/project/targets?projectId0ddbb5c17e7f4bf090834650bbea4995&avfalse 设置长宽高…

Nginx 的基本介绍和使用

Nginx是一个高性能的HTTP和反向代理服务器&#xff0c;也是一个IMAP/POP3代理服务器。Nginx由俄罗斯的程序员Igor Sysoev开发&#xff0c;最初是为了解决C10k问题&#xff08;即同时处理10,000个网络连接的挑战&#xff09;而设计的。它现在是世界上使用最广泛的Web服务器之一&…

0220作业

C语言实现LED1闪烁 led.h #ifndef __LED_H__ #define __LED_H__//RCC寄存器封装 #define RCC_MP_AHB4_ENSETR (*(volatile unsigned int*)0x50000A28) //寄存器封装//GPIO寄存器封装 typedef struct{volatile unsigned int MODER; //00volatile unsigned int OTYPER; //04vol…

web移动端适配有哪些解决方案?每个方案的优缺点评估

移动端适配的解决方案主要包括以下几种&#xff1a; rem方案&#xff1a;这是最早被广泛采用和讨论的移动端适配方案。通过在页面上使用rem单位来控制页面元素的大小&#xff0c;实现在不同尺寸的设备上保持界面展示效果的一致性。这种方案的优点在于简单易用&#xff0c;但缺点…