当前位置: 首页 > news >正文

【Bluedroid】蓝牙HID DEVICE断开连接流程源码分析

蓝牙HID(Human Interface Device)的断开连接流程涉及从应用层到协议栈的多层交互。本文通过剖析Android Bluetooth协议栈代码,梳理从上层调用disconnect()到最终物理链路断开的完整流程,涵盖状态检查、消息传递、L2CAP通道关闭、资源清理及回调通知等关键环节。流程中通过事件驱动机制协调各模块,确保安全断开并通知应用层状态变化。

一、流程概述

1.1 应用层触发断开

  • 入口函数btif_hd.cc中的disconnect()首先检查应用注册和设备状态。

    • 若条件不满足(如未注册或未启用),直接返回错误状态。

    • 条件满足时调用BTA_HdDisconnect()发起断开请求。

1.2 BTA层消息传递

  1. BTA_HdDisconnect通过bta_sys_sendmsg()发送BTA_HD_API_DISCONNECT_EVT事件,触发状态机处理,而非直接操作硬件。

1.3 状态机处理断开事件

  • 状态机bta_hd_better_state_machine根据当前状态(如BTA_HD_CONN_ST)处理BTA_HD_API_DISCONNECT_EVT,调用bta_hd_disconnect_act()执行实际断开操作。

1.4 HID协议栈断开操作

  • HID_DevDisconnect()验证设备状态后,调用hidd_conn_disconnect()关闭L2CAP通道:

    • 清理待发送数据(osi_free(hd_cb.pending_data))。

    • 通过L2CA_DisconnectReq()依次断开控制通道(ctrl_cid)和中断通道(intr_cid)。

    • 更新连接状态为HID_CONN_STATE_DISCONNECTING

1.5 底层通道关闭与资源释放

  • hidd_l2cif_disconnect()处理L2CAP断开:

    • 若所有通道关闭(ctrl_cidintr_cid均为0),更新设备状态为HIDD_DEV_NO_CONN,并通过回调通知上层(HID_DHOST_EVT_CLOSEHID_DHOST_EVT_VC_UNPLUG)。

1.6 回调通知与资源清理

  • BTA层回调bta_hd_cback()根据事件类型(如HID_DHOST_EVT_CLOSE)转发至btif_hd_upstreams_evt()

  • 应用层处理

    • 正常断开:通知BTHD_CONN_STATE_DISCONNECTED,移除设备绑定信息(BTA_DmRemoveDevice()btif_hd_remove_device())。

    • 虚拟线缆拔出:触发vc_unplug_cb,强制清理设备数据。

二、源码分析

disconnect

/packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/********************************************************************************* Function         disconnect** Description      Disconnects from host** Returns          bt_status_t*******************************************************************************/
static bt_status_t disconnect(void) {log::verbose("");if (!btif_hd_cb.app_registered) {log::warn("application not yet registered");return BT_STATUS_NOT_READY;}if (btif_hd_cb.status != BTIF_HD_ENABLED) {log::warn("BT-HD not enabled, status={}", btif_hd_cb.status);return BT_STATUS_NOT_READY;}BTA_HdDisconnect();return BT_STATUS_SUCCESS;
}

断开与蓝牙主机的连接。在执行断开连接操作之前,先检查应用程序是否已经注册以及蓝牙 HID 设备(BT - HD)是否已经启用。若这两个条件都满足,会调用 BTA_HdDisconnect 函数来发起断开连接的请求;

BTA_HdDisconnect

/packages/modules/Bluetooth/system/bta/hd/bta_hd_api.cc
/********************************************************************************* Function         BTA_HdDisconnect** Description      This function is called when host shall be disconnected** Returns          void*******************************************************************************/
void BTA_HdDisconnect(void) {log::verbose("");BT_HDR_RIGID* p_buf = (BT_HDR_RIGID*)osi_malloc(sizeof(BT_HDR_RIGID));p_buf->event = BTA_HD_API_DISCONNECT_EVT;bta_sys_sendmsg(p_buf);
}

该函数本身并不直接执行断开连接的实际动作,而是通过消息机制通知系统进行相应处理。

bta_hd_better_state_machine(BTA_HD_API_DISCONNECT_EVT)

packages/modules/Bluetooth/system/bta/hd/bta_hd_main.cc
static void bta_hd_better_state_machine(uint16_t event, tBTA_HD_DATA* p_data) {switch (get_state()) {case BTA_HD_INIT_ST:...break;case BTA_HD_IDLE_ST:switch (event) {case BTA_HD_API_UNREGISTER_APP_EVT:set_state(BTA_HD_INIT_ST);bta_hd_unregister_act();break;case BTA_HD_API_CONNECT_EVT:bta_hd_connect_act(p_data);break;...case BTA_HD_CONN_ST:switch (event) {case BTA_HD_API_UNREGISTER_APP_EVT:set_state(BTA_HD_TRANSIENT_TO_INIT_ST);bta_hd_disconnect_act();break;case BTA_HD_API_DISCONNECT_EVT:bta_hd_disconnect_act();break;...

bta_hd_disconnect_act

packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/********************************************************************************* Function         bta_hd_disconnect_act** Description      Disconnect from device** Returns          void*******************************************************************************/
void bta_hd_disconnect_act() {tHID_STATUS ret;tBTA_HD cback_data;log::verbose("");ret = HID_DevDisconnect();if (ret != HID_SUCCESS) {log::warn("HID_DevDisconnect returned {}", ret);return;}if (HID_DevGetDevice(&cback_data.conn.bda) == HID_SUCCESS) {cback_data.conn.status = BTHD_CONN_STATE_DISCONNECTING;bta_hd_cb.p_cback(BTA_HD_CONN_STATE_EVT, &cback_data);}
}

执行与蓝牙设备断开连接的操作。首先调用 HID_DevDisconnect 函数尝试断开连接,然后检查断开连接的结果。如果断开连接成功,会获取设备地址并设置连接状态为 “正在断开连接”,最后调用回调函数 bta_hd_cb.p_cback 通知上层连接状态的变化。

bta_hd_cb.p_cback(BTA_HD_CONN_STATE_EVT, &cback_data)

btif_hd_upstreams_evt(BTA_HD_CONN_STATE_EVT)

packages/modules/Bluetooth/system/btif/src/btif_hd.cc 
/********************************************************************************* Function         btif_hd_upstreams_evt** Description      Executes events in btif context** Returns          void*******************************************************************************/
static void btif_hd_upstreams_evt(uint16_t event, char* p_param) {tBTA_HD* p_data = (tBTA_HD*)p_param;log::verbose("event={}", dump_hd_event(event));switch (event) {...case BTA_HD_CONN_STATE_EVT:HAL_CBACK(bt_hd_callbacks, connection_state_cb,(RawAddress*)&p_data->conn.bda,(bthd_connection_state_t)p_data->conn.status);break;...

当接收到 BTA_HD_CONN_STATE_EVT 事件时,会调用回调函数来通知上层应用设备的连接状态发生了变化(BTHD_CONN_STATE_DISCONNECTING)。

HID_DevDisconnect

/packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
/********************************************************************************* Function         HID_DevDisconnect** Description      Disconnects from device** Returns          tHID_STATUS*******************************************************************************/
tHID_STATUS HID_DevDisconnect(void) {if (!hd_cb.reg_flag) {log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NOT_REGISTERED_AT_DISCONNECT,1);return HID_ERR_NOT_REGISTERED;}if (!hd_cb.device.in_use) {log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_DEVICE_NOT_IN_USE_AT_DISCONNECT,1);return HID_ERR_INVALID_PARAM;}if (hd_cb.device.state == HIDD_DEV_NO_CONN) {/* If we are still trying to connect, just close the connection. */if (hd_cb.device.conn.conn_state != HID_CONN_STATE_UNUSED) { // 说明设备正在尝试连接tHID_STATUS ret = hidd_conn_disconnect();hd_cb.device.conn.conn_state = HID_CONN_STATE_UNUSED;hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE,HID_ERR_DISCONNECTING, NULL);log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_DISCONNECTING,1);return ret;}// 确实没有连接log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NO_CONNECTION_AT_DISCONNECT,1);return HID_ERR_NO_CONNECTION;}return hidd_conn_disconnect();
}

在执行断开连接操作之前,会对设备的注册状态、使用状态和连接状态进行检查。若检查通过,则调用 hidd_conn_disconnect 来实际执行断开连接操作。

hidd_conn_disconnect
packages/modules/Bluetooth/system/stack/hid/hidd_conn.cc
/********************************************************************************* Function         hidd_conn_disconnect** Description      Disconnects existing HID connection** Returns          HID_SUCCESS*******************************************************************************/
tHID_STATUS hidd_conn_disconnect(void) {log::verbose("");// clean any outstanding data on intrif (hd_cb.pending_data) {osi_free(hd_cb.pending_data);hd_cb.pending_data = NULL;}// 获取连接信息指针tHID_CONN* p_hcon = &hd_cb.device.conn;// 判断连接状态并执行断开操作if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0)) {p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING;/* Set l2cap idle timeout to 0 (so ACL link is disconnected* immediately after last channel is closed) */// 将 L2CAP 空闲超时时间设置为0,这样在最后一个通道关闭后,ACL 链路会立即断开L2CA_SetIdleTimeoutByBdAddr(hd_cb.device.addr, 0, BT_TRANSPORT_BR_EDR);if (p_hcon->intr_cid) {hidd_l2cif_disconnect(p_hcon->intr_cid);} else if (p_hcon->ctrl_cid) {hidd_l2cif_disconnect(p_hcon->ctrl_cid);}} else {log::warn("already disconnected");p_hcon->conn_state = HID_CONN_STATE_UNUSED;}return (HID_SUCCESS);
}

在执行断开操作前,会清理可能存在的待处理数据,然后根据连接的控制通道 ID(ctrl_cid)和中断通道 ID(intr_cid)的情况,执行相应的断开连接步骤,并更新连接状态。若连接已经断开,则更新状态。

hidd_l2cif_disconnect
packages/modules/Bluetooth/system/stack/hid/hidd_conn.cc
static void hidd_l2cif_disconnect(uint16_t cid) {L2CA_DisconnectReq(cid);log::verbose("cid={:04x}", cid);tHID_CONN* p_hcon = &hd_cb.device.conn;if (p_hcon->conn_state == HID_CONN_STATE_UNUSED ||(p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) {log::warn("unknown cid");return;}if (cid == p_hcon->ctrl_cid) {p_hcon->ctrl_cid = 0;} else {p_hcon->intr_cid = 0;// now disconnect CTRLL2CA_DisconnectReq(p_hcon->ctrl_cid);if (bluetooth::common::init_flags::clear_hidd_interrupt_cid_on_disconnect_is_enabled()) {p_hcon->ctrl_cid = 0;}}//  更新连接状态与回调通知if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) {log::verbose("INTR and CTRL disconnected");hd_cb.device.state = HIDD_DEV_NO_CONN;p_hcon->conn_state = HID_CONN_STATE_UNUSED;if (hd_cb.pending_vc_unplug) {hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_VC_UNPLUG,p_hcon->disc_reason, NULL);hd_cb.pending_vc_unplug = FALSE;} else {hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE,p_hcon->disc_reason, NULL);}}
}

断开指定的 L2CAP通道连接。先调用 L2CA_DisconnectReq 尝试断开连接,然后根据连接状态判断是否为未知通道 ID。若通道 ID 有效,根据通道类型(控制通道或中断通道)断开相应通道连接,并在两个通道都断开时更新设备连接状态,调用回调函数通知上层连接已断开。

情况1:hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_VC_UNPLUG,p_hcon->disc_reason, NULL)

bta_hd_cback(HID_DHOST_EVT_VC_UNPLUG)

/********************************************************************************* Function         bta_hd_cback** Description      BTA HD callback function** Returns          void*******************************************************************************/
static void bta_hd_cback(const RawAddress& bd_addr, uint8_t event,uint32_t data, BT_HDR* pdata) {tBTA_HD_CBACK_DATA* p_buf = NULL;uint16_t sm_event = BTA_HD_INVALID_EVT;log::verbose("event={}", event);switch (event) {...case HID_DHOST_EVT_CLOSE:sm_event = BTA_HD_INT_CLOSE_EVT;break;...case HID_DHOST_EVT_VC_UNPLUG:sm_event = BTA_HD_INT_VC_UNPLUG_EVT;break;...}if (sm_event != BTA_HD_INVALID_EVT &&(p_buf = (tBTA_HD_CBACK_DATA*)osi_malloc(sizeof(tBTA_HD_CBACK_DATA) +sizeof(BT_HDR))) != NULL) {p_buf->hdr.event = sm_event;p_buf->addr = bd_addr;p_buf->data = data;p_buf->p_data = pdata;bta_sys_sendmsg(p_buf);}
}

bta_hd_better_state_machine(BTA_HD_INT_VC_UNPLUG_EVT)

    case BTA_HD_INT_VC_UNPLUG_EVT:set_state(BTA_HD_IDLE_ST);bta_hd_vc_unplug_done_act(p_data);break;

bta_hd_vc_unplug_done_act

packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/********************************************************************************* Function         bta_hd_vc_unplug_done_act** Description** Returns          void*******************************************************************************/
void bta_hd_vc_unplug_done_act(tBTA_HD_DATA* p_data) {tBTA_HD_CBACK_DATA* p_cback = (tBTA_HD_CBACK_DATA*)p_data;tBTA_HD cback_data;log::verbose("");bta_sys_conn_close(BTA_ID_HD, 1, p_cback->addr); // 关闭系统连接HID_DevUnplugDevice(p_cback->addr); // 设备拔出操作// 设置回调数据并调用回调函数cback_data.conn.bda = p_cback->addr;bta_hd_cb.bd_addr = p_cback->addr;(*bta_hd_cb.p_cback)(BTA_HD_VC_UNPLUG_EVT, &cback_data);
}

处理 HID 设备虚拟线缆(VC)拔出完成后的操作。完成关闭系统连接、执行设备拔出操作,并通过回调函数通知上层设备虚拟线缆已拔出。

bta_sys_conn_close

packages/modules/Bluetooth/system/bta/sys/bta_sys_conn.cc
/********************************************************************************* Function         bta_sys_conn_close** Description      Called by BTA subsystems when a connection to the service*                  is closed*** Returns          void*******************************************************************************/
void bta_sys_conn_close(tBTA_SYS_ID id, uint8_t app_id,const RawAddress& peer_addr) {if (bta_sys_cb.prm_cb) {bta_sys_cb.prm_cb(BTA_SYS_CONN_CLOSE, id, app_id, peer_addr);}if (bta_sys_cb.ppm_cb) {bta_sys_cb.ppm_cb(BTA_SYS_CONN_CLOSE, id, app_id, peer_addr);}
}

根据配置情况调用两个回调函数 bta_sys_cb.prm_cbbta_sys_cb.ppm_cb,向这些回调函数传递连接关闭的事件信息以及相关的参数,如系统 ID、应用 ID 和对端设备地址。

HID_DevUnplugDevice

packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
/********************************************************************************* Function         HID_DevUnplugDevice** Description      Unplugs virtual cable from given host** Returns          tHID_STATUS*******************************************************************************/
tHID_STATUS HID_DevUnplugDevice(const RawAddress& addr) {if (hd_cb.device.addr == addr) {hd_cb.device.in_use = FALSE;hd_cb.device.conn.conn_state = HID_CONN_STATE_UNUSED;hd_cb.device.conn.ctrl_cid = 0;hd_cb.device.conn.intr_cid = 0;}return HID_SUCCESS;
}

从给定的主机上拔出虚拟线缆,通过检查设备地址,将设备的使用状态设置为未使用,并将连接状态相关的控制通道 ID 和中断通道 ID 设置为 0。

(*bta_hd_cb.p_cback)(BTA_HD_VC_UNPLUG_EVT, &cback_data)

/********************************************************************************* Function         btif_hd_upstreams_evt** Description      Executes events in btif context** Returns          void*******************************************************************************/
static void btif_hd_upstreams_evt(uint16_t event, char* p_param) {tBTA_HD* p_data = (tBTA_HD*)p_param;log::verbose("event={}", dump_hd_event(event));switch (event) {...case BTA_HD_VC_UNPLUG_EVT:// 检查连接情况并执行清理操作HAL_CBACK(bt_hd_callbacks, connection_state_cb,(RawAddress*)&p_data->conn.bda, BTHD_CONN_STATE_DISCONNECTED);// 检查连接情况并执行清理操作if (bta_dm_check_if_only_hd_connected(p_data->conn.bda)) { // 检查指定地址的设备是否仅连接了 HID 配置文件log::verbose("Removing bonding as only HID profile connected");BTA_DmRemoveDevice(p_data->conn.bda);} else {RawAddress* bd_addr = (RawAddress*)&p_data->conn.bda;log::verbose("Only removing HID data as some other profiles connected");btif_hd_remove_device(*bd_addr);}// 调用虚拟线缆拔出回调函数HAL_CBACK(bt_hd_callbacks, vc_unplug_cb);break;...

BTA_HD_VC_UNPLUG_EVT 事件表示蓝牙 HID 设备的虚拟线缆(VC)已拔出。此事件处理主要包括更新连接状态、根据连接情况执行不同的清理操作,以及调用相应的回调函数通知上层。

btif_hd_remove_device

packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/********************************************************************************* Function         btif_hd_remove_device** Description      Removes plugged device** Returns          void*******************************************************************************/
void btif_hd_remove_device(RawAddress bd_addr) {BTA_HdRemoveDevice(bd_addr);btif_storage_remove_hidd(&bd_addr);
}
BTA_HdRemoveDevice
packages/modules/Bluetooth/system/bta/hd/bta_hd_api.cc
/********************************************************************************* Function         BTA_HdRemoveDevice** Description      This function is called when a device is virtually uncabled** Returns          void*******************************************************************************/
void BTA_HdRemoveDevice(const RawAddress& addr) {log::verbose("");tBTA_HD_DEVICE_CTRL* p_buf =(tBTA_HD_DEVICE_CTRL*)osi_malloc(sizeof(tBTA_HD_DEVICE_CTRL));p_buf->hdr.event = BTA_HD_API_REMOVE_DEVICE_EVT;p_buf->addr = addr;bta_sys_sendmsg(p_buf);
}

在设备的虚拟线缆断开(即设备被视为移除)时被调用。将该消息发送出去,以触发后续的设备移除操作。

bta_hd_better_state_machine(BTA_HD_API_REMOVE_DEVICE_EVT)

packages/modules/Bluetooth/system/bta/hd/bta_hd_main.cccase BTA_HD_CONN_ST:switch (event) {case BTA_HD_API_UNREGISTER_APP_EVT:set_state(BTA_HD_TRANSIENT_TO_INIT_ST);bta_hd_disconnect_act();break;case BTA_HD_API_DISCONNECT_EVT:bta_hd_disconnect_act();break;case BTA_HD_API_ADD_DEVICE_EVT:bta_hd_add_device_act(p_data);break;case BTA_HD_API_REMOVE_DEVICE_EVT:bta_hd_remove_device_act(p_data);break;

bta_hd_remove_device_act

packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/********************************************************************************* Function         bta_hd_remove_device_act** Description** Returns          void*******************************************************************************/
void bta_hd_remove_device_act(tBTA_HD_DATA* p_data) {tBTA_HD_DEVICE_CTRL* p_ctrl = (tBTA_HD_DEVICE_CTRL*)p_data;log::verbose("");HID_DevUnplugDevice(p_ctrl->addr);
}

处理移除蓝牙 HID 设备的操作。调用 HID_DevUnplugDevice 函数来执行设备的虚拟线缆拔出操作。HID_DevUnplugDevice前文有分析。

btif_storage_remove_hidd
packages/modules/Bluetooth/system/btif/src/btif_profile_storage.cc
/********************************************************************************* Function         btif_storage_remove_hidd** Description      Removes hidd bonded device info from nvram** Returns          BT_STATUS_SUCCESS*******************************************************************************/
bt_status_t btif_storage_remove_hidd(RawAddress* remote_bd_addr) {btif_config_remove(remote_bd_addr->ToString(),BTIF_STORAGE_KEY_HID_DEVICE_CABLED);return BT_STATUS_SUCCESS;
}

从非易失性随机访问存储器(NVRAM)中移除与 HID已配对设备相关的信息。

情况2:hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, p_hcon->disc_reason, NULL)

bta_hd_better_state_machine(BTA_HD_INT_CLOSE_EVT)

  case BTA_HD_CONN_ST:switch (event) {...case BTA_HD_INT_CLOSE_EVT:set_state(BTA_HD_IDLE_ST);bta_hd_close_act(p_data);break;...

bta_hd_close_act

packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/********************************************************************************* Function         bta_hd_close_act** Description** Returns          void*******************************************************************************/
void bta_hd_close_act(tBTA_HD_DATA* p_data) {tBTA_HD_CBACK_DATA* p_cback = (tBTA_HD_CBACK_DATA*)p_data;tBTA_HD cback_data;tBTA_HD_EVT cback_event = BTA_HD_CLOSE_EVT;log::verbose("");// 关闭系统连接bta_sys_conn_close(BTA_ID_HD, 1, p_cback->addr); // 检查虚拟线缆拔出标志并执行相应操作if (bta_hd_cb.vc_unplug) {bta_hd_cb.vc_unplug = FALSE;HID_DevUnplugDevice(p_cback->addr);cback_event = BTA_HD_VC_UNPLUG_EVT;}// 设置回调数据并调用回调函数cback_data.conn.bda = p_cback->addr;bta_hd_cb.bd_addr = RawAddress::kEmpty;bta_hd_cb.p_cback(cback_event, &cback_data);
}

处理蓝牙 HID 设备的关闭操作。关闭系统连接,根据 bta_hd_cb.vc_unplug 标志决定是否执行设备的虚拟线缆拔出操作,最后调用回调函数通知上层设备关闭或虚拟线缆拔出事件。

bta_hd_cb.p_cback(BTA_HD_CLOSE_EVT)

packages/modules/Bluetooth/system/btif/src/btif_hd.cccase BTA_HD_CLOSE_EVT:if (btif_hd_cb.forced_disc) { // 意味着设备是被强制断开连接的RawAddress* addr = (RawAddress*)&p_data->conn.bda;log::warn("remote device was forcefully disconnected");btif_hd_remove_device(*addr);btif_hd_cb.forced_disc = FALSE;break;}// 正常断开情况处理HAL_CBACK(bt_hd_callbacks, connection_state_cb,(RawAddress*)&p_data->conn.bda, BTHD_CONN_STATE_DISCONNECTED);break;

BTA_HD_CLOSE_EVT 事件代表蓝牙 HID 设备关闭连接的事件。代码根据 btif_hd_cb.forced_disc 标志的状态,执行不同的操作,要么强制移除设备,要么通知上层设备连接已断开。

正常断开的情况会将设备地址和断开连接的状态 BTHD_CONN_STATE_DISCONNECTED 传递给该回调函数,从而通知上层设备连接已断开。

三、时序图

相关文章:

  • 基于Java和高德开放平台的WebAPI集成实践-以搜索POI2.0为例
  • 什么是深度神经网络
  • VirtualBox中安装并运行ubuntu-24.04.2-desktop虚拟机
  • 控制LED灯设备
  • 专题一:汉诺塔问题:递归算法的精妙解析
  • Spring框架(一)
  • OpenResty反向代理
  • 在Java项目中实现本地语音识别与热点检测,并集成阿里云智能语音服务(优化版)
  • 【Part 2安卓原生360°VR播放器开发实战】第四节|安卓VR播放器性能优化与设备适配
  • Redis设计与实现——单机Redis实现
  • iVX 平台技术解析:图形化与组件化的融合创新
  • 信息系统项目管理师-软考高级(软考高项)​​​​​​​​​​​2025最新(十五)
  • 深入剖析缓存与数据库一致性:Java技术视角下的解决方案与实践
  • java的Stream流处理
  • MySql(进阶)
  • macOS 15 (Sequoia) 解除Gatekeeper限制
  • wget、curl 命令使用场景与命令实践
  • 第八讲 | stack和queue的使用及其模拟实现
  • MySQL 数据库故障排查指南
  • 浏览器的B/S架构和C/S架构
  • 市场监管总局等五部门约谈外卖平台企业
  • 事关心脏健康安全,经导管植入式人工心脏瓣膜国家标准发布
  • 书法需从字外看,书法家、学者吴本清辞世
  • 甩掉“肥胖刺客”,科学减重指南来了
  • 第1现场 | 印巴停火次日:当地民众逐渐恢复正常生活
  • 打击网络谣言、共建清朗家园,中国互联网联合辟谣平台2025年4月辟谣榜