HarmonyOS4.0——IPC与RPC通信

基本概念

IPC(Inter-Process Communication)与RPC(Remote Procedure Call)用于实现跨进程通信,不同的是前者使用Binder驱动,用于设备内的跨进程通信,后者使用软总线驱动,用于跨设备跨进程通信。需要跨进程通信的原因是因为每个进程都有自己独立的资源和内存空间,其他进程不能随意访问不同进程的内存和资源,IPC/RPC便是为了突破这一点。IPC和RPC通常采用客户端-服务器(Client-Server)模型,在使用时,请求服务的(Client)一端进程可获取提供服务(Server)一端所在进程的代理(Proxy),并通过此代理读写数据来实现进程间的数据通信,更具体的讲,首先请求服务的(Client)一端会建立一个服务提供端(Server)的代理对象,这个代理对象具备和服务提供端(Server)一样的功能,若想访问服务提供端(Server)中的某一个方法,只需访问代理对象中对应的方法即可,代理对象会将请求发送给服务提供端(Server);然后服务提供端(Server)处理接受到的请求,处理完之后通过驱动返回处理结果给代理对象;最后代理对象将请求结果进一步返回给请求服务端(Client)。通常,Server会先注册系统能力(System Ability)到系统能力管理者(System Ability Manager,缩写SAMgr)中,SAMgr负责管理这些SA并向Client提供相关的接口。Client要和某个具体的SA通信,必须先从SAMgr中获取该SA的代理,然后使用代理和SA通信。下文直接使用Proxy表示服务请求方,Stub表示服务提供方。

约束与限制

  • 单个设备上跨进程通信时,传输的数据量最大约为1MB,过大的数据量请使用匿名共享内存。
  • 不支持在RPC中订阅匿名Stub对象(没有向SAMgr注册Stub对象)的死亡通知。
  • 不支持把跨设备的Proxy对象传递回该Proxy对象所指向的Stub对象所在的设备,即指向远端设备Stub的Proxy对象不能在本设备内进行二次跨进程传递。

使用建议

首先,需要编写接口类,接口类中必须定义消息码,供通信双方标识操作,可以有未实现的的方法,因为通信双方均需继承该接口类且双方不能是抽象类,所以此时定义的未实现的方法必须在双方继承时给出实现,这保证了继承双方不是抽象类。然后,需要编写Stub端相关类及其接口,并且实现AsObject方法及OnRemoteRequest方法。同时,也需要编写Proxy端,实现接口类中的方法和AsObject方法,也可以封装一些额外的方法用于调用SendRequest向对端发送数据。以上三者都具备后,便可以向SAMgr注册SA了,此时的注册应该在Stub所在进程完成。最后,在需要的地方从SAMgr中获取Proxy,便可通过Proxy实现与Stub的跨进程通信了。

相关步骤:

  • 实现接口类:需继承IRemoteBroker,需定义消息码,可声明不在此类实现的方法。
  • 实现服务提供端(Stub):需继承IRemoteStub或者RemoteObject,需重写AsObject方法及OnRemoteRequest方法。
  • 实现服务请求端(Proxy):需继承IRemoteProxy或RemoteProxy,需重写AsObject方法,封装所需方法调用SendRequest。
  • 注册SA:申请SA的唯一ID,向SAMgr注册SA。
  • 获取SA:通过SA的ID和设备ID获取Proxy,使用Proxy与远端通信

场景介绍

IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,包括Proxy和Stub运行在不同设备的情况。

接口说明

表1 Native侧IPC接口

类/接口方法功能说明
IRemoteBrokersptr AsObject()返回通信对象。Stub端返回RemoteObject对象本身,Proxy端返回代理对象。
IRemoteStubvirtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)请求处理方法,派生类需要重写该方法用来处理Proxy的请求并返回结果。
IRemoteProxyRemote()->SendRequest(code, data, reply, option)消息发送方法,业务的Proxy类需要从IRemoteProxy类派生,该方法用来向对端发送消息。

开发步骤

Native侧开发步骤

1. 添加依赖

SDK依赖:

#ipc场景
external_deps = ["ipc:ipc_single",
]#rpc场景
external_deps = ["ipc:ipc_core",
]

此外, IPC/RPC依赖的refbase实现在公共基础库下,请增加对utils的依赖:

    1.  external\_deps = \[2.    "c\_utils:utils",3.  \]

2. 定义IPC接口ITestAbility

SA接口继承IPC基类接口IRemoteBroker,接口里定义描述符、业务函数和消息码,其中业务函数在Proxy端和Stub端都需要实现。

    1.  #include "iremote\_broker.h"3.  //定义消息码4.  const int TRANS\_ID\_PING\_ABILITY = 56.  const std::string DESCRIPTOR = "test.ITestAbility";8.  class ITestAbility : public IRemoteBroker {9.  public:10.      // DECLARE\_INTERFACE\_DESCRIPTOR是必需的,入参需使用std::u16string;11.      DECLARE\_INTERFACE\_DESCRIPTOR(to\_utf16(DESCRIPTOR));12.      virtual int TestPingAbility(const std::u16string &dummy) \= 0; // 定义业务函数13.  };

3. 定义和实现服务端TestAbilityStub

该类是和IPC框架相关的实现,需要继承 IRemoteStub。Stub端作为接收请求的一端,需重写OnRemoteRequest方法用于接收客户端调用。

    1.  #include "iability\_test.h"2.  #include "iremote\_stub.h"4.  class TestAbilityStub : public IRemoteStub<ITestAbility> {5.  public:6.      virtual int OnRemoteRequest(uint32\_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override;7.      int TestPingAbility(const std::u16string &dummy) override;8.   };10.  int TestAbilityStub::OnRemoteRequest(uint32\_t code,11.   MessageParcel &data, MessageParcel &reply, MessageOption &option)12.  {13.      switch (code) {14.          case TRANS\_ID\_PING\_ABILITY: {15.              std::u16string dummy = data.ReadString16();16.              int result = TestPingAbility(dummy);17.              reply.WriteInt32(result);18.              return 0;19.          }20.          default:21.              return IPCObjectStub::OnRemoteRequest(code, data, reply, option);22.      }23.  }

4. 定义服务端业务函数具体实现类TestAbility

    1.  #include "iability\_server\_test.h"3.  class TestAbility : public TestAbilityStub {4.  public:5.      int TestPingAbility(const std::u16string &dummy);6.  }8.  int TestAbility::TestPingAbility(const std::u16string &dummy) {9.      return 0;10.  }

5. 定义和实现客户端 TestAbilityProxy

该类是Proxy端实现,继承IRemoteProxy,调用SendRequest接口向Stub端发送请求,对外暴露服务端提供的能力。

    1.  #include "iability\_test.h"2.  #include "iremote\_proxy.h"3.  #include "iremote\_object.h"5.  class TestAbilityProxy : public IRemoteProxy<ITestAbility> {6.  public:7.      explicit TestAbilityProxy(const sptr<IRemoteObject> &impl);8.      int TestPingAbility(const std::u16string &dummy) override;9.  private:10.      static inline BrokerDelegator<TestAbilityProxy> delegator\_; // 方便后续使用iface\_cast宏11.  }13.  TestAbilityProxy::TestAbilityProxy(const sptr<IRemoteObject> &impl)14.      : IRemoteProxy<ITestAbility>(impl)15.  {16.  }18.  int TestAbilityProxy::TestPingAbility(const std::u16string &dummy){19.      MessageOption option;20.      MessageParcel dataParcel, replyParcel;21.      dataParcel.WriteString16(dummy);22.      int error = Remote()->SendRequest(TRANS\_ID\_PING\_ABILITY, dataParcel, replyParcel, option);23.      int result = (error == ERR\_NONE) ? replyParcel.ReadInt32() : \-1;24.      return result;25.  }

6. SA注册与启动

SA需要将自己的TestAbilityStub实例通过AddSystemAbility接口注册到SystemAbilityManager,设备内与分布式的注册参数不同。

    1.  // 注册到本设备内2.  auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();3.  samgr->AddSystemAbility(saId, new TestAbility());5.  // 在组网场景下,会被同步到其他设备上6.  auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();7.  ISystemAbilityManager::SAExtraProp saExtra;8.  saExtra.isDistributed = true; // 设置为分布式SA9.  int result = samgr->AddSystemAbility(saId, new TestAbility(), saExtra);

7. SA获取与调用

通过SystemAbilityManager的GetSystemAbility方法可获取到对应SA的代理IRemoteObject,然后构造TestAbilityProxy即可。

    1.  // 获取本设备内注册的SA的proxy2.  sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();3.  sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(saId);4.  sptr<ITestAbility> testAbility = iface\_cast<ITestAbility>(remoteObject); // 使用iface\_cast宏转换成具体类型6.  // 获取其他设备注册的SA的proxy7.  sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();9.  // networkId是组网场景下对应设备的标识符,可以通过GetLocalNodeDeviceInfo获取10.  sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(saId, networkId);11.  sptr<TestAbilityProxy> proxy(new TestAbilityProxy(remoteObject)); // 直接构造具体Proxy

JS侧开发步骤

1. 添加依赖

    1.  import rpc from "@ohos.rpc"2.  import featureAbility from "@ohos.ability.featureAbility"

2. 绑定Ability

首先,构造变量want,指定要绑定的Ability所在应用的包名、组件名,如果是跨设备的场景,还需要绑定目标设备NetworkId(组网场景下对应设备的标识符,可以使用deviceManager获取目标设备的NetworkId);然后,构造变量connect,指定绑定成功、绑定失败、断开连接时的回调函数;最后,使用featureAbility提供的接口绑定Ability。

    1.  import rpc from "@ohos.rpc"2.  import featureAbility from "@ohos.ability.featureAbility"4.  let proxy = null5.  let connectId = null7.  // 单个设备绑定Ability8.  let want = {9.      // 包名和组件名写实际的值10.      "bundleName": "ohos.rpc.test.server",11.      "abilityName": "ohos.rpc.test.server.ServiceAbility",12.  }13.  let connect = {14.      onConnect:function(elementName, remote) {15.          proxy = remote16.      },17.      onDisconnect:function(elementName) {18.      },19.      onFailed:function() {20.          proxy = null21.      }22.  }23.  connectId = featureAbility.connectAbility(want, connect)25.  // 如果是跨设备绑定,可以使用deviceManager获取目标设备NetworkId26.  import deviceManager from '@ohos.distributedHardware.deviceManager'27.  function deviceManagerCallback(deviceManager) {28.      let deviceList = deviceManager.getTrustedDeviceListSync()29.      let networkId = deviceList\[0\].networkId30.      let want = {31.          "bundleName": "ohos.rpc.test.server",32.          "abilityName": "ohos.rpc.test.service.ServiceAbility",33.          "networkId": networkId,34.          "flags": 25635.      }36.      connectId = featureAbility.connectAbility(want, connect)37.  }38.  // 第一个参数是本应用的包名,第二个参数是接收deviceManager的回调函数39.  deviceManager.createDeviceManager("ohos.rpc.test", deviceManagerCallback)

3. 服务端处理客户端请求

服务端被绑定的Ability在onConnect方法里返回继承自rpc.RemoteObject的对象,该对象需要实现onRemoteMessageRequest方法,处理客户端的请求。

    1.  onConnect(want: Want) {2.      var robj:rpc.RemoteObject = new Stub("rpcTestAbility")3.      return robj4.  }5.  class Stub extends rpc.RemoteObject {6.      constructor(descriptor) {7.          super(descriptor)8.      }9.      onRemoteMessageRequest(code, data, reply, option) {10.          // 根据code处理客户端的请求11.          return true12.      }13.  }

4. 客户端处理服务端响应

客户端在onConnect回调里接收到代理对象,调用sendRequestAsync方法发起请求,在期约(JavaScript期约:用于表示一个异步操作的最终完成或失败及其结果值)或者回调函数里接收结果。

    1.  // 使用期约2.  let option = new rpc.MessageOption()3.  let data = rpc.MessageParcel.create()4.  let reply = rpc.MessageParcel.create()5.  // 往data里写入参数6.  proxy.sendRequestAsync(1, data, reply, option)7.      .then(function(result) {8.          if (result.errCode != 0) {9.              console.error("send request failed, errCode: " + result.errCode)10.              return11.          }12.          // 从result.reply里读取结果13.      })14.      .catch(function(e) {15.          console.error("send request got exception: " + e)16.      }17.      .finally(() => {18.          data.reclaim()19.          reply.reclaim()20.      })22.  // 使用回调函数23.  function sendRequestCallback(result) {24.      try {25.          if (result.errCode != 0) {26.              console.error("send request failed, errCode: " + result.errCode)27.              return28.          }29.          // 从result.reply里读取结果30.      } finally {31.          result.data.reclaim()32.          result.reply.reclaim()33.      }34.  }35.  let option = new rpc.MessageOption()36.  let data = rpc.MessageParcel.create()37.  let reply = rpc.MessageParcel.create()38.  // 往data里写入参数39.  proxy.sendRequest(1, data, reply, option, sendRequestCallback)
  1. 断开连接

    IPC通信结束后,使用featureAbility的接口断开连接。

    1.  import rpc from "@ohos.rpc"2.  import featureAbility from "@ohos.ability.featureAbility"3.  function disconnectCallback() {4.      console.info("disconnect ability done")5.  }6.  featureAbility.disconnectAbility(connectId, disconnectCallback)

远端状态订阅开发实例

IPC/RPC提供对远端Stub对象状态的订阅机制, 在远端Stub对象消亡时,可触发消亡通知告诉本地Proxy对象。这种状态通知订阅需要调用特定接口完成,当不再需要订阅时也需要调用特定接口取消。使用这种订阅机制的用户,需要实现消亡通知接口DeathRecipient并实现onRemoteDied方法清理资源。该方法会在远端Stub对象所在进程消亡或所在设备离开组网时被回调。值得注意的是,调用这些接口有一定的顺序。首先,需要Proxy订阅Stub消亡通知,若在订阅期间Stub状态正常,则在不再需要时取消订阅;若在订阅期间Stub所在进程退出或者所在设备退出组网,则会自动触发Proxy自定义的后续操作。

使用场景

这种订阅机制适用于本地Proxy对象需要感知远端Stub对象所在进程消亡,或所在设备离开组网的场景。当Proxy感知到Stub端消亡后,可适当清理本地资源。此外,RPC目前不提供匿名Stub对象的消亡通知,即只有向SAMgr注册过的服务才能被订阅消亡通知,IPC则支持匿名对象的消亡通知。

Native侧接口

接口名返回值类型功能描述
AddDeathRecipient(const sptr &recipient);bool订阅远端Stub对象状态。
RemoveDeathRecipient(const sptr &recipient);bool取消订阅远端Stub对象状态。
OnRemoteDied(const wptr &object);void当远端Stub对象死亡时回调。

参考代码

1.  #include "iremote\_broker.h"
2.  #include "iremote\_stub.h"5.  //定义消息码
6.  enum {
7.      TRANS\_ID\_PING\_ABILITY = 5,
8.      TRANS\_ID\_REVERSED\_MONITOR
9.  };11.  const std::string DESCRIPTOR = "test.ITestAbility";13.  class ITestService : public IRemoteBroker {
14.  public:
15.      // DECLARE\_INTERFACE\_DESCRIPTOR是必需的,入参需使用std::u16string;
16.      DECLARE\_INTERFACE\_DESCRIPTOR(to\_utf16(DESCRIPTOR));
17.      virtual int TestPingAbility(const std::u16string &dummy) \= 0; // 定义业务函数
18.  };20.  class TestServiceProxy : public IRemoteProxy<ITestAbility> {
21.  public:
22.      explicit TestAbilityProxy(const sptr<IRemoteObject> &impl);
23.      virtual int TestPingAbility(const std::u16string &dummy) override;
24.      int TestAnonymousStub();
25.  private:
26.      static inline BrokerDelegator<TestAbilityProxy> delegator\_; // 方便后续使用iface\_cast宏
27.  };29.  TestServiceProxy::TestServiceProxy(const sptr<IRemoteObject> &impl)
30.      : IRemoteProxy<ITestAbility>(impl)
31.  {
32.  }34.  int TestServiceProxy::TestPingAbility(const std::u16string &dummy){
35.      MessageOption option;
36.      MessageParcel dataParcel, replyParcel;
37.      dataParcel.WriteString16(dummy);
38.      int error = PeerHolder::Remote()->SendRequest(TRANS\_ID\_PING\_ABILITY, dataParcel, replyParcel, option);
39.      int result = (error == ERR\_NONE) ? replyParcel.ReadInt32() : \-1;
40.      return result;
41.  }

1.  #include "iremote\_object.h"3.  class TestDeathRecipient : public IRemoteObject::DeathRecipient {
4.  public:
5.      virtual void OnRemoteDied(const wptr<IRemoteObject>& remoteObject);
6.  }8.  void TestDeathRecipient::OnRemoteDied(const wptr<IRemoteObject>& remoteObject)
9.  {
10.  }

1.  sptr<IPCObjectProxy> object = new IPCObjectProxy(1, to\_utf16(DESCRIPTOR));
2.  sptr<IRemoteObject::DeathRecipient> deathRecipient (new TestDeathRecipient());// 构造一个消亡通知对象
3.  bool result = object\->AddDeathRecipient(deathRecipient); // 注册消亡通知
4.  result = object\->RemoveDeathRecipient(deathRecipient); // 移除消亡通知

JS侧接口

接口名返回值类型功能描述
addDeathRecipientboolean注册用于接收远程对象消亡通知的回调,增加proxy对象上的消亡通知。
removeDeathRecipientboolean注销用于接收远程对象消亡通知的回调。
onRemoteDiedvoid在成功添加死亡通知订阅后,当远端对象死亡时,将自动调用本方法。

参考代码

import FA from "@ohos.ability.featureAbility";
let proxy;
let connect = {onConnect: function(elementName, remoteProxy) {console.log("RpcClient: js onConnect called.");proxy = remoteProxy;},onDisconnect: function(elementName) {console.log("RpcClient: onDisconnect");},onFailed: function() {console.log("RpcClient: onFailed");}
};
let want = {"bundleName": "com.ohos.server","abilityName": "com.ohos.server.MainAbility",
};
FA.connectAbility(want, connect);
class MyDeathRecipient {onRemoteDied() {console.log("server died");}
}
let deathRecipient = new MyDeathRecipient();
proxy.addDeathRecipient(deathRecipient, 0);
proxy.removeDeathRecipient(deathRecipient, 0);

Stub感知Proxy消亡(匿名Stub的使用)

正向的消亡通知是Proxy感知Stub的状态,若想达到反向的死消亡通知,即Stub感知Proxy的状态,可以巧妙的利用正向消亡通知。如两个进程A(原Stub所在进程)和B(原Proxy所在进程),进程B在获取到进程A的Proxy对象后,在B进程新建一个匿名Stub对象(匿名指未向SAMgr注册),可称之为回调Stub,再通过SendRequest接口将回调Stub传给进程A的原Stub。这样一来,进程A便获取到了进程B的回调Proxy。当进程B消亡或B所在设备离开组网时,回调Stub会消亡,回调Proxy会感知,进而通知给原Stub,便实现了反向消亡通知。

注意:

反向死亡通知仅限设备内跨进程通信使用,不可用于跨设备。

当匿名Stub对象没有被任何一个Proxy指向的时候,内核会自动回收。

参考代码

1.  //Proxy
2.  int TestAbilityProxy::TestAnonymousStub()
3.  {
4.      MessageOption option;
5.      MessageParcel dataParcel, replyParcel;
6.      dataParcel.UpdateDataVersion(Remote());
7.      dataParcel.WriteRemoteObject(new TestAbilityStub());
8.      int error = Remote()->SendRequest(TRANS\_ID\_REVERSED\_MONITOR,dataParcel, replyParcel, option);
9.      int result = (error == ERR\_NONE) ? replyParcel.ReadInt32() : \-1;
10.      return result;
11.  }13.  //Stub15.  int TestAbilityStub::OnRemoteRequest(uint32\_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)
16.  {
17.      switch (code) {
18.          case TRANS\_ID\_REVERSED\_MONITOR: {
19.              sptr<IRemoteObject> obj = data.ReadRemoteObject();
20.              if (obj == nullptr) {
21.                  reply.WriteInt32(ERR\_NULL\_OBJECT);
22.                  return ERR\_NULL\_OBJECT;
23.              }
24.              bool result = obj->AddDeathRecipient(new TestDeathRecipient());
25.              result ? reply.WriteInt32(ERR\_NONE) : reply.WriteInt32(\-1);
26.              break;
27.          }
28.          default:
29.              break;
30.      }
31.      return ERR\_NONE;
32.  }

为了能让大家更好的学习鸿蒙 (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/671909.shtml

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

相关文章

【iOS ARKit】人形遮挡

人形遮挡简介 在 AR系统中&#xff0c;计算机通过对设备摄像头采集的图像进行视觉处理和组织&#xff0c;建立起实景空间&#xff0c;然后将生成的虚拟对象依据几何一致性原理嵌入到实景空间中&#xff0c;形成虚实融合的增强现实环境&#xff0c;再输出到显示系统中呈现给使用…

【数据结构】链表OJ面试题3(题库+解析)

1.前言 前五题在这http://t.csdnimg.cn/UeggB 后三题在这http://t.csdnimg.cn/gbohQ 记录每天的刷题&#xff0c;继续坚持&#xff01; 2.OJ题目训练 9. 给定一个链表&#xff0c;判断链表中是否有环。 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成…

假期最好的安排:读书学习成长

假期是每个人放松身心、充电学习的好时机。然而&#xff0c;很多人往往会将假期用于休闲娱乐&#xff0c;错失了充实自己的宝贵机会。本文将介绍如何通过读书学习成长&#xff0c;让你度过一个充实、有意义的假期。 一、知识拓展 假期是知识拓展的好时机。你可以选择一些与你…

嵌入式中《C++之旅》阅读笔记

constexpr constexpr的隐含意思是在编译阶段求值&#xff0c;对于一些求值操作&#xff0c;如果声明为constexpr&#xff0c;那么会编译器会尝试在编译阶段进行计算求值&#xff0c;如果求值成功&#xff0c;则用结果进行替换。 一个常用的例子是如下&#xff1a; constexpr…

深入解析 Spring 事务机制

当构建复杂的企业级应用程序时&#xff0c;数据一致性和可靠性是至关重要的。Spring 框架提供了强大而灵活的事务管理机制&#xff0c;成为开发者处理事务的首选工具。本文将深入探讨 Spring 事务的使用和原理&#xff0c;为大家提供全面的了解和实际应用的指导。 本文概览 首…

ORM模型类

模型 创建两个表 创建模型类 from django.db import models# Create your models here. class BookInfo(models.Model):name models.CharField(max_length10, uniqueTrue) # 书名pub_date models.DateField(nullTrue) # 发布时间read_count models.IntegerField(default…

【JSON2WEB】04 amis低代码前端框架介绍

1 什么是 amis amis 是一个低代码前端框架&#xff0c;它使用 JSON 配置来生成页面&#xff0c;可以减少页面开发工作量&#xff0c;极大提升效率。 看到amis一句话的介绍&#xff0c;感觉就是JSON2WEB要找的前端框架。 amis是百度开源的框架&#xff0c;毕竟是大厂&#xff0c…

即插即用、简单有效的大语言模型推荐算法!港大联合百度推出RLMRec

论文链接&#xff1a; https://arxiv.org/abs/2310.15950 论文代码&#xff1a; https://github.com/HKUDS/RLMRec 实验室主页&#xff1a; https://sites.google.com/view/chaoh/group-join-us?authuser0 TLDR 本文从互信息最大化的理论角度出发&#xff0c;通过引入文本信号…

使用No-SQL数据库支持连接查询用例的讨论

简介 在本文中&#xff0c;我们将简单介绍什么是No-SQL数据库。然后我们会讨论一种使用关系数据库比较容易实现的查询&#xff0c;即连接查询&#xff0c;怎么样使用No-SQL来实现。 什么是No-SQL数据库 与No-SQL数据库相对应的是传统的关系数据库&#xff08;RDBMS&#xff…

JRT监听程序

本次设计避免以往设计缺陷&#xff0c;老的主要为了保持兼容性&#xff0c;在用的设计就不好调了。 首先&#xff0c;接口抽象时候就不在给参数放仪器ID和处理类了&#xff0c;直接放仪器配置实体&#xff0c;接口实现想用什么属性就用什么属性&#xff0c;避免老方式要扩参数时…

java的excel列行合并模版

1.效果 2.模版 <tableborder"1"cellpadding"0"cellspacing"0"class"tablebor"id"TABLE"><tr align"center" class"bg217"><td style"background-color: #008000; color: #ffffff;p…

archlinux 使用 electron-ssr 代理 socks5

提前下载好 pacman 包 https://github.com/shadowsocksrr/electron-ssr/releases/download/v0.2.7/electron-ssr-0.2.7.pacman 首先要有 yay 和 aur 源&#xff0c;这个可以参考我之前的博客 虚拟机内使用 archinstall 安装 arch linux 2024.01.01 安装依赖 yay 安装的&#…

WebGL+Three.js入门与实战——绘制水平移动的点、通过鼠标控制绘制(点击绘制、移动绘制、模拟画笔)

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

JVM 性能调优 - 常用的垃圾回收器(6)

垃圾收集器 在 JVM(Java虚拟机)中,垃圾收集器(Garbage Collector)是负责自动管理内存的组件。它的主要任务是在程序运行过程中,自动回收不再使用的对象所占用的内存空间,以便为新的对象提供足够的内存。 JVM中的垃圾收集器使用不同的算法和策略来实现垃圾收集过程,以…

第4章——深度学习入门(鱼书)

第4章 神经网络的学习 本章的主题是神经网络的学习。这里所说的“学习”是指从训练数据中自动获取最优权重参数的过程。本章中&#xff0c;为了使神经网络能进行学习&#xff0c;将导入损失函数这一指标。而学习的目的就是以该损失函数为基准&#xff0c;找出能使它的值达到最…

一些常见的电源方案

开关电源&#xff1a; RM C 板&#xff1a;&#xff08;24V电压&#xff0c;10A电流&#xff09; SMBJ30CA&#xff1a;静电和浪涌保护(TVS/ESD) 一般使用NMOS管&#xff0c;因为PMOS管导通电阻与NMOS管比较会较大 模电非基础01——从一种常见的防反接&#xff0c;上电缓启…

Mysql大表添加字段失败解决方案

背景 最近遇到一个问题&#xff0c;需要在user用户表千万级别数据中添加两个字段&#xff0c;发现老是加不上去&#xff0c;一直卡死。表数据量不仅大&#xff0c;而且是一个热点表&#xff0c;访问频率特别高&#xff0c;而且该表的访问是在一个大事务中。加字段的时候一直在…

挑战杯 python 爬虫与协同过滤的新闻推荐系统

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; python 爬虫与协同过滤的新闻推荐系统 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;4分 该项目较为新颖&…

【人工智能】神奇的Embedding:文本变向量,大语言模型智慧密码解析(10)

什么是嵌入&#xff1f; OpenAI 的文本嵌入衡量文本字符串的相关性。嵌入通常用于&#xff1a; Search 搜索&#xff08;结果按与查询字符串的相关性排序&#xff09;Clustering 聚类&#xff08;文本字符串按相似性分组&#xff09;Recommendations 推荐&#xff08;推荐具有…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之RichText组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之RichText组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、RichText组件 鸿蒙&#xff08;HarmonyOS&#xff09;富文本组件&#xff0c;…