Android 11 Audio音頻系統配置文件解析

在AudioPolicyService的啟動過程中,會去創建AudioPolicyManager對象,進而去解析配置文件

//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface): AudioPolicyManager(clientInterface, false /*forTesting*/)
{loadConfig();
}

loadConfig

//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
void AudioPolicyManager::loadConfig() {if (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) {ALOGE("could not load audio policy configuration file, setting defaults");getConfig().setDefault();}
}

deserializeAudioPolicyXmlConfig

//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) {char audioPolicyXmlConfigFile[AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH];std::vector<const char*> fileNames;status_t ret;//省略fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME);//AUDIO_POLICY_XML_CONFIG_FILE_NAME:audio_policy_configuration.xmlfor (const char* fileName : fileNames) {for (const auto& path : audio_get_configuration_paths()) {snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile),"%s/%s", path.c_str(), fileName);ret = deserializeAudioPolicyFile(audioPolicyXmlConfigFile, &config);//1if (ret == NO_ERROR) {config.setSource(audioPolicyXmlConfigFile);return ret;}}}return ret;
}

audio_get_configuration_paths返回的路徑為“/odm/etc”, “/vendor/etc”,“/system/etc” 。然后遍歷這些路徑,找到audio_policy_configuration.xml文件,然后調用注釋1處的deserializeAudioPolicyFile來解析這個文件

deserializeAudioPolicyFile

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
status_t deserializeAudioPolicyFile(const char *fileName, AudioPolicyConfig *config)
{PolicySerializer serializer;return serializer.deserialize(fileName, config);
}

PolicySerializer::deserialize

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
status_t PolicySerializer::deserialize(const char *configFile, AudioPolicyConfig *config)
{auto doc = make_xmlUnique(xmlParseFile(configFile));xmlNodePtr root = xmlDocGetRootElement(doc.get());//省略:對文件的合法性進行效驗// Lets deserialize children// ModulesModuleTraits::Collection modules;status_t status = deserializeCollection<ModuleTraits>(root, &modules, config);//1if (status != NO_ERROR) {return status;}//設置mHwModulesconfig->setHwModules(modules);//省略return android::OK;
}

注釋1處,開始解析配置文件中的modules標簽。注意modules類型為 ModuleTraits::Collection

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
template <class Trait>
status_t deserializeCollection(const xmlNode *cur,typename Trait::Collection *collection,typename Trait::PtrSerializingCtx serializingContext)
{for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {const xmlNode *child = NULL;if (!xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>(Trait::collectionTag))) {child = cur->xmlChildrenNode;} else if (!xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>(Trait::tag))) {child = cur;}for (; child != NULL; child = child->next) {if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>(Trait::tag))) {auto element = Trait::deserialize(child, serializingContext);//1if (element.isOk()) {status_t status = Trait::addElementToCollection(element, collection);//添加元素} else {return BAD_VALUE;}}}}return NO_ERROR;
}

注釋1處,針對不同的Trait類型,就調用不同的子類的deserialize處理。對于modules標簽,調用ModuleTraits的deserialize處理。

ModuleTraits::deserialize

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
Return<ModuleTraits::Element> ModuleTraits::deserialize(const xmlNode *cur, PtrSerializingCtx ctx)
{std::string name = getXmlAttribute(cur, Attributes::name);//得到名字,如:primaryElement module = new HwModule(name.c_str(), versionMajor, versionMinor);//創建HwModule對象//開始解析mixPorts標簽MixPortTraits::Collection mixPorts;status_t status = deserializeCollection<MixPortTraits>(cur, &mixPorts, NULL);module->setProfiles(mixPorts);//開始解析devicePorts標簽DevicePortTraits::Collection devicePorts;status = deserializeCollection<DevicePortTraits>(cur, &devicePorts, NULL);module->setDeclaredDevices(devicePorts);//開始解析routes標簽RouteTraits::Collection routes;status = deserializeCollection<RouteTraits>(cur, &routes, module.get());module->setRoutes(routes);//開始解析attachedDevices標簽for (const xmlNode *children = cur->xmlChildrenNode; children != NULL;children = children->next) {if (!xmlStrcmp(children->name, reinterpret_cast<const xmlChar*>(childAttachedDevicesTag))) {for (const xmlNode *child = children->xmlChildrenNode; child != NULL;child = child->next) {if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>(childAttachedDeviceTag))) {auto attachedDevice = make_xmlUnique(xmlNodeListGetString(child->doc, child->xmlChildrenNode, 1));if (attachedDevice != nullptr) {sp<DeviceDescriptor> device = module->getDeclaredDevices().getDeviceFromTagName(std::string(reinterpret_cast<const char*>(attachedDevice.get())));ctx->addDevice(device);}}}}//開始解析defaultOutputDevice標簽if (!xmlStrcmp(children->name,reinterpret_cast<const xmlChar*>(childDefaultOutputDeviceTag))) {auto defaultOutputDevice = make_xmlUnique(xmlNodeListGetString(children->doc, children->xmlChildrenNode, 1));if (defaultOutputDevice != nullptr) {sp<DeviceDescriptor> device = module->getDeclaredDevices().getDeviceFromTagName(std::string(reinterpret_cast<const char*>(defaultOutputDevice.get())));if (device != 0 && ctx->getDefaultOutputDevice() == 0) {ctx->setDefaultOutputDevice(device);}}}
}

解析mixPorts標簽

//vendor/etc/audio_policy_configuration.xml
<mixPorts><mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY"><profile name="" format="AUDIO_FORMAT_PCM_16_BIT"samplingRates="44100,48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/></mixPort><mixPort name="spdif_passthrough" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT"><profile name="" format="AUDIO_FORMAT_PCM_16_BIT"samplingRates="32000,44100,48000"channelMasks="AUDIO_CHANNEL_OUT_STEREO"/><profile name="" format="AUDIO_FORMAT_IEC61937"samplingRates="32000,44100,48000"channelMasks="AUDIO_CHANNEL_OUT_STEREO"/></mixPort><mixPort name="hdmi" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT"><profile name="" format="AUDIO_FORMAT_PCM_16_BIT"samplingRates="32000,44100,48000,96000,176400,192000"channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_QUAD,AUDIO_CHANNEL_OUT_5POINT1,AUDIO_CHANNEL_OUT_7POINT1"/><profile name="" format="AUDIO_FORMAT_IEC61937"samplingRates="32000,44100,48000,96000,176400,192000"channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_QUAD,AUDIO_CHANNEL_OUT_5POINT1,AUDIO_CHANNEL_OUT_7POINT1"/><profile name=""/></mixPort><mixPort name="primary input" role="sink"><profile name="" format="AUDIO_FORMAT_PCM_16_BIT"samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO"/></mixPort></mixPorts>

對于mixPorts標簽,會循環調用MixPortTraits的deserialize方法解析其子標簽,也就是mixPort

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
Return<MixPortTraits::Element> MixPortTraits::deserialize(const xmlNode *child,PtrSerializingCtx /*serializingContext*/)
{std::string name = getXmlAttribute(child, Attributes::name);//得到名字:primary output,spdif_passthrough,hdmi,primary inputstd::string role = getXmlAttribute(child, Attributes::role);//sink或者sourceaudio_port_role_t portRole = (role == Attributes::roleSource) ?AUDIO_PORT_ROLE_SOURCE : AUDIO_PORT_ROLE_SINK;Element mixPort = new IOProfile(name, portRole);//創建IOProfile對象AudioProfileTraits::Collection profiles;status_t status = deserializeCollection<AudioProfileTraits>(child, &profiles, NULL);//解析mixPort標簽下的profile// The audio profiles are in order of listed in audio policy configuration file.// Sort audio profiles accroding to the format.sortAudioProfiles(profiles);mixPort->setAudioProfiles(profiles);//添加到mProfiles鏈表中//省略

根據解析mixPort得到的name和role 創建IOProfile對象,然后開始解析mixPort標簽下的profile ,最后將解析結果添加到mProfiles鏈表中。來看一下profile 的解析

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
Return<AudioProfileTraits::Element> AudioProfileTraits::deserialize(const xmlNode *cur,PtrSerializingCtx /*serializingContext*/)
{std::string samplingRates = getXmlAttribute(cur, Attributes::samplingRates);//采樣率std::string format = getXmlAttribute(cur, Attributes::format);//采樣位數std::string channels = getXmlAttribute(cur, Attributes::channelMasks);//通道Element profile = new AudioProfile(formatFromString(format, gDynamicFormat),channelMasksFromString(channels, ","),samplingRatesFromString(samplingRates, ","));//創建AudioProfile對象profile->setDynamicFormat(profile->getFormat() == gDynamicFormat);profile->setDynamicChannels(profile->getChannels().empty());profile->setDynamicRate(profile->getSampleRates().empty());return profile;
}

該部分解析完成后,可得到以下結果:每個mixPort標簽都會轉化成IOProfile對象,mixPort標簽下的profile,都會轉化成AudioProfile對象,并將該對象添加到對應IOProfile的mProfiles鏈表。
回到modules的解析方法中,mixPorts標簽解析完成后,調用HwModule的setProfiles保存解析的結果

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
void HwModule::setProfiles(const IOProfileCollection &profiles)
{for (size_t i = 0; i < profiles.size(); i++) {//遍歷得到的IOProfileaddProfile(profiles[i]);}
}status_t HwModule::addProfile(const sp<IOProfile> &profile)
{switch (profile->getRole()) {case AUDIO_PORT_ROLE_SOURCE:return addOutputProfile(profile);case AUDIO_PORT_ROLE_SINK:return addInputProfile(profile);case AUDIO_PORT_ROLE_NONE:return BAD_VALUE;}return BAD_VALUE;
}

可以看出,對于role為sink的IOProfile,則添加到HwModule的mInputProfiles中,比如上面場景的primary input。對于role為source的IOProfile,則是添加到mOutputProfiles中,比如上面場景的primary output,hdmi等。比如針對上面的場景,經過解析之后可以得到以下信息
在這里插入圖片描述
解析devicePorts標簽

<devicePorts><devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink"></devicePort><devicePort tagName="Wired Headset" type="AUDIO_DEVICE_OUT_WIRED_HEADSET" role="sink"></devicePort><devicePort tagName="Wired Headphones" type="AUDIO_DEVICE_OUT_WIRED_HEADPHONE" role="sink"></devicePort><devicePort tagName="BT SCO" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO" role="sink"></devicePort><devicePort tagName="BT SCO Headset" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET" role="sink"></devicePort><devicePort tagName="BT SCO Car Kit" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT" role="sink"></devicePort><devicePort tagName="HDMI Out" type="AUDIO_DEVICE_OUT_AUX_DIGITAL" role="sink"></devicePort><devicePort tagName="SPDIF Out" type="AUDIO_DEVICE_OUT_SPDIF" role="sink"></devicePort><devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source"></devicePort><devicePort tagName="Wired Headset Mic" type="AUDIO_DEVICE_IN_WIRED_HEADSET" role="source"></devicePort><devicePort tagName="BT SCO Headset Mic" type="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET" role="source"></devicePort><devicePort tagName="HDMIIn" type="AUDIO_DEVICE_IN_HDMI" role="source"></devicePort></devicePorts>

對于devicePorts標簽,循環調用DevicePortTraits的deserialize來解析其子標簽

Return<DevicePortTraits::Element> DevicePortTraits::deserialize(const xmlNode *cur,PtrSerializingCtx /*serializingContext*/)
{std::string name = getXmlAttribute(cur, Attributes::tagName);//得到名字:Speaker,Wired Headset等std::string typeName = getXmlAttribute(cur, Attributes::type);//得到類型std::string role = getXmlAttribute(cur, Attributes::role);//sink或者sourceaudio_port_role_t portRole = (role == Attributes::roleSource) ?AUDIO_PORT_ROLE_SOURCE : AUDIO_PORT_ROLE_SINK;//省略Element deviceDesc = new DeviceDescriptor(type, name, address, encodedFormats);//創建DeviceDescriptor對象AudioProfileTraits::Collection profiles;status_t status = deserializeCollection<AudioProfileTraits>(cur, &profiles, NULL);//解析devicePort標簽下的profile標簽// The audio profiles are in order of listed in audio policy configuration file.// Sort audio profiles accroding to the format.sortAudioProfiles(profiles);deviceDesc->setAudioProfiles(profiles);//省略

首先解析得到name和type,然后創建DeviceDescriptor對象,最后也是解析profile標簽并將得到的AudioProfile添加到對應的DeviceDescriptor對象的mProfiles鏈表中。解析完成后,回到ModuleTraits::deserialize函數中,調用HwModule的setDeclaredDevices保存解析的結果

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
void HwModule::setDeclaredDevices(const DeviceVector &devices)
{mDeclaredDevices = devices;//保存到mDeclaredDevices中for (size_t i = 0; i < devices.size(); i++) {mPorts.add(devices[i]);//保存到mPorts中}
}

最后將結果保存在HwModule的mDeclaredDevices和mPorts中。針對上面的場景,可以得到以下解析結果
在這里插入圖片描述
解析routes標簽

<routes><route type="mix" sink="Speaker"sources="primary output"/><route type="mix" sink="Wired Headset"sources="primary output"/><route type="mix" sink="Wired Headphones"sources="primary output"/><route type="mix" sink="BT SCO"sources="primary output"/><route type="mix" sink="BT SCO Headset"sources="primary output"/><route type="mix" sink="BT SCO Car Kit"sources="primary output"/><route type="mix" sink="HDMI Out"sources="primary output,hdmi"/><route type="mix" sink="SPDIF Out"sources="primary output,spdif_passthrough"/><route type="mix" sink="primary input"sources="Built-In Mic,Wired Headset Mic,BT SCO Headset Mic,HDMIIn"/></routes>

對于routes下的子標簽,調用RouteTraits的deserialize解析處理

Return<RouteTraits::Element> RouteTraits::deserialize(const xmlNode *cur, PtrSerializingCtx ctx)
{std::string type = getXmlAttribute(cur, Attributes::type);//解析typeaudio_route_type_t routeType = (type == Attributes::typeMix) ?AUDIO_ROUTE_MIX : AUDIO_ROUTE_MUX;Element route = new AudioRoute(routeType);//創建AudioRoute對象std::string sinkAttr = getXmlAttribute(cur, Attributes::sink);//解析sinksp<PolicyAudioPort> sink = ctx->findPortByTagName(sinkAttr);//根據解析得到的sink的名字,從ports鏈表中找到DeviceDescriptorroute->setSink(sink);//將找到的DeviceDescriptor添加到AudioRoute的mSink中std::string sourcesAttr = getXmlAttribute(cur, Attributes::sources);//解析sourcesPolicyAudioPortVector sources;std::unique_ptr<char[]> sourcesLiteral{strndup(sourcesAttr.c_str(), strlen(sourcesAttr.c_str()))};char *devTag = strtok(sourcesLiteral.get(), ",");while (devTag != NULL) {if (strlen(devTag) != 0) {sp<PolicyAudioPort> source = ctx->findPortByTagName(devTag);//根據source,可以找到對應的IOProfilesources.add(source);}devTag = strtok(NULL, ",");}sink->addRoute(route);//將該route添加到sink的mRoutes鏈表中for (size_t i = 0; i < sources.size(); i++) {sp<PolicyAudioPort> source = sources.itemAt(i);source->addRoute(route);//將該route添加到source的mRoutes鏈表中}route->setSources(sources);//將source添加到AudioRoute的mSources中return route;}

該方法主要是解析sink和source,然后根據其名字,分別找到對應的PolicyAudioPort,設置AudioRoute的mSink和mSources。回到ModuleTraits::deserialize函數中,調用HwModule的setRoutes保存解析的結果

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
void HwModule::setRoutes(const AudioRouteVector &routes)
{mRoutes = routes;//將結果保存在mRoutes 中// Now updating the streams (aka IOProfile until now) supported devicesrefreshSupportedDevices();
}

主要是將解析的結果,保存在HwModule的mRoutes 中。針對以上場景,可以得到如下結果
在這里插入圖片描述
解析attachedDevices標簽

<attachedDevices><item>Speaker</item><item>Built-In Mic</item><item>HDMIIn</item>
</attachedDevices>

attachedDevices標簽的解析,就在ModuleTraits::deserialize函數中

for (const xmlNode *children = cur->xmlChildrenNode; children != NULL;children = children->next) {if (!xmlStrcmp(children->name, reinterpret_cast<const xmlChar*>(childAttachedDevicesTag))) {ALOGV("%s: %s %s found", __func__, tag, childAttachedDevicesTag);for (const xmlNode *child = children->xmlChildrenNode; child != NULL;child = child->next) {if (!xmlStrcmp(child->name,reinterpret_cast<const xmlChar*>(childAttachedDeviceTag))) {auto attachedDevice = make_xmlUnique(xmlNodeListGetString(child->doc, child->xmlChildrenNode, 1));if (attachedDevice != nullptr) {sp<DeviceDescriptor> device = module->getDeclaredDevices().getDeviceFromTagName(std::string(reinterpret_cast<const char*>(attachedDevice.get())));//從mDeclaredDevices 鏈表中根據名字找到DeviceDescriptorctx->addDevice(device);//更加type類型,分別添加到AudioPolicyConfig的mOutputDevices和mInputDevices中}}}}

來看一下addDevice方法

///frameworks/av/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.hvoid addDevice(const sp<DeviceDescriptor> &device){if (audio_is_output_device(device->type())) {mOutputDevices.add(device);} else if (audio_is_input_device(device->type())) {mInputDevices.add(device);}}

解析defaultOutputDevice標簽

<defaultOutputDevice>Speaker</defaultOutputDevice>

對于該標簽的解析比較簡單,主要從mDeclaredDevices 鏈表中根據名字找到對應的DeviceDescriptor,然后將其賦值給AudioPolicyConfig的mDefaultOutputDevice成員

回到PolicySerializer::deserialize方法,當moudles解析完成后,會將解析得到的HwModule保存在AudioPolicyConfig的mHwModules中

//frameworks/av/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.hvoid setHwModules(const HwModuleCollection &hwModules){mHwModules = hwModules;}

總結

  1. 解析mixPorts標簽,每個mixPort子項,都會構建一個IOProfile對象,對于mixPort標簽下的每個profile項,都會構建一個AudioProfile對象,并將AudioProfile對象放到對應IOProfile對象的mProfiles中。最后這些IOProfile對象,根據type類型,分別添加到HwModule的mOutputProfiles和mInputProfiles中。并且這些IOProfile也會添加到HwModule的mPorts中
  2. 解析devicePorts標簽,每個devicePort都會創建DeviceDescriptor對象,最后這些DeviceDescriptor都會添加到HwModule的mDeclaredDevices和mPorts中
  3. 解析routes標簽,對每個route,都會創建AudioRoute對象。分別根據sink和source的名字,從前面得到的mPorts中找到對應項,設置AudioRoute的mSink和mSource,最后將這些AudioRoute添加到HwModule的mRoutes中
  4. AudioPolicyConfig 有幾個成員
HwModuleCollection &mHwModules; /**< Collection of Module, with Profiles, i.e. Mix Ports. */
DeviceVector &mOutputDevices;
DeviceVector &mInputDevices;
sp<DeviceDescriptor> &mDefaultOutputDevice;

其中mHwModules保存的是modules標簽下的每個HwModule,解析attachedDevices標簽時,會根據其名字找到對應的DeviceDescriptor,然后根據這些DeviceDescriptor的type,分別放入mOutputDevices和mInputDevices中。解析defaultOutputDevice標簽時,根據名字找到對應的DeviceDescriptor,然后保存在mDefaultOutputDevice中

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/18573.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/18573.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/18573.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MySQL目錄和文件

MySQL目錄和文件 bin目錄 存儲一些mysql腳本比如mysqld、mysqld-self等等&#xff0c;用于執行mysql一些操作 數據目錄 show variables like datadir;--查看數據目錄位置每一個數據庫都有一個和數據庫名相同的文件夾&#xff1b;MySQL5.7開始每創建一個表&#xff0c;在Innod…

Python機器學習 Tensorflow + keras 實現CNN

一、實驗目的 1. 了解SkLearn Tensorlow使用方法 2. 了解SkLearn keras使用方法 二、實驗工具&#xff1a; 1. SkLearn 三、實驗內容 &#xff08;貼上源碼及結果&#xff09; 使用Tensorflow對半環形數據集分 #encoding:utf-8import numpy as npfrom sklearn.datasets i…

Dynadot API調整一覽

關于Dynadot Dynadot是通過ICANN認證的域名注冊商&#xff0c;自2002年成立以來&#xff0c;服務于全球108個國家和地區的客戶&#xff0c;為數以萬計的客戶提供簡潔&#xff0c;優惠&#xff0c;安全的域名注冊以及管理服務。 Dynadot平臺操作教程索引&#xff08;包括域名郵…

AI Agent教育行業落地案例

【AI賦能教育】揭秘Duolingo背后的AI Agent&#xff0c;讓學習更高效、更有趣&#xff01; ©作者|Blaze 來源|神州問學 引言 隨著科技的迅猛發展&#xff0c;人工智能技術已經逐步滲透到我們生活的各個方面。而隨著AI技術的廣泛應用&#xff0c;教育培訓正引領著一場新的…

149.二叉樹:二叉樹的前序遍歷(力扣)

代碼解決 這段代碼實現了二叉樹的前序遍歷&#xff0c;前序遍歷的順序是&#xff1a;訪問根節點 -> 遞歸遍歷左子樹 -> 遞歸遍歷右子樹。以下是詳細解釋&#xff0c;包括各個部分的注釋&#xff1a; // 二叉樹節點的定義 struct TreeNode {int val; // 節…

php -v在cmd中正常顯示 在vscode中卻報錯

效果展示 原因 在vscode中 終端是 PowerShell PowerShell 默認情況下它不會繼承系統的PATH環境變量 解決方案 使用CMD作為終端 打開VSCode設置&#xff08;File > Preferences > Settings 或 Ctrl,&#xff09;。搜索 terminal.integrated.shell.windows。更改其值…

springboot集成nacos

springboot集成nacos 1.版本2. POM依賴3. nacos服務3.1 下載nacos壓縮包3.2 啟動nacos 4. yaml配置5.Demo5.1 配置中心簡單格式獲取方式普通方式還可以再啟動類上添加注解完成5.2 獲取json格式的demo5.2 自動注冊根據yaml配置 1.版本 nacos版本:2.3.2 springboot版本&#xff…

【已解決】使用StringUtils.hasLength參數輸入空格仍然添加成功定價為負數仍然添加成功

Bug情景 今天在做功能測試時&#xff0c;發現使用使用StringUtils.hasLength&#xff08;&#xff09;方法以及定價為負數時&#xff0c;添加圖書仍然成功 思考過程 0.1 當時在做參數檢驗時用了spring提供的StringUtils工具包&#xff0c;百度/大數據模型說&#xff1a; 0.2…

Redis:redis基礎

Redis Remote Dictionary Service即遠程字典服務 一個基于內存的key-value結構數據庫,在開發中常常作為緩存存儲不經常被改變的數據 基于內存存儲,讀寫性能高 在企業中應用廣泛 Redis介紹 用C語言開發的開源高性能鍵值對數據庫,可以達到10w的qps,可以存儲豐富的value類型…

【ubuntu20】--- 定時同步文件

在編程的藝術世界里&#xff0c;代碼和靈感需要尋找到最佳的交融點&#xff0c;才能打造出令人為之驚嘆的作品。而在這座秋知葉i博客的殿堂里&#xff0c;我們將共同追尋這種完美結合&#xff0c;為未來的世界留下屬于我們的獨特印記。 【Linux命令】--- 多核壓縮命令大全&…

肉類食品解凍污水處理設備功能特點

諸城市鑫淼環保小編帶大家了解一下肉類食品解凍污水處理設備功能特點 肉類食品解凍污水處理設備是專門用于處理肉類加工過程中產生的解凍廢水的設備。這些設備在保障肉類食品生產過程中的衛生安全同時&#xff0c;也有效處理了廢水&#xff0c;避免了環境污染。以下是對肉類食品…

VM虛擬機共享文件夾fuse: bad mount point `/mnt/hgfs‘: No such file or directory

報錯顯示掛載點 /mnt/hgfs 不存在&#xff0c;你需要先創建這個目錄。可以按照以下步驟進行操作&#xff1a; 創建掛載點目錄&#xff1a; sudo mkdir -p /mnt/hgfs 手動掛載共享文件夾&#xff1a; sudo vmhgfs-fuse .host:/ /mnt/hgfs -o allow_other 確保每次啟動時自動…

液氮罐內部會污染嗎

液氮罐是一種常見的存儲液態氮的設備&#xff0c;廣泛應用于科研、生物醫藥、食品冷凍等領域。但是&#xff0c;人們對于液氮罐內部是否會產生污染一直存在疑問。 我們來看液氮罐內部可能的污染源。液氮罐內部主要存在以下幾種潛在的污染來源&#xff1a;氣體污染、雜質污染、…

C++ | Leetcode C++題解之第117題填充每個節點的下一個右側節點指針II

題目&#xff1a; 題解&#xff1a; class Solution { public:void handle(Node* &last, Node* &p, Node* &nextStart) {if (last) {last->next p;} if (!nextStart) {nextStart p;}last p;}Node* connect(Node* root) {if (!root) {return nullptr;}Node *…

推券客CMS淘寶優惠券網站源碼

推券客CMS淘寶優惠券網站源碼是一個以PHPMySQL進行開發的PHP淘寶客優惠券網站。支持電腦站、手機站以及微信公眾號查券。支持多級代理返利和阿里媽媽最新的渠道管理等功能。 五大優勢 一、全開源 推券客cms網站程序數據庫完全開源,目前市場上基本都是以下2種淘寶客系統 第一…

LeetCode - 雙指針(Two Pointers) 算法集合 [對撞指針、快慢指針、滑動窗口、雙鏈遍歷]

歡迎關注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/139270999 雙指針算法是一種常見且靈活的技巧&#xff0c;通過使用兩個指針協同完成任務。這些指針可以指向不同的元素&#xff0c;具體應用取決于…

Java中的異常處理策略:編寫健壯的軟件

異常處理是Java編程中一個重要的方面&#xff0c;正確的異常處理策略可以使軟件更加健壯和易于維護。本文將詳細探討Java中的異常處理機制&#xff0c;介紹常見的異常類&#xff0c;以及提供有效的異常處理技巧和最佳實踐。 #### 1. Java異常類別 Java中的異常分為兩大類&…

Clickhouse 字符串函數使用總結—— Clickhouse基礎篇(七)

文章目錄 判空非空判斷字符串長度左補齊字符串右補齊字符串字符串轉小寫字符串轉大寫重復字符串拼接字符串函數計算子串base64編碼base64解碼判斷開頭字符串判斷結尾字符串刪除空白字符從HTML提取純文本字符串部分替換字符串全部替換字符串正則部分替換字符串正則全部替換計算子…

Spring Boot 與 OpenJ9 的 Docker 集成:提升 Java 應用性能的新選擇

## 引言 隨著 Docker 的普及&#xff0c;越來越多的開發者開始使用 Docker 來部署和管理他們的應用。在這種趨勢下&#xff0c;將 Spring Boot 與 OpenJ9 結合使用&#xff0c;可以為 Java 應用帶來更高的性能和更低的資源占用。本文將介紹如何在 Docker 環境中使用 Spring Bo…

回顧封裝、繼承和多態的概念,并給出相關示例

封裝、繼承和多態是面向對象編程&#xff08;OOP&#xff09;的三個核心概念。下面我將分別解釋這些概念&#xff0c;并給出相應的示例。 封裝 概念&#xff1a;封裝是將數據&#xff08;變量&#xff09;和操作數據的方法&#xff08;函數&#xff09;組合到一個類中&#x…