OpenHarmony 实战开发——分布式硬件管理详解

前言

分布式硬件是 OpenHarmony 提供的一个分布式能力,能将多种设备组成一个“超级终端”,使用户根据现实需要和硬件能力,选择合适的硬件提供服务,灵活运用硬件进行资源分配和按需组合,充分发挥各类硬件设备的能力,达到使用上的最佳效果。

介绍

概念说明

什么是分布式硬件子系统?

OpenHarmony 通过增强原来的分布式设备虚拟化能力,将多台设备的各种硬件资源,如屏幕、相机、扩音器、键盘、传感器及存储器等予以抽象,形成“超级虚拟终端”内的统一硬件资源池,并支持硬件资源的按需分配和重新组合,真正实现了硬件资源的全局调用。应用需要使用哪种分布式的硬件能力,只需要访问对应的服务即可;并且由硬件资源池提供的多种硬件资源可以同时被不同的应用访问,实现“一对多”的硬件资源访问

分布式硬件有哪些类型?

有相机,屏幕,麦克风,扩音器,gps,各类传感器,cpu,内存,存储器,键盘,鼠标等

硬件如何接入分布式硬件子系统?

各硬件设备通过 Wi-Fi、网线、移动网络等方式接入网络,分布式硬件子系统通过设备管理模块的上线监听回调监听到硬件上线,添加硬件驱动并通知硬件管理框架,通过接口执行与本地硬件相同的管理

分布式硬件子系统有哪些特点?

支持周边设备发现,硬件热插拔,子部件管理,PIN 码设备认证等

分布式硬件连接示意图

OpenHarmony 分布式硬件子系统为 OpenHarmony 提供了一系列硬件的分布式管理接口。 ** 分布式硬件管理框架(distributedhardwarefwk)是为分布式硬件子系统提供信息管理能力的部件。分布式硬件管理框架为分布式硬件子系统提供统一的硬件接入、查询和使能等能力。** ** DeviceManager 组件在 OpenHarmony 上提供账号无关的分布式设备的认证组网能力,并为开发者提供了一套用于分布式设备间监听、发现和认证的接口。** ** 分布式相机(distributedcamera)是多个设备的相机同时协同使用的能力。分布式相机组件是为分布式硬件子系统提供这一能力的组件。本组件不直接对接应用,只向分布式硬件框架子系统提供 C++ 接口。应用可以通过相机框架的接口使用分布式相机组件操作其他设备的 Camera,使用方式与本地相机一致。** ** 分布式屏幕(distributedscreen)是一种屏幕虚拟化能力,支持用户指定组网认证过的其他 OpenHarmony 设备的屏幕作为 Display 的显示区域。在分布式硬件子系统中,分布式屏幕组件提供跨设备屏幕能力调用,为 OpenHarmony 操作系统提供系统投屏、屏幕镜像、屏幕分割等能力的实现。**

构成组件及其功能

子模块名称 涉及仓库 代码目录 功能
distributedhardwarefwk https://gitee.com/openharmony/distributed_hardware_fwk foundation/distributedhardware/distributedhardwarefwk 提供信息管理能力,包括硬件接入、查询和使能等能力
devicemanager https://gitee.com/openharmony/device_manager foundation/distributedhardware/devicemanager 提供账号无关的分布式设备的认证组网能力,并为开发者提供一套用于分布式设备间监听,发现和认证的接口
distributedcamera https://gitee.com/openharmony/distributed_camera foundation/distributedhardware/distributedcamera 是为分布式硬件子系统提供多个设备的相机同时协同使用的能力的组件
distributedscreen https://gitee.com/openharmony/distributed_screen foundation/distributedhardware/distributedscreen 是一种屏幕虚拟化的能力,支持用户指定组网认证过的其他 OpenHarmony 设备的屏幕作为 Display 的显示区域。在分布式硬件子系统中,分布式屏幕组件提供跨设备屏幕能力调用,为 OpenHarmony 操作系统提供系统投屏,屏幕镜像,屏幕分割等能力的实现

硬件类型:

//foundation/distributedhardware/distributed_hardware_fwk/common/utils/include/device_type.h
enum class DHType : uint32_t {
    UNKNOWN = 0x0,            // unknown device
    CAMERA = 0x01,            // Camera
    MIC = 0x02,               // Mic
    SPEAKER = 0x04,           // Speaker
    DISPLAY = 0x08,           // Display
    GPS = 0x10,               // GPS
    INPUT = 0x20,             // Key board
    HFP = 0x40,               // HFP External device
    A2D = 0x80,               // A2DP External device
    VIRMODEM_MIC = 0x100,     // Cellular call MIC
    VIRMODEM_SPEAKER = 0x200, // Cellular call Speaker
    MAX_DH = 0x80000000
};

各组件说明

分布式硬件管理框架

架构图说明:

  • 硬件接入管理(AccessManager):硬件接入管理模块对接设备管理(DeviceManger)子系统,用于处理设备的上下线事件响应。
  • 硬件资源管理(ResourceManager):对接分布式数据服务,用于存储信任体系内,本机和周边设备同步过来的设备硬件信息。
  • 分布式硬件部件管理(ComponentManager):对接各分布式硬件实例化的部件,实现对分布式硬件的动态加载和使能/去使能等操作。
  • 本地硬件信息管理(LocalHardwareManager):用于采集本地硬件信息,并通过 ResourceManager 进行硬件信息的持久化存储;同时,通过对接硬件驱动,用于感知本地硬件的插拔等操作,感知是否新增或移除可用硬件,将动态变化的硬件设备也纳入分布式硬件管理。
  • 部件加载管理(ComponentLoader):用于解析部件配置文件,按需加载部件驱动的实现 so,获取驱动外部接口函数句柄以及实现版本,供其他业务使用。
  • 版本管理(VersionManager):用于管理超级终端内,各个设备的分布式硬件平台和分布式硬件部件的版本号,供分布式硬件业务各个部件业务使用。
  • 设备管理(DeviceManager):在 OpenHarmony 上提供账号无关的分布式设备的认证组网能力,并为开发者提供了一套用于分布式设备间监听、发现和认证的接口。

流程说明

  1. 设备开机启动 系统拉起分布式硬件管理框架的 SA 服务(DistributedHardwareService::DistributedHardwareService(int32_t saId, bool runOnCreate):SystemAbility(saId, runOnCreate)),AccessManager 会进行初始化,AccessManager 初始化 DeviceManger 并且注册上下线监听。

//foundation/distributedhardware/distributed_hardware_fwk/services/distributedhardwarefwkservice/src/distributed_hardware_service.cpp
void DistributedHardwareService::OnStart()
{
    DHLOGI("DistributedHardwareService::OnStart start");
    HiSysEventWriteMsg(DHFWK_INIT_BEGIN, OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
        "dhfwk sa start on demand.");

    if (state_ == ServiceRunningState::STATE_RUNNING) {     //判断分布式硬件服务的运行状态是否为running状态
        DHLOGI("DistributedHardwareService has already started.");
        return;
    }
    if (!Init()) {      //分布式硬件服务初始化
        DHLOGE("failed to init DistributedHardwareService");
        return;
    }
    state_ = ServiceRunningState::STATE_RUNNING;    //设置分布式硬件服务的运行状态
    DHLOGI("DistributedHardwareService::OnStart start service success.");
}

  1. 设备组网上线

(1) DeviceManger 触发上线监听回调。

//foundation/distributedhardware/distributed_hardware_fwk/services/distributedhardwarefwkservice/src/accessmanager/access_manager.cpp
int32_t AccessManager::Init()   //AccessManager初始化
{
    DHLOGI("start");
    if (InitDeviceManager() != DH_FWK_SUCCESS) {        //判断设备管理器初始化是否
        DHLOGE("InitDeviceManager failed");
        return ERR_DH_FWK_ACCESS_INIT_DM_FAILED;
    }

    if (RegisterDevStateCallback() != DH_FWK_SUCCESS) { //注册设备上下线监听
        DHLOGE("RegisterDevStateCallback failed");
        return ERR_DH_FWK_ACCESS_REGISTER_DM_FAILED;
    }
    SendTrustedDeviceOnline();
    return DH_FWK_SUCCESS;
}

(2) ResourceManager、ComponentLoader、LocalHardwareManager、VersionManager 、ComponentManager 各个模块依次初始化。

//foundation/distributedhardware/distributed_hardware_fwk/services/distributedhardwarefwkserviceimpl/src/distributed_hardware_manager.cpp
int32_t DistributedHardwareManager::Initialize()
{
    DHLOGI("start");
    CapabilityInfoManager::GetInstance()->Init();   //能力信息管理器实例初始化
    ComponentLoader::GetInstance().Init();  //组件加载实例初始化
    LocalHardwareManager::GetInstance().Init(); //本地硬件管理器初始化
    VersionManager::GetInstance().Init();       //版本管理器初始化
    ComponentManager::GetInstance().Init(); //组件管理器实例初始化
    return DH_FWK_SUCCESS;
}

(3) 分布式硬件管理框架内部会构造上线任务,使能各个分布式硬件部件。

//foundation/distributedhardware/distributed_hardware_fwk/services/distributedhardwarefwkserviceimpl/src/componentmanager/component_manager.cpp
  int32_t ComponentManager::Enable(const std::string &networkId, const std::string &uuid, const std::string &dhId,
      const DHType dhType){}

以分布式相机为例,某设备上线后,分布式硬件管理框架同步到上线设备的相机硬件信息并使能,使能成功后在系统中会新增分布式相机驱动并通知到相机框架,相机框架统一管理本地相机和分布式相机驱动;上层应用通过相机框架接口可以查询到分布式相机,并按照和本地相机相同的接口使用分布式相机。

  1. 设备下线

** (1) DeviceManger 触发下线监听回调。**

//foundation/distributedhardware/distributed_hardware_fwk/services/distributedhardwarefwkservice/src/accessmanager/access_manager.cpp
int32_t AccessManager::UnInit()
{
    DHLOGI("start");
    if (UnInitDeviceManager() != DH_FWK_SUCCESS) {  //判断设备管理器是否下线成功
        DHLOGE("UnInitDeviceManager failed");
        return ERR_DH_FWK_ACCESS_UNINIT_DM_FAILED;
    }

    if (UnRegisterDevStateCallback() != DH_FWK_SUCCESS) {   //判断设备状态回调函数是否注销成功
        DHLOGE("UnRegisterDevStateCallback failed");
        return ERR_DH_FWK_ACCESS_UNREGISTER_DM_FAILED;
    }
    return DH_FWK_SUCCESS;
}

(2) 分布式硬件管理框架内部会构造下线任务,去使能各个分布式硬件部件。

int32_t ComponentManager::Disable(const std::string &networkId, const std::string &uuid, const std::string &dhId,
    const DHType dhType)
{
    auto find = compSource_.find(dhType);
    if (find == compSource_.end()) {
        DHLOGE("can not find handler for dhId = %s.", dhId.c_str());
        return ERR_DH_FWK_PARA_INVALID;
    }

    auto compDisable = std::make_shared<ComponentDisable>();
    auto result = compDisable->Disable(networkId, dhId, find->second);
    if (result != DH_FWK_SUCCESS) {     //判断组件去使能是否成功
        for (int32_t retryCount = 0; retryCount < DISABLE_RETRY_MAX_TIMES; retryCount++) {  //逐个设备检查是否在线并且已经去使能
            if (DHContext::GetInstance().IsDeviceOnline(uuid)) {        //判断设备是否在线
                DHLOGE("device is already online, no need try disable, uuid = %s", GetAnonyString(uuid).c_str());
                return result;
            }
            if (compDisable->Disable(networkId, dhId, find->second) == DH_FWK_SUCCESS) {    //组件去使能
                DHLOGE("disable success, retryCount = %d", retryCount);
                EnabledCompsDump::GetInstance().DumpDisabledComp(networkId, dhType, dhId);
                return DH_FWK_SUCCESS;
            }
            DHLOGE("disable failed, retryCount = %d", retryCount);
        }
        return result;
    }
    DHLOGI("disable result is %d, uuid = %s, dhId = %s", result, GetAnonyString(uuid).c_str(),
        GetAnonyString(dhId).c_str());
    EnabledCompsDump::GetInstance().DumpDisabledComp(networkId, dhType, dhId);

    return result;
}
设备管理组件

DeviceManager

可信设备管理:
int32_t DeviceManagerService::GetTrustedDeviceList(const std::string &pkgName, const std::string &extra,std::vector<DmDeviceInfo> &deviceList):
设备状态管理(devicestate)一些函数包括:
RegisterProfileListener():通过传入的包名和设备信息参数注册profile监听器,回调函数
UnRegisterProfileListener():通过传入的包名和设备信息参数注销profile监听器,回调函数
PostDeviceOnline():通过传入的包名和设备信息参数将设备推送上线,回调函数
PostDeviceOffline():通过传入的包名和设备信息参数将设备推送下线,回调函数
OnDeviceOnline():通过传入的包名和设备信息参数触发设备上线事件,回调函数
OnDeviceOffline():通过传入的包名和设备信息参数触发设备下线事件,回调函数
OnDeviceChanged():通过传入的包名和设备信息参数触发设备变更事件,回调函数
OnDeviceReady():通过传入的包名和设备信息参数触发设备就绪事件,回调函数
OnProfileReady():通过传入的包名和设备id参数触发profile就绪事件,回调函数
RegisterSoftbusStateCallback():注册软总线状态,回调函数
RegisterDevStateCallback():注册设备状态,回调函数
UnRegisterDevStateCallback():注销设备状态,回调函数
RegisterOffLineTimer():注册下线定时器
StartOffLineTimer():启动下线定时器
DeleteTimeOutGroup():删除超时组
设备发现:
StartDeviceDiscovery():启动设备查找
StopDeviceDiscovery():关闭设备查找
OnDeviceFound():设备已发现事件
OnDiscoveryFailed():设备查找失败事件
OnDiscoverySuccess():设备查找成功事件
HandleDiscoveryTimeout():句柄发现超时
认证管理:
AuthenticateDevice():设备认证
UnAuthenticateDevice():取消设备认证
VerifyAuthentication():确认认证
OnSessionOpened():认证会话开启事件
OnSessionClosed():认证会话关闭事件
OnDataReceived():数据已接收事件
OnGroupCreated():组已创建事件
OnMemberJoin():成员加入事件
HandleAuthenticateTimeout():认证超时
EstablishAuthChannel():建立认证通道
StartNegotiate():开始协商
RespNegotiate():
SendAuthRequest():发送认证请求
StartAuthProcess():开始认证流程
StartRespAuthProcess():开始反馈认证流程 (???)
CreateGroup():创建组
AddMember():添加成员
GetConnectAddr():获取链接地址
JoinNetwork():接入网络
AuthenticateFinish():认证结束
CancelDisplay():取消展示
GeneratePincode():生成Pincode
GenerateGroupName():生成组名
GetIsCryptoSupport():获取是否加密支持
SetAuthRequestState():设置认证请求状态
SetAuthResponseState():设置认证回复状态
GetPinCode():获取Pincode
ShowConfigDialog():展示配置对话框
ShowAuthInfoDialog():展示认证对话框
ShowStartAuthDialog():展示开始认证对话框
GetAuthenticationParam():获取认证参数
OnUserOperation():用户操作事件
UserSwitchEventCallback():用户切换事件回调函数
SetPageId():设置页码
SetReason():设置原因

分布式相机组件

架构图说明:

  • 分布式相机接口(DistributedCameraSDK):为分布式硬件管理框架提供超级终端虚拟 Camera 使能/去使能能力,以及相机状态。
  • 分布式相机主控端生命周期管理(DistributedCameraSourceMgr):通过状态机管理主控端 Camera 状态,负责主控端相关对象的创建销毁,以及两端的参数协商。
  • 分布式相机被控端生命周期管理(DistributedCameraSinkMgr):管理被控端 Camera 状态,负责被控端相关对象的创建销毁,以及两端的参数协商。
  • 通道模块(Channel):通过软总线连接主控端与被控端,接收发送主控端的相机指令或被控端的图像信息。
  • 数据处理器(DataProcess):对相机框架返回的图像数据做处理(编解码、色彩空间转换、分辨率缩放、帧率调整,角度调整等)。
  • 分布式相机客户端(CameraClient):被控端调用多媒体接口查询操作本地相机(查询相机数量及相关信息;打开、关闭、获取流等操作)。
  • 虚拟相机 HDF 层实现(VirtualCameraHdf):在 HDF 层建立的虚拟 Camera 硬件,能够被多媒体框架发现和加载,像使用本地的 Camera 一样被使用
    流程: 1.开始 DCameraStreamDataProcessProducer::Start,将 DCameraProducerState 状态置为 1 **2.如果流的类型参数 **streamType 是 CONTINUOUS_FRAME,则进入一个循环 3.在循环内,如果 DCameraProducerState 一直是 1,加一个锁,等待一个条件,DCameraProducerState 为 0 或者 buffer 为空 即退出循环 4.buffer_ 是一个数据缓存队列, Start->LooperContinue->FeedStreamToDriver(满足特定条件会给 buffer_队列填充)
分布式屏幕组件

  • 屏幕区域管理(ScreenRegionManager):管理主控端映射在被控端屏幕上的显示区域的状态,包括为显示区域指定显示的 display,设置显示区域的宽高,解码类型等参数。
  • 分布式屏幕管理(DScreenManager):管理被控端屏幕的参数和状态,负责主控端相关对象的创建和销毁。
  • 屏幕服务(ScreenService):分布式屏幕主控端 SA 服务和分布式屏幕被控端 SA 服务,负责处理分布式硬件管理框架的 IPC 调用
  • 软总线适配器(SoftbusAdapter):对接软总线传输接口,为屏幕图像、输入事件等提供封装的统一调用接口,实现设备间的流数据、字节数据传输和交互。
  • 屏幕传输组件(ScreenTransport):分布式屏幕传输模块,实现屏幕图像数据编码、解码、发送、接收。
  • 屏幕代理客户端(ScreenClient):屏幕图像显示代理客户端,用于在设备上显示其他设备投射过来的屏幕图像数据。

ScreenRegionManager:管理主控端映射在被控端屏幕上的显示区域的状态,包括为显示区域指定显示的 display,设置显示区域的宽高,解码类型等参数。

sptr<IDScreenSource> GetDScreenSourceSA(const std::string &devId);	//获取主控端屏幕SA(Systerm Ability)
int32_t NotifyRemoteScreenService(const std::string &remoteDevId, const std::string &dhId,
	int32_t eventCode, const std::string &eventContent);	//通知远程屏幕服务
void HandleNotifySetUp(const std::string &remoteDevId, const std::string &eventContent);	//通知设置
void NotifyRemoteSourceSetUpResult(const std::string &remoteDevId, const std::string &dhId,
	int32_t errCode, const std::string &errContent);	//通知远程主控端设置结果

DScreenManager: 管理被控端屏幕的参数和状态,负责主控端相关对象的创建和销毁。

int32_t Init();	//初始化,加载屏幕管理
int32_t UnInit();	//卸载屏幕管理
int32_t EnableDistributedScreen(const std::string &devId, const std::string &dhId, const  std::string ¶m,
        const std::string &reqId);	//分布式屏幕使能
int32_t DisableDistributedScreen(const std::string &devId, const std::string &dhId, const std::string &reqId);	//分布式屏幕去使能
void HandleDScreenNotify(const std::string &devId, int32_t eventCode, const std::string &eventContent);	//
void RegisterDScreenCallback(const sptr<IDScreenSourceCallback> &callback);	//
void HandleScreenChange(const std::shared_ptr<DScreen> &changedScreen, Rosen::ScreenGroupChangeEvent event);	//掌控屏幕回调
std::shared_ptr<DScreen> FindDScreenByScreenId(uint64_t screenId);	//根据屏幕Id找到分布式屏幕
sptr<IDScreenSink> GetDScreenSinkSA(const std::string &devId);	//获取分布式屏幕被控端的SA
int32_t NotifyRemoteScreenService(const std::string &devId, int32_t eventCode, const std::string &eventContent);
void NotifyRemoteSinkSetUp(const std::shared_ptr<DScreen> &dScreen);	//通知远程被控端设置
void HandleNotifySetUpResult(const std::string &remoteDevId, const std::string &eventContent);

ScreenService:分布式屏幕主控端 SA 服务和分布式屏幕被控端 SA 服务,负责处理分布式硬件管理框架的 IPC 调用

sink端:
int32_t InitSink(const std::string ¶ms) override;	//初始化被控端
int32_t ReleaseSink() override;	//释放被控端
int32_t SubscribeLocalHardware(const std::string &dhId, const std::string ¶m) override;	//订阅本地硬件
int32_t UnsubscribeLocalHardware(const std::string &dhId) override;	//取消订阅本地硬件
void DScreenNotify(const std::string &devId, int32_t eventCode, const std::string &eventContent) override;	//屏幕通知
void OnStart() override;	//启动被控端屏幕服务
void OnStop() override;		//停止被控端屏幕服务
DISALLOW_COPY_AND_MOVE(DScreenSinkService);

source端:
int32_t InitSource(const std::string ¶ms, const sptr<IDScreenSourceCallback> &callback) override;	//初始化主控端
int32_t ReleaseSource() override;	//释放主控端
int32_t RegisterDistributedHardware(const std::string &devId, const std::string &dhId,
        const EnableParam ¶m, const std::string &reqId) override;	//注册分布式硬件
int32_t UnregisterDistributedHardware(const std::string &devId, const std::string &dhId,
        const std::string &reqId) override;		//注销分布式硬件
int32_t ConfigDistributedHardware(const std::string &devId, const std::string &dhId, const std::string &key,
        const std::string &value) override;		//配置分布式硬件
void DScreenNotify(const std::string &devId, const int32_t eventCode, const std::string &eventContent) override;	//屏幕通知
void OnStart() override;	//启动
void OnStop() override;		//停止
DISALLOW_COPY_AND_MOVE(DScreenSourceService);

SoftbusAdapter:对接软总线传输接口,为屏幕图像、输入事件等提供封装的统一调用接口,实现设备间的流数据、字节数据传输和交互。

int32_t CreateSoftbusSessionServer(const std::string &pkgname, const std::string &sessionName,
    const std::string &peerDevId);	//创建软总线会话服务器
int32_t RemoveSoftbusSessionServer(const std::string &pkgname, const std::string &sessionName,
    const std::string &peerDevId);	//移除软总线会话服务器
int32_t OpenSoftbusSession(const std::string &mySessionName, const std::string &peerSessionName,
    const std::string &peerDevId);	//打卡软总线会话
int32_t CloseSoftbusSession(int32_t sessionId);	//关闭软总线会话
int32_t SendSoftbusBytes(int32_t sessionId, const void *data, int32_t dataLen);	//发送软总线字节
int32_t SendSoftbusStream(int32_t sessionId, const StreamData *data, const StreamData *ext,
    const StreamFrameInfo *param);	//发送软总线数据流
int32_t RegisterSoftbusListener(const std::shared_ptr<ISoftbusListener> &listener, const std::string &sessionName,
    const std::string &peerDevId);	//注册软总线监听
int32_t UnRegisterSoftbusListener(const std::string &sessionName, const std::string &peerDevId);	//注销软总线监听
int32_t OnSoftbusSessionOpened(int32_t sessionId, int32_t result);	//软总线会话打开事件
void OnSoftbusSessionClosed(int32_t sessionId);	//软总线会话关闭事件
void OnBytesReceived(int32_t sessionId, const void *data, uint32_t dataLen);	//字节被接收事件
void OnStreamReceived(int32_t sessionId, const StreamData *data, const StreamData *ext,
    const StreamFrameInfo *StreamFrameInfo);	//数据流被接收事件
void OnMessageReceived(int sessionId, const void *data, unsigned int dataLen);	//消息被接收事件
void OnQosEvent(int sessionId, int eventId, int tvCount, const QosTv *tvList);	//Qos事件
std::shared_ptr<ISoftbusListener> &GetSoftbusListenerByName(int32_t sessionId);
std::shared_ptr<ISoftbusListener> &GetSoftbusListenerById(int32_t sessionId);

ScreenTransport:分布式屏幕传输模块,实现屏幕图像数据编码、解码、发送、接收。

int32_t CheckVideoParam(const VideoParam ¶m);	//检查视频参数
int32_t CheckTransParam(const VideoParam &localParam, const VideoParam &remoteParam, const std::string &peerDevId);	//检查传输参数
int32_t InitScreenTrans(const VideoParam &localParam, const VideoParam &remoteParam, const std::string &peerDevId);	//初始化传输
int32_t RegisterChannelListener();	//注册通道监听函数
int32_t RegisterProcessorListener(const VideoParam &localParam, const VideoParam &remoteParam,const std::string &peerDevId);	//注册进程监听函数

ScreenClient:屏幕图像显示代理客户端,用于在设备上显示其他设备投射过来的屏幕图像数据。

sptr<Surface> CreateWindow(std::shared_ptr<WindowProperty> windowProperty, int32_t windowId);	//创建窗口
int32_t ShowWindow(int32_t windowId);	//展示窗口
int32_t HideWindow(int32_t windowId);	//隐藏窗口
int32_t MoveWindow(int32_t windowId, int32_t startX, int32_t startY);	//移动窗口
int32_t RemoveWindow(int32_t windowId);	//移除窗口
int32_t AddWindow(std::shared_ptr<WindowProperty> windowProperty);	//添加窗口
sptr<Surface> GetSurface(int32_t windowId);	//获得surface(窗口的一种抽象)

目录

## 分布式硬件管理框架
/foundation/distributedhardware/distributedhardwarefwk
├── common                                  # 分布式硬件管理框架为各个部件提供的公共接口类
├── sa_profile                              # 分布式硬件管理框架的SA配置信息
├── services                                # 分布式硬件管理框架的SA具体实现
│   └── distributedhardwarefwkservice       # 分布式硬件管理框架的服务层
│       └── accessmanager                   # 硬件接入管理
│   └── distributedhardwarefwkserviceimpl   # 分布式硬件管理框架的服务实现层
│       ├── resourcemanager                 # 硬件资源管理
│       ├── componentmanager                # 分布式硬件部件管理
│       ├── localhardwaremanager            # 本地硬件信息管理
│       ├── componentloader                 # 部件加载管理
│       └── versionmanager                  # 版本管理
└── utils                                   # 分布式硬件管理框架为各个部件提供的工具类

## devicemanager
/foundation/distributedhardware/devicemanager 
├── common                        #公共能力头文件存放目录 
│  └── include 
│      └── ipc 
│          └── model              #ipc功能模块头文件存放目录 
├── display                       #DM显示hap代码 
│  └── entry 
│      └── src 
│          └── main 
│              ├── js             #DM PIN码显示FA相关JS代码 
│              └── resources      #DM PIN码显示FA相关资源配置文件目录 
├── figures 
├── interfaces 
│  ├── inner_kits                 #内部接口及实现存放目录 
│  │  └── native_cpp              #内部native接口及实现存放目录 
│  │      ├── include 
│  │      │  ├── ipc              #ipc头文件存放目录 
│  │      │  │  ├── lite          #small 
│  │      │  │  └── standard      #standard 
│  │      │  └── notify           #ipc回调通知头文件目录 
│  │      └── src 
│  │          ├── ipc             #ipc功能代码 
│  │          │  ├── lite         #small 
│  │          │  └── standard     #standard 
│  │          └── notify          #ipc回调通知功能代码 
│  └── kits                       #外接口及实现存放目录 
│      └── js                     #外部JS接口及实现存放目录 
│          ├── include            #外部JS接口及实现欧文件存放目录 
│          └── src                #外部JS接口及实现代码 
├── sa_profile 
├── services 
│  └── devicemanagerservice       #devicemanagerservice服务实现核心代码 
│      ├── include 
│      │  ├── ability             #与PIN码显示FA拉起管理相关头文件 
│      │  ├── auth                #devie_auth交互相关头文件 
│      │  ├── ipc                 #进程间通信相关头文件 
│      │  │  ├── lite             #small 
│      │  │  └── standard         #standard 
│      │  ├── message             #消息数据解析相关头文件 
│      │  ├── requestauth         #设备认证功能相关头文件 
│      │  ├── softbus             #软总线相关头文件 
│      │  └── timer               #定时器处理相关头文件 
│      └── src 
│          ├── ability            #与PIN码显示FA拉起管理相关功能代码 
│          │  ├── lite            #small 
│          │  └── standard        #standard 
│          ├── auth               #devie_auth交互相关核心代码 
│          ├── ipc                #进程间通信相功能代码 
│          │  ├── lite            #small 
│          │  └── standard        #standard 
│          ├── message            #消息数据解析相功能代码 
│          ├── requestauth        #设备认证功能代码 
│          ├── softbus            #通道建立功能核心代码 
│          └── timer              #timer处理代码 
└── utils                         #公共能力头文件存放目  
├── include  
│  ├── cipher                     #加解密功能相关头文件  
│  ├── ipc                        #ipc公共头文件存放目录  
│  │  ├── lite                    #small  
│  │  └── standard                #standard  
│  └── log                        #log相关头文件存放目录  
└── src  
├── cipher                        #加解密功能代码  
├── ipc                           #ipc公共功能代码  
│  ├── lite                       #small  
│  └── standard                   #standard  
└── log                           #log相关功能代码

## 分布式相机
/foundation/distributedhardware/distributedcamera 
├── camera_hdf                             # 分布式相机HAL功能 
├── common                                 # 分布式相机公共模块 
├── interfaces                             # 分布式相机对外接口模块 
├── sa_profile                             # 分布式相机SA配置模块 
├── services                               # 服务模块 
│   └── cameraservice                      # 相机服务模块 
│       ├── base                           # 分布式相机两端公共部分 
│       ├── cameraoperator                 # 分布式相机相机操作模块 
│       ├── sinkservice                    # 分布式相机被控端服务模块 
│       └── sourceservice                  # 分布式相机主控端服务模块 
│   ├── channel                            # 分布式相机通道模块 
│   └── data_process                       # 分布式相机数据处理模块

## 分布式屏幕
/foundation/distributedhardware/distributedscreen  
├── common                                  # 分布式屏幕公共数据定义,包括常量、错误码、日志、工具等  
├── interface                               # 分布式屏幕SDK,包含主控端和被控端服务调用接口  
├── sa_profile                              # 分布式屏幕的SA配置信息  
├── services                                # 分布式屏幕主控端和被控端功能主体业务实现  
│   └── common                              # 分布式屏幕功能主控端、被控端共用功能实现  
│       ├── databuffer                      # 屏幕数据存储定义  
│       └── screen_channel                  # 屏幕数据传输通道接口定义  
│   └── screenclient                        # 分布式屏幕代理客户端实现  
│   └── screenservice                       # 分布式屏幕主体功能实现  
│       └── sinkservice                     # 分布式屏幕被控端服务功能实现  
│           ├── dscreenservice              # 分布式屏幕被控端SA  
│           └── screenregionmgr             # 分布式屏幕被控端显示区域管理  
│       └── sourceservice                   # 分布式屏幕主控端服务功能实现  
│           ├── dscreenmgr                  # 分布式屏幕主控端屏幕管理  
│           └── dscreenservice              # 分布式屏幕主控端SA  
│       └── screentransport                 # 分布式屏幕传输组件  
│           ├── screendatachannel           # 屏幕数据传输通道,用于传输组件和编解码器之间数据传输  
│           ├── screensinkprocessor         # 分布式屏幕被控端数据处理模块,包括解码等  
│           ├── screensinktrans             # 分布式屏幕被控端数据传输组件,包含数据传输通道channel和数据处理模块processor  
│           ├── screensourceprocessor       # 分布式屏幕主控端数据处理模块,包括编码等  
│           └── screensourcetrans           # 分布式屏幕主控端数据传输组件,包含数据传输通道channel和数据处理模块processor  
│       └── softbusadapter                  # 软总线接口适配器,为屏幕传输、触控事件传输提供统一传输接口  
└── screenhandler                           # 分布式屏幕硬件信息上报、设备状态变化通知,由分布式硬件管理框架加载

代码分析

本地硬件启动流程分析

//foundation/distributedhardware/distributed_hardware_fwk/services/distributedhardwarefwkserviceimpl/src/localhardwaremanager/local_hardware_manager.cpp
void LocalHardwareManager::Init()
{
    std::vector<DHType> allCompTypes = ComponentLoader::GetInstance().GetAllCompTypes();	//获取所有组件类型
    for (auto dhType : allCompTypes) {
        IHardwareHandler *hardwareHandler = nullptr;
        int32_t status = ComponentLoader::GetInstance().GetHardwareHandler(dhType, hardwareHandler);
        if (status != DH_FWK_SUCCESS || hardwareHandler == nullptr) {	//判断(组件加载模块的)获取硬件是否成功
            DHLOGE("GetHardwareHandler %#X failed", dhType);
            continue;
        }
        if (hardwareHandler->Initialize() != DH_FWK_SUCCESS) {		//各硬件是否初始化成功
            DHLOGE("Initialize %#X failed", dhType);
            continue;
        }

        DHQueryTraceStart(dhType);
        QueryLocalHardware(dhType, hardwareHandler);
        DHTraceEnd();
        if (!hardwareHandler->IsSupportPlugin()) {	//判断硬件是否支持插件
            DHLOGI("hardwareHandler is not support hot swap plugin, release!");
            ComponentLoader::GetInstance().ReleaseHardwareHandler(dhType);
            hardwareHandler = nullptr;
        } else {
            compToolFuncsMap_[dhType] = hardwareHandler;
            std::shared_ptr<PluginListener> listener = std::make_shared<PluginListenerImpl>(dhType);
            pluginListenerMap_[dhType] = listener;
            hardwareHandler->RegisterPluginListener(listener);
        }
    }
}

本地硬件关闭流程分析

//foundation/distributedhardware/distributed_hardware_fwk/services/distributedhardwarefwkserviceimpl/src/localhardwaremanager/local_hardware_manager.cpp
void LocalHardwareManager::UnInit()
{
    DHLOGI("start");
    compToolFuncsMap_.clear();
    pluginListenerMap_.clear();
}

分布式硬件启动流程分析

//foundation/distributedhardware/distributed_hardware_fwk/services/distributedhardwarefwkservice/src/distributed_hardware_service.cpp
void DistributedHardwareService::OnStart()
{
    DHLOGI("DistributedHardwareService::OnStart start");
    HiSysEventWriteMsg(DHFWK_INIT_BEGIN, OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
        "dhfwk sa start on demand.");
    if (state_ == ServiceRunningState::STATE_RUNNING) {		//判断分布式硬件服务的状态是否为running状态
        DHLOGI("DistributedHardwareService has already started.");
        return;
    }
    if (!Init()) {		//初始化分布式硬件服务
        DHLOGE("failed to init DistributedHardwareService");
        return;
    }
    state_ = ServiceRunningState::STATE_RUNNING;	//将分布式硬件服务的状态置为running
    DHLOGI("DistributedHardwareService::OnStart start service success.");
}

//foundation/distributedhardware/distributed_hardware_fwk/services/distributedhardwarefwkserviceimpl/src/distributed_hardware_manager.cpp
int32_t DistributedHardwareManager::Initialize()
{
    DHLOGI("start");
    CapabilityInfoManager::GetInstance()->Init();	//能力信息管理实例初始化
    ComponentLoader::GetInstance().Init();		//组件加载实例初始化
    LocalHardwareManager::GetInstance().Init();		//本地硬件管理实例初始化
    VersionManager::GetInstance().Init();		//版本管理实例初始化
    ComponentManager::GetInstance().Init();		//组件管理实例初始化
    return DH_FWK_SUCCESS;
}

分布式硬件关闭流程分析

OnStop()

//foundation/distributedhardware/distributed_hardware_fwk/services/distributedhardwarefwkservice/src/distributed_hardware_service.cpp
void DistributedHardwareService::OnStop()
{
    DHLOGI("DistributedHardwareService::OnStop ready to stop service.");
    state_ = ServiceRunningState::STATE_NOT_START;
    registerToService_ = false;
}

//foundation/distributedhardware/distributed_hardware_fwk/services/distributedhardwarefwkserviceimpl/src/distributed_hardware_manager.cpp
int32_t DistributedHardwareManager::Release()
{
    DHLOGI("start");
    TaskBoard::GetInstance().WaitForALLTaskFinish();	//任务板等待所有任务结束
    ComponentManager::GetInstance().UnInit();		//组件管理实例去初始化
    VersionManager::GetInstance().UnInit();		//版本管理实例去初始化
    LocalHardwareManager::GetInstance().UnInit();	//本地硬件管理去初始化
    ComponentLoader::GetInstance().UnInit();		//组件加载实例去初始化
    CapabilityInfoManager::GetInstance()->UnInit();	//能力信息管理实例去初始化
    return DH_FWK_SUCCESS;
}

分布式硬件管理框架:distributedhardwarefwk

//第一部分:componentloader 硬件加载模块,可以获取硬件的版本信息,类型,来源,目的等基本信息,读取配置,以及释放硬件
//foundation/distributedhardware/distributed_hardware_fwk/services/distributedhardwarefwkserviceimpl/src/componentloader/component_loader.cpp
//组件的构成
struct CompHandler {
    void *sourceHandler;    //源头
    void *sinkHandler;      //目的地
    void *hardwareHandler;  //硬件句柄
};
class ComponentLoader {
    DECLARE_SINGLE_INSTANCE_BASE(ComponentLoader);
public:
    ComponentLoader() : isLocalVersionInit_(false) {}
    ~ComponentLoader() {}
public:
    int32_t Init();             //初始化函数,解析配置文件:/etc/distributed_hardware_components_cfg.json,从配置里读取组件的路径和版本等信息
    int32_t GetHardwareHandler(const DHType dhType, IHardwareHandler *&hardwareHandlerPtr);     //获取硬件
    int32_t GetSource(const DHType dhType, IDistributedHardwareSource *&sourcePtr);             //获取源头
    int32_t GetSink(const DHType dhType, IDistributedHardwareSink *&sinkPtr);                   //获取目的地
    int32_t UnInit();           //
    int32_t ReleaseHardwareHandler(const DHType dhType);
    int32_t ReleaseSource(const DHType dhType);
    int32_t ReleaseSink(const DHType dhType);
    std::vector<DHType> GetAllCompTypes();
    int32_t GetLocalDHVersion(DHVersion &dhVersion);
private:
    void *GetHandler(const std::string &soName);
    void GetAllHandler(std::map<DHType, CompConfig> &dhtypeMap);
    int32_t ReleaseHandler(void *&handler);
    int32_t GetCompPathAndVersion(const std::string &jsonStr, std::map<DHType, CompConfig> &dhtypeMap); //解析配置文件函数
    CompVersion GetCompVersionFromComConfig(const CompConfig& cCfg);
    int32_t ParseConfig();
    bool IsDHTypeExist(DHType dhType);
    std::string Readfile(const std::string &filePath);
private:
    DHVersion localDHVersion_;
    std::map<DHType, CompHandler> compHandlerMap_;
    std::atomic<bool> isLocalVersionInit_;
};
//第二部分:localhardwaremanager 本地硬件管理模块,
//foundation/distributedhardware/distributed_hardware_fwk/services/distributedhardwarefwkserviceimpl/src/localhardwaremanager/local_hardware_manager.cpp
class LocalHardwareManager {
    DECLARE_SINGLE_INSTANCE_BASE(LocalHardwareManager);
public:
    LocalHardwareManager();
    ~LocalHardwareManager();
    void Init();        //本地所有硬件初始化
    void UnInit();      //本地所有硬件释放
private:
    void QueryLocalHardware(const DHType dhType, IHardwareHandler *hardwareHandler);
    void AddLocalCapabilityInfo(const std::vector<DHItem> &dhItems, const DHType dhType);
private:
    std::map<DHType, IHardwareHandler*> compToolFuncsMap_;
    std::map<DHType, std::shared_ptr<PluginListener>> pluginListenerMap_;
};
class PluginListenerImpl : public PluginListener {
public:
    explicit PluginListenerImpl(const DHType type) : dhType_(type) {}
    virtual ~PluginListenerImpl() = default;
    virtual void PluginHardware(const std::string &dhId, const std::string &attrs) override;    //给硬件加载插件
    virtual void UnPluginHardware(const std::string &dhId) override;                            //给硬件解除插件
private:
    DHType dhType_;
};
//第三部分:resourcemanager 资源管理模块
//foundation/distributedhardware/distributedhardwarefwk/services/distributedhardwarefwkserviceimpl/src/resourcemanager

启动分布式相机

//foundation/distributedhardware/distributed_camera/services/cameraservice/cameraoperator/client/test/sample/main.cpp
int main()
{
    DHLOGI("========== Distributed Camera Demo Start ==========");
    std::shared_ptr<StateCallback> stateCallback = std::make_shared<DCameraDemoStateCallback>();

    sptr<CameraManager> cameraManager = CameraManager::GetInstance();			//创建相机管理的实例
    cameraManager->SetCallback(std::make_shared<DemoDCameraManagerCallback>());		//设置相机管理的回调函数
    sptr<CaptureSession> captureSession = cameraManager->CreateCaptureSession();		//创建相机采集会话
    captureSession->SetCallback(std::make_shared<DemoDCameraSessionCallback>(stateCallback));	//设置相机采集回话的回调函数

    sptr<CameraInfo> cameraInfo = GetCameraInfo(cameraManager);				//获取相机信息
    sptr<CaptureInput> cameraInput = cameraManager->CreateCameraInput(cameraInfo);	//创建相机输入参数
    std::shared_ptr<DemoDCameraInputCallback> inputCallback = std::make_shared<DemoDCameraInputCallback>(stateCallback);	//设置相机输入参数的回调函数
    ((sptr<CameraInput> &)cameraInput)->SetErrorCallback(inputCallback);	//设置相机的错误回调
    ((sptr<CameraInput> &)cameraInput)->SetFocusCallback(inputCallback);	//设置相机的焦点回调

    std::shared_ptr<DCameraCaptureInfo> photoInfo = std::make_shared<DCameraCaptureInfo>();	//获取相片信息
    InitPhotoInfo(photoInfo);	//初始化相片信息

    std::shared_ptr<DCameraCaptureInfo> previewInfo = std::make_shared<DCameraCaptureInfo>();	//获取预览信息
    InitPreviewInfo(previewInfo);	//初始化预览信息

    std::shared_ptr<DCameraCaptureInfo> videoInfo = std::make_shared<DCameraCaptureInfo>();	//获取视频信息
    InitVideoInfo(videoInfo);	//初始化视频信息

    SetPhotoOutput(cameraManager, photoInfo, stateCallback);	//设置相片输出参数

    std::shared_ptr<PhotoCaptureSetting> photoCaptureSettings = std::make_shared<PhotoCaptureSetting>();	//相片采集设置
    SetCaptureSettings(photoCaptureSettings);	//设置采集配置

    SetPreviewOutput(cameraManager, previewInfo, stateCallback);	//设置预览输出

    SetVideoOutput(cameraManager, videoInfo, stateCallback);	//设置视频输出

    captureSession->BeginConfig();		//采集会话开始配置
    captureSession->AddInput(cameraInput);	//采集会话中添加输入配置
    captureSession->AddOutput(g_photoOutput);	//采集会话中添加相片输出配置
    captureSession->AddOutput(g_previewOutput);	//采集会话中添加预览输出配置
    captureSession->CommitConfig();		//采集会话提交配置
    captureSession->Start();			//采集会话启动
    sleep(SLEEP_FIVE_SECOND);

    SetFocusAndExposure(cameraInput);		//根据相机的输入参数设置焦点和曝光
    sleep(SLEEP_FIVE_SECOND);

    ((sptr<PhotoOutput> &)g_photoOutput)->Capture(photoCaptureSettings);
    sleep(SLEEP_TWENTY_SECOND);

    captureSession->Stop();			//采集会话停止
    captureSession->Release();			//采集会话释放
    cameraInput->Release();			//相机输入释放
    cameraManager->SetCallback(nullptr);	//将相机管理的回调函数置空

    DHLOGI("========== Distributed Camera Demo End ==========");
    return 0;
}

打开分布式相机

//foundation/distributedhardware/distributedcamera/camera_hdf/hdi_impl/src/dcamera_device/dcamera_device.cpp
CamRetCode DCameraDevice::OpenDCamera(const OHOS::sptr<ICameraDeviceCallback> &callback)
{
    if (callback == nullptr) {		//判断传入的回调参数
        DHLOGE("Input callback is null.");
        return CamRetCode::INVALID_ARGUMENT;
    }
    dCameraDeviceCallback_ = callback;

    std::shared_ptr<DCameraProvider> provider = DCameraProvider::GetInstance();		//获取一个分布式相机服务提供者的实例
    if (provider == nullptr) {
        DHLOGE("Get distributed camera provider instance is null.");
        return CamRetCode::DEVICE_ERROR;
    }
    DCamRetCode ret = provider->OpenSession(dhBase_);		//打开一个分布式相机控制会话
    if (ret != DCamRetCode::SUCCESS) {
        DHLOGE("Open distributed camera control session failed, ret = %d.", ret);
        return MapToExternalRetCode(ret);
    }

    unique_lock<mutex> lock(openSesslock_);
    auto st = openSessCV_.wait_for(lock, chrono::seconds(WAIT_OPEN_TIMEOUT_SEC));	//等待分布式相机会话的连接
    if (st == cv_status::timeout) {
        DHLOGE("Wait for distributed camera session open timeout.");
        return CamRetCode::DEVICE_ERROR;
    }
    {
        unique_lock<mutex> lock(isOpenSessFailedlock_);
        if (isOpenSessFailed_) {
            DHLOGE("Open distributed camera session failed.");
            return CamRetCode::DEVICE_ERROR;
        }
    }

    ret = CreateDStreamOperator();		//创建分布式相机流操作
    if (ret != SUCCESS) {
        DHLOGE("Create distributed camera stream operator failed.");
        return MapToExternalRetCode(ret);
    }
    isOpened_ = true;

    return MapToExternalRetCode(ret);
}

基于相机的分布式硬件子系统流程分析

流程示意图

相机流程说明

1.第 1 步,系统拉起 SA 服务,AccessManager 初始化,DeviceManager 注册上下线监听(分布式管理框架)
2.第 2 步,启动相机(本地与远程)
3.第 3 步,DeviceManager 触发上线监听回调(相机管理框架 à 分布式管理框架)
4.第 4 步,多个模块依次初始化(ResourceManager/ComponentLoader/LocalHardwareManager/VersionManager/ComponentManager)
5.第 5 步,本地和远程相机组网上线(本地相机管理框架 ßà 远程相机管理框架)
6.第 6 步,分布式硬件管理框框架构造上线任务,同步相机信息(分布式管理框架)
7.第 7 步,分布式硬件管理框架使能本地和远程相机(分布式管理框架 à 相机管理框架)
8.第 8 步,分布式硬件管理框架增加分布式相机驱动(分布式管理框架)
9.第 9 步,分布式硬件管理框架通知到相机框架(分布式管理框架 à 相机管理框架)
10.第 10 步,分布式相机框架统一管理本地和远程相机驱动(分布式管理框架 ßà 相机管理框架)
11.第 11 步,以同样方式操作分布式相机和本地相机(分布式相机管理框架 à 相机管理框架)
12.第 12 步,相机下线(本地与远程)
13.第 13 步,DeviceManager 触发下线监听回调(相机管理框架 à 分布式管理框架)
14.第 14 步,分布式硬件管理框架对相机部件去使能,分布式硬件管理框架通知分布式相机框架(分布式管理框架 à 相机管理框架)

总结

分布式硬件是OpenHarmony的一个新的分布式能力,其为用户提供丰富的硬件管控能力,充分发挥各设备中硬件资源的能力

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

《鸿蒙开发学习手册》:

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

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

开发基础知识: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. ……

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-05-12 09:36:05       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-12 09:36:05       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-12 09:36:05       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-12 09:36:05       18 阅读

热门阅读

  1. jquery

    jquery

    2024-05-12 09:36:05      9 阅读
  2. 设计模式:观察者模式

    2024-05-12 09:36:05       11 阅读
  3. 第02章_MySQL环境搭建

    2024-05-12 09:36:05       7 阅读
  4. 区块链链底层架构,IPFS,DAPP

    2024-05-12 09:36:05       10 阅读
  5. MySQL从主库恢复从库

    2024-05-12 09:36:05       9 阅读
  6. MySQL主从切换测试

    2024-05-12 09:36:05       9 阅读
  7. 升级 CentOS7.9 的 sqlite-devel

    2024-05-12 09:36:05       12 阅读
  8. 基于 element-ui 表格组件 el-table 导出表格数据

    2024-05-12 09:36:05       9 阅读
  9. Vue3知识总结-2

    2024-05-12 09:36:05       9 阅读
  10. npm 常用指令介绍

    2024-05-12 09:36:05       8 阅读
  11. Spring Boot进阶 - Starter原理

    2024-05-12 09:36:05       8 阅读
  12. python - pip安装及使用详解

    2024-05-12 09:36:05       10 阅读
  13. 高斯-牛顿法C实现

    2024-05-12 09:36:05       10 阅读
  14. Oracle数据库之条件查询、模糊查询和排序(四)

    2024-05-12 09:36:05       11 阅读
  15. 介绍 TensorFlow 的基本概念和使用场景

    2024-05-12 09:36:05       9 阅读