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
英文地址,請注明出處:http://en.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 …