Android11 wifi开启源码分析

       

目录

一、APP层源码分析

1.1、寻找页面activity

1.2、寻找页面开关按钮布局

二,framework层代码分析

2.1 开启wifi入口

2.2 WiFiNative

三,HAL层代码分析


         这段时间撸了WIFI开启流程源码,本着前人栽树后人乘凉的原则,有志于android系统开发的新同学们提供一盏明灯,照亮你们前行。

        本人撸代码风格,喜欢从app撸到kernel,启航出发。

一、APP层源码分析


1.1、寻找页面activity

adb shell dumpsys window | grep "mCurrentFocus"

执行上面代码后,

得到页面WifiSettings2Activity,

由《Android11 Settings详解》文章分析可知,对应页面为WifiSettings2.java,

1.2、寻找页面开关按钮布局

    public void onCreate(Bundle icicle) {super.onCreate(icicle);// TODO(b/37429702): Add animations and preference comparator back after initial screen is// loaded (ODR).setAnimationAllowed(false);addPreferences();mIsRestricted = isUiRestricted();}

通过addPreferences()加载布局文件,

    private void addPreferences() {addPreferencesFromResource(R.xml.wifi_settings2);mConnectedWifiEntryPreferenceCategory = findPreference(PREF_KEY_CONNECTED_ACCESS_POINTS);mWifiEntryPreferenceCategory = findPreference(PREF_KEY_ACCESS_POINTS);mConfigureWifiSettingsPreference = findPreference(PREF_KEY_CONFIGURE_WIFI_SETTINGS);mSavedNetworksPreference = findPreference(PREF_KEY_SAVED_NETWORKS);mAddWifiNetworkPreference = new AddWifiNetworkPreference(getPrefContext());mStatusMessagePreference = findPreference(PREF_KEY_STATUS_MESSAGE);mDataUsagePreference = findPreference(PREF_KEY_DATA_USAGE);mDataUsagePreference.setVisible(DataUsageUtils.hasWifiRadio(getContext()));mDataUsagePreference.setTemplate(NetworkTemplate.buildTemplateWifiWildcard(),0 /*subId*/,null /*service*/);}

对应的布局文件如下

<PreferenceScreenxmlns:android="http://schemas.android.com/apk/res/android"xmlns:settings="http://schemas.android.com/apk/res-auto"android:title="@string/wifi_settings"settings:keywords="@string/keywords_wifi"><com.android.settings.wifi.LinkablePreferenceandroid:key="wifi_status_message"/><PreferenceCategoryandroid:key="connected_access_point"android:layout="@layout/preference_category_no_label"/><PreferenceCategoryandroid:key="access_points"android:layout="@layout/preference_category_no_label"/><Preferenceandroid:key="configure_wifi_settings"android:title="@string/wifi_configure_settings_preference_title"settings:allowDividerAbove="true"android:fragment="com.android.settings.wifi.ConfigureWifiSettings"/><Preferenceandroid:key="saved_networks"android:title="@string/wifi_saved_access_points_label"android:fragment="com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsWifiSettings2"/><com.android.settings.datausage.DataUsagePreferenceandroid:key="wifi_data_usage"android:title="@string/wifi_data_usage"/>
</PreferenceScreen>

发现并没有WIFI打开按钮对应的布局,纳尼?代码撸错了?

在WifiSettings2.java文件里面寻找蛛丝马迹,WifiSettings2本质是fragment,在onViewCreated接口中发现,

    public void onViewCreated(View view, Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);final Activity activity = getActivity();if (activity != null) {mProgressHeader = setPinnedHeaderView(R.layout.progress_header).findViewById(R.id.progress_bar_animation);setProgressBarVisible(false);}((SettingsActivity) activity).getSwitchBar().setSwitchBarText(R.string.wifi_settings_master_switch_title,R.string.wifi_settings_master_switch_title);}

跟着走,到SettingsActivity.java文件。哇哦,有点感觉了,哈哈。对应的布局文件settings_main_prefs.xml,是所有设置页面的SwitchBar共有的,无需fragment增加布局,见下。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_height="match_parent"android:layout_width="match_parent"><com.android.settings.widget.SwitchBarandroid:id="@+id/switch_bar"android:layout_height="?android:attr/actionBarSize"android:layout_width="match_parent"android:theme="?attr/switchBarTheme"/><FrameLayoutandroid:id="@+id/main_content"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"/>

到这里,终于看到庐山真面目了,原来WIFI开关藏在这里,我们继续撸。在文件WifiSettings.java文件中,构建了WifiEnabler对象,持有SwitchBarController对象,来控制SwitchBar找个UI组件,

    private WifiEnabler createWifiEnabler() {final SettingsActivity activity = (SettingsActivity) getActivity();return new WifiEnabler(activity, new SwitchBarController(activity.getSwitchBar()),mMetricsFeatureProvider);}

在WifiEnabler.java文件中,控制开关代码如下,

    public boolean onSwitchToggled(boolean isChecked) {//Do nothing if called as a result of a state machine eventif (mStateMachineEvent) {return true;}// Show toast message if Wi-Fi is not allowed in airplane modeif (isChecked && !WirelessUtils.isRadioAllowed(mContext, Settings.Global.RADIO_WIFI)) {Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();// Reset switch to off. No infinite check/listener loop.mSwitchWidget.setChecked(false);return false;}if (isChecked) {mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_WIFI_ON);} else {// Log if user was connected at the time of switching off.mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_WIFI_OFF,mConnected.get());}//这里控制WIFI开关,调用了wifimanagerif (!mWifiManager.setWifiEnabled(isChecked)) {// ErrormSwitchWidget.setEnabled(true);Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show();}return true;}

mWifiManager.setWifiEnabled(isChecked),看到这行调用很兴奋,有没有?

APP层面代码撸完了,接下来到framework层,follow me!

二,framework层代码分析

接着第一章讲解,

2.1 开启wifi入口

在文件WifiManager.java中,

    public boolean setWifiEnabled(boolean enabled) {try {return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

调用到WifiServiceImpl.java文件中,

    @Overridepublic synchronized boolean setWifiEnabled(String packageName, boolean enable) {mWifiMetrics.incrementNumWifiToggles(isPrivileged, enable);//核心入口mActiveModeWarden.wifiToggled();return true;}

到文件ActiveModeWarden.java文件中,

    /** Wifi has been toggled. */public void wifiToggled() {mWifiController.sendMessage(WifiController.CMD_WIFI_TOGGLED);}

接下来由ActiveModeWarden.java文件内部的WifiController处理CMD_WIFI_TOGGLED消息

在打开wifi的情况下,此时wifi是disabled,由DisabledState处理,

        class DisabledState extends BaseState {@Overridepublic void enter() {log("DisabledState.enter()");super.enter();if (hasAnyModeManager()) {Log.e(TAG, "Entered DisabledState, but has active mode managers");}}@Overridepublic void exit() {log("DisabledState.exit()");super.exit();}@Overridepublic boolean processMessageFiltered(Message msg) {switch (msg.what) {case CMD_WIFI_TOGGLED:case CMD_SCAN_ALWAYS_MODE_CHANGED:if (shouldEnableSta()) {startClientModeManager();transitionTo(mEnabledState);}break;
..................................................................

走到startClientModeManager(),

    private boolean startClientModeManager() {Log.d(TAG, "Starting ClientModeManager");ClientListener listener = new ClientListener();ClientModeManager manager = mWifiInjector.makeClientModeManager(listener);listener.setActiveModeManager(manager);manager.start();if (!switchClientModeManagerRole(manager)) {return false;}mActiveModeManagers.add(manager);return true;}

走到ClientModeManager.java,

    public void start() {mTargetRole = ROLE_CLIENT_SCAN_ONLY;mStateMachine.sendMessage(ClientModeStateMachine.CMD_START);}

走到 mWifiNative.setupInterfaceForClientInScanMode( mWifiNativeInterfaceCallback),setupInterfaceForClientInScanMode传入了一个mWifiNativeInterfaceCallback用来来自native层的iface状态回调,

        private class IdleState extends State {@Overridepublic void enter() {Log.d(TAG, "entering IdleState");mClientInterfaceName = null;mIfaceIsUp = false;}@Overridepublic boolean processMessage(Message message) {switch (message.what) {case CMD_START:// Always start in scan mode first.mClientInterfaceName =mWifiNative.setupInterfaceForClientInScanMode(mWifiNativeInterfaceCallback);if (TextUtils.isEmpty(mClientInterfaceName)) {Log.e(TAG, "Failed to create ClientInterface. Sit in Idle");mModeListener.onStartFailure();break;}transitionTo(mScanOnlyModeState);break;

2.2 WiFiNative

上面涉及到setupInterfaceForClientInScanMode(mWifiNativeInterfaceCallback);走到WifiNative.java文件,

// 1. 初始化驱动和vendor hal
startHal();
// 2. 初始化interface
Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_STA_FOR_SCAN);
iface.externalListener = interfaceCallback;
iface.name = createStaIface(iface, requestorWs);
// 3. 初始化wificond
mWifiCondManager.setupInterfaceForClientMode(iface.name, Runnable::run,new NormalScanEventCallback(iface.name),new PnoScanEventCallback(iface.name));//4. 监听interface的down/up
iface.networkObserver = new NetworkObserverInternal(iface.id);
registerNetworkObserver(iface.networkObserver)// 5. 启动supplicant监听(但是此时supplicant进程还未启动)
mWifiMonitor.startMonitoring(iface.name);
onInterfaceStateChanged(iface, isInterfaceUp(iface.name));
mWifiVendorHal.enableLinkLayerStats(iface.name);// 6. 获取芯片支持的wifi feature
iface.featureSet = getSupportedFeatureSetInternal(iface.name);

接下来进入另外一个世界,hal层。

WifiNative -> WifiVendorHal -> HalDeviceManager

三,HAL层代码分析

WifiNative.startHal() -> WifiVendorHal.startVendorHal() -> HalDeviceManager.start() -> HalDeviceManager.startWifi(),代码流程如下,

a, WifiNative#startHal()

    private boolean startHal() {synchronized (mLock) {if (!mIfaceMgr.hasAnyIface()) {if (mWifiVendorHal.isVendorHalSupported()) {if (!mWifiVendorHal.startVendorHal()) {Log.e(TAG, "Failed to start vendor HAL");return false;}} else {Log.i(TAG, "Vendor Hal not supported, ignoring start.");}}return true;}}

b, WifiVendorHal#startVendorHal

    public boolean startVendorHal() {synchronized (sLock) {if (!mHalDeviceManager.start()) {mLog.err("Failed to start vendor HAL").flush();return false;}mLog.info("Vendor Hal started successfully").flush();return true;}}

c,HalDeviceManager#start()

    private boolean startWifi() {if (VDBG) Log.d(TAG, "startWifi");initIWifiIfNecessary();synchronized (mLock) {try {if (mWifi == null) {Log.w(TAG, "startWifi called but mWifi is null!?");return false;} else {int triedCount = 0;while (triedCount <= START_HAL_RETRY_TIMES) {//核心调用,调用hal层startWifiStatus status = mWifi.start();if (status.code == WifiStatusCode.SUCCESS) {initIWifiChipDebugListeners();managerStatusListenerDispatch();if (triedCount != 0) {Log.d(TAG, "start IWifi succeeded after trying "+ triedCount + " times");}return true;} else if (status.code == WifiStatusCode.ERROR_NOT_AVAILABLE) {// Should retry. Hal might still be stopping.Log.e(TAG, "Cannot start IWifi: " + statusString(status)+ ", Retrying...");try {Thread.sleep(START_HAL_RETRY_INTERVAL_MS);} catch (InterruptedException ignore) {// no-op}triedCount++;} else {// Should not retry on other failures.Log.e(TAG, "Cannot start IWifi: " + statusString(status));return false;}}Log.e(TAG, "Cannot start IWifi after trying " + triedCount + " times");return false;}} catch (RemoteException e) {Log.e(TAG, "startWifi exception: " + e);return false;}}}
 mWifi.start()关键行,调用hal层wifi,hardware/interface/wifi1.6/default/wifi.cpp
Return<void> Wifi::start(start_cb hidl_status_cb) {return validateAndCall(this, WifiStatusCode::ERROR_UNKNOWN, &Wifi::startInternal,hidl_status_cb);
}WifiStatus Wifi::startInternal() {// ... ...WifiStatus wifi_status = initializeModeControllerAndLegacyHal();// ... ...// Create the chip instance once the HAL is started.android::hardware::wifi::V1_0::ChipId chipId = kPrimaryChipId;for (auto& hal : legacy_hals_) {chips_.push_back(new WifiChip(chipId, chipId == kPrimaryChipId, hal, mode_controller_,std::make_shared<iface_util::WifiIfaceUtil>(iface_tool_, hal),feature_flags_, on_subsystem_restart_callback));chipId++;}
}WifiStatus Wifi::initializeModeControllerAndLegacyHal() {if (!mode_controller_->initialize()) {LOG(ERROR) << "Failed to initialize firmware mode controller";return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);}legacy_hal::wifi_error legacy_status = legacy_hal_->initialize();if (legacy_status != legacy_hal::WIFI_SUCCESS) {LOG(ERROR) << "Failed to initialize legacy HAL: "<< legacyErrorToString(legacy_status);return createWifiStatusFromLegacyError(legacy_status);}return createWifiStatus(WifiStatusCode::SUCCESS);
}
legacy_hal_->initialize()关键句调用,
wifi_error WifiLegacyHal::initialize() {LOG(DEBUG) << "Initialize legacy HAL";// TODO: Add back the HAL Tool if we need to. All we need from the HAL tool// for now is this function call which we can directly call.if (!initHalFuncTableWithStubs(&global_func_table_)) {LOG(ERROR)<< "Failed to initialize legacy hal function table with stubs";return WIFI_ERROR_UNKNOWN;}wifi_error status = init_wifi_vendor_hal_func_table(&global_func_table_);if (status != WIFI_SUCCESS) {LOG(ERROR) << "Failed to initialize legacy hal function table";}return status;
}

我们以libwifi-hal-qcom为例,其init_wifi_vendor_hal_func_table()方法如下,只是为函数指针赋值,

// hardware/qcom/wlan/qcwcn/wifi_hal/wifi_hal.cpp
/*initialize function pointer table with Qualcomm HAL API*/
wifi_error init_wifi_vendor_hal_func_table(wifi_hal_fn *fn) {if (fn == NULL) {return WIFI_ERROR_UNKNOWN;}fn->wifi_initialize = wifi_initialize;fn->wifi_wait_for_driver_ready = wifi_wait_for_driver_ready;fn->wifi_cleanup = wifi_cleanup;fn->wifi_event_loop = wifi_event_loop;fn->wifi_get_supported_feature_set = wifi_get_supported_feature_set;fn->wifi_get_concurrency_matrix = wifi_get_concurrency_matrix;fn->wifi_set_scanning_mac_oui =  wifi_set_scanning_mac_oui;fn->wifi_get_ifaces = wifi_get_ifaces;fn->wifi_get_iface_name = wifi_get_iface_name;fn->wifi_set_iface_event_handler = wifi_set_iface_event_handler;fn->wifi_reset_iface_event_handler = wifi_reset_iface_event_handler;fn->wifi_start_gscan = wifi_start_gscan;fn->wifi_stop_gscan = wifi_stop_gscan;fn->wifi_get_cached_gscan_results = wifi_get_cached_gscan_results;fn->wifi_set_bssid_hotlist = wifi_set_bssid_hotlist;fn->wifi_reset_bssid_hotlist = wifi_reset_bssid_hotlist;fn->wifi_set_significant_change_handler = wifi_set_significant_change_handler;fn->wifi_reset_significant_change_handler = wifi_reset_significant_change_handler;fn->wifi_get_gscan_capabilities = wifi_get_gscan_capabilities;fn->wifi_set_link_stats = wifi_set_link_stats;fn->wifi_get_link_stats = wifi_get_link_stats;fn->wifi_clear_link_stats = wifi_clear_link_stats;fn->wifi_get_valid_channels = wifi_get_valid_channels;fn->wifi_rtt_range_request = wifi_rtt_range_request;fn->wifi_rtt_range_cancel = wifi_rtt_range_cancel;fn->wifi_get_rtt_capabilities = wifi_get_rtt_capabilities;fn->wifi_rtt_get_responder_info = wifi_rtt_get_responder_info;fn->wifi_enable_responder = wifi_enable_responder;fn->wifi_disable_responder = wifi_disable_responder;fn->wifi_set_nodfs_flag = wifi_set_nodfs_flag;fn->wifi_start_logging = wifi_start_logging;fn->wifi_set_epno_list = wifi_set_epno_list;fn->wifi_reset_epno_list = wifi_reset_epno_list;fn->wifi_set_country_code = wifi_set_country_code;fn->wifi_enable_tdls = wifi_enable_tdls;fn->wifi_disable_tdls = wifi_disable_tdls;fn->wifi_get_tdls_status = wifi_get_tdls_status;fn->wifi_get_tdls_capabilities = wifi_get_tdls_capabilities;fn->wifi_get_firmware_memory_dump = wifi_get_firmware_memory_dump;fn->wifi_set_log_handler = wifi_set_log_handler;fn->wifi_reset_log_handler = wifi_reset_log_handler;fn->wifi_set_alert_handler = wifi_set_alert_handler;fn->wifi_reset_alert_handler = wifi_reset_alert_handler;fn->wifi_get_firmware_version = wifi_get_firmware_version;fn->wifi_get_ring_buffers_status = wifi_get_ring_buffers_status;fn->wifi_get_logger_supported_feature_set = wifi_get_logger_supported_feature_set;fn->wifi_get_ring_data = wifi_get_ring_data;fn->wifi_get_driver_version = wifi_get_driver_version;fn->wifi_set_passpoint_list = wifi_set_passpoint_list;fn->wifi_reset_passpoint_list = wifi_reset_passpoint_list;fn->wifi_set_lci = wifi_set_lci;fn->wifi_set_lcr = wifi_set_lcr;fn->wifi_start_sending_offloaded_packet =wifi_start_sending_offloaded_packet;fn->wifi_stop_sending_offloaded_packet = wifi_stop_sending_offloaded_packet;fn->wifi_start_rssi_monitoring = wifi_start_rssi_monitoring;fn->wifi_stop_rssi_monitoring = wifi_stop_rssi_monitoring;fn->wifi_nan_enable_request = nan_enable_request;fn->wifi_nan_disable_request = nan_disable_request;fn->wifi_nan_publish_request = nan_publish_request;fn->wifi_nan_publish_cancel_request = nan_publish_cancel_request;fn->wifi_nan_subscribe_request = nan_subscribe_request;fn->wifi_nan_subscribe_cancel_request = nan_subscribe_cancel_request;fn->wifi_nan_transmit_followup_request = nan_transmit_followup_request;fn->wifi_nan_stats_request = nan_stats_request;fn->wifi_nan_config_request = nan_config_request;fn->wifi_nan_tca_request = nan_tca_request;fn->wifi_nan_beacon_sdf_payload_request = nan_beacon_sdf_payload_request;fn->wifi_nan_register_handler = nan_register_handler;fn->wifi_nan_get_version = nan_get_version;fn->wifi_set_packet_filter = wifi_set_packet_filter;fn->wifi_get_packet_filter_capabilities = wifi_get_packet_filter_capabilities;fn->wifi_read_packet_filter = wifi_read_packet_filter;fn->wifi_nan_get_capabilities = nan_get_capabilities;fn->wifi_nan_data_interface_create = nan_data_interface_create;fn->wifi_nan_data_interface_delete = nan_data_interface_delete;fn->wifi_nan_data_request_initiator = nan_data_request_initiator;fn->wifi_nan_data_indication_response = nan_data_indication_response;fn->wifi_nan_data_end = nan_data_end;fn->wifi_configure_nd_offload = wifi_configure_nd_offload;fn->wifi_get_driver_memory_dump = wifi_get_driver_memory_dump;fn->wifi_get_wake_reason_stats = wifi_get_wake_reason_stats;fn->wifi_start_pkt_fate_monitoring = wifi_start_pkt_fate_monitoring;fn->wifi_get_tx_pkt_fates = wifi_get_tx_pkt_fates;fn->wifi_get_rx_pkt_fates = wifi_get_rx_pkt_fates;fn->wifi_get_roaming_capabilities = wifi_get_roaming_capabilities;fn->wifi_configure_roaming = wifi_configure_roaming;fn->wifi_enable_firmware_roaming = wifi_enable_firmware_roaming;fn->wifi_select_tx_power_scenario = wifi_select_tx_power_scenario;fn->wifi_reset_tx_power_scenario = wifi_reset_tx_power_scenario;fn->wifi_set_radio_mode_change_handler = wifi_set_radio_mode_change_handler;fn->wifi_virtual_interface_create = wifi_virtual_interface_create;fn->wifi_virtual_interface_delete = wifi_virtual_interface_delete;fn->wifi_set_latency_mode = wifi_set_latency_mode;fn->wifi_set_thermal_mitigation_mode = wifi_set_thermal_mitigation_mode;fn->wifi_set_dtim_config = wifi_set_dtim_config;return WIFI_SUCCESS;

成功获取到vendor hal接口后,对所有的legacy hal执行初始化

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

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

相关文章

R语言使用nonrandom包进行倾向评分匹配

倾向评分匹配&#xff08;Propensity Score Matching&#xff0c;简称PSM&#xff09;是一种统计学方法&#xff0c;用于处理观察研究&#xff08;Observational Study&#xff09;的数据&#xff0c;在SCI文章中应用非常广泛。在观察研究中&#xff0c;由于种种原因&#xff0…

LeetCode Hot 100 找到字符串中所有字母异位词

给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 示例 1: 输入: s "cbaebabacd", p "abc" 输出: [0,6] 解释: 起始索引等于 0 的子串是 "cba", 它是 "a…

关于庐山派多视频层(layer)和bind_layer的应用

嘉立创分了适配层和OSD&#xff08;我称它为图片层&#xff09;顾名思义&#xff0c;一个是能显示视频流到LCD屏幕&#xff0c;一个是只能显示照片&#xff0c;也就是你可以对不同层进行操作而不影响其他层&#xff0c;解决的场景就是用于你画了一个正方形在照片上&#xff0c;…

多传感器标定简介

目录 标定内容及方法 雷达内参标定 IMU内参标定 编码器内参标定 相机内参标定 雷达和相机外参标定 多雷达外参标定 手眼标定 融合中标定 总结 连续时间 标定内容及方法 雷达内参标定 1) 目的 由于安装原因&#xff0c;线束之间的夹角和设计不一致&#xff0c;会导致…

day46/60

浙大疏锦行 DAY 46 通道注意力(SE注意力) 知识点回顾&#xff1a; 不同CNN层的特征图&#xff1a;不同通道的特征图什么是注意力&#xff1a;注意力家族&#xff0c;类似于动物园&#xff0c;都是不同的模块&#xff0c;好不好试了才知道。通道注意力&#xff1a;模型的定义和插…

提升创作效率:轻松调用固定素材与模板

日常工作和生活中&#xff0c;我们经常需要复制粘贴不同类型的数据&#xff0c;如文本、图片、文件等。使用剪切板管理工具可以快速访问之前复制的内容&#xff0c;而无需反复切换应用进行复制操作。 这款绿色便携版应用&#xff0c;无需安装&#xff0c;双击即开&#xff0c;…

【C++】组合模式

目录 一、模式核心概念与结构二、C 实现示例&#xff1a;文件系统三、组合模式的关键特性四、应用场景五、组合模式与其他设计模式的关系六、C 标准库中的组合模式应用七、优缺点分析八、实战案例&#xff1a;图形编辑器九、实现注意事项如果这篇文章对你有所帮助&#xff0c;渴…

C++包管理工具:conan2持续集成 (CI) 教程

1.持续集成 (CI) ​ 这是一个高级主题&#xff0c;需要具备 Conan 的基础知识。请先阅读并练习用户教程。本节面向设计和实施涉及 Conan 包的生产 CI 管道的 DevOps 和构建工程师。如果不是这种情况&#xff0c;您可以跳过本节。 持续集成 (CI) 对不同用户和组织有不同的含义…

免费SSL证书一键申请与自动续期

免费SSL证书申请与自动续期教程 本文介绍如何通过乐此加密&#xff08;www.letsencrypt.top) 实现免费SSL证书一键配置和自动续期 一、准备工作 服务器要求 Linux 系统&#xff08;推荐 Ubuntu/CentOS&#xff09;已安装 curl 和 crontab拥有 sudo 权限的用户 域名验证 确保域…

【NLP】自然语言项目设计

目录 项目简介 要求 需要考虑的问题 硬件需求和环境配置 n卡驱动配置 以cuda11.8 版本为例 下载对应的cudnn(version11) 安装GPU版本的torch 安装gpu版本的TensorFlow 检查cuda安装情况 项目简介 训练一个模型&#xff0c;实现歌词仿写生成 任务类型&#xff1a;文本…

设计模式:观察者模式 (Observer) 案例详解

目录 一、引言&#xff1a;为什么需要观察者模式&#xff1f; 二、观察者模式的核心原理 1. 角色划分 2. 类图关系 三、经典案例解析 案例1&#xff1a;天气监测系统 案例2&#xff1a;股票价格监控系统 案例3&#xff1a;MVC架构中的模型-视图分离 案例4&#xff1a;J…

CTF-Misc:开启全方位解题之旅

目录 一、CTF-Misc 入门指引二、基础技能储备2.1 文件格式识别2.2 基础工具使用 三、信息搜集技巧3.1 搜索引擎技巧3.2 网络信息挖掘 四、编码转换奥秘4.1 常见编码类型4.2 编码转换工具 五、隐写分析秘籍5.1 图片隐写5.1.1 LSB 隐写5.1.2 颜色通道与 Exif 信息5.1.3 图片修复与…

Adobe创意套件深度挖掘:效率倍增与灵感迸发的新玩法

最近在深入体验奥地利Blueskyy艺术学院的Adobe正版教育订阅&#xff0c;并研究全家桶时有不少新发现&#xff0c;忍不住想和大家分享一下。 先简单说下这个订阅的感受&#xff1a; Firefly 积分。 这应该是我用过Firefly积分最多的版本&#xff0c;1500点/周。对于我们这些创意…

左神算法之有序二维矩阵中的目标值查找

有序二维矩阵中的目标值查找 目录 有序二维矩阵中的目标值查找1. 题目描述2. 问题解释3. 解决思路方法一&#xff1a;逐行二分查找&#xff08;适合行数较少的情况&#xff09;方法二&#xff1a;利用行列有序特性&#xff08;最优解&#xff09; 4. 代码实现5. 总结 1. 题目描…

深入理解AVL树及其旋转操作

AVL树的概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单枝树&#xff0c;查找元素相当于在顺序表中搜索元素&#xff0c;效率低下。因此&#xff0c;两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种方法…

URL带有中文会引入哪些问题

处理含中文字符的 URL 1 为什么会出现“乱码”或崩溃&#xff1f; URL 标准&#xff08;RFC 3986&#xff09;规定&#xff1a;除少数保留字符外&#xff0c;URL 只能包含 ASCII。中文属于 Unicode&#xff0c;因此必须先转换。如果直接把 https://example.com/路径/ 这样的字…

结构体字段能否单独加 mut

你问的这个问题在 Rust 里很常见&#xff1a; 一、结构体字段能否单独加 mut 1. 结构体字段能否单独加 mut&#xff1f; 不能。Rust 中&#xff0c;mut 是用来修饰变量绑定的&#xff0c;可变性是绑定的属性&#xff0c;而不是结构体字段本身的属性。 你不能写&#xff1a; …

scGPT-spatial 复现

文章目录 ✅ 总体流程总览&#xff08;从 H5AD 到模型训练&#xff09;&#x1f527; 步骤 1&#xff1a;读取 H5AD 文件并做基础预处理&#x1f9f1; 步骤 2&#xff1a;构造训练样本输入&#xff08;token、value&#xff09;&#x1f4e6; 步骤 3&#xff1a;使用 DataColla…

运放电压跟随器为什么要加电阻

运放电压跟随器为什么要加电阻 我们常见运放的电压跟随器如下&#xff1a; 有时候会看见电路中加两个电阻&#xff1a; 作用就是保护运放&#xff0c;起限流电阻的作用。 当输入电压高的时候&#xff0c;运放内部存在钳位二极管&#xff0c;此电阻就能限流。 并不是所有运放…

MinerU 2.0部署

简介 MinerU 2.0使用sglang加速&#xff0c;与之前差别较大&#xff0c;建议按照官方的Docker镜像的方式启动。 Docker镜像 Dockerfile 这是官方的Dockerfile # Use the official sglang image FROM lmsysorg/sglang:v0.4.7-cu124# install mineru latest RUN python3 -m …