【笔记】Android Settings 应用设置菜单的界面代码介绍

news2024/7/4 12:00:09

简介

Settings应用中,提供多类设置菜单入口,每个菜单内又有各模块功能的实现。

那么各个模块基于Settings 基础的界面Fragment去实现UI,层层按不同业务进行封装继承实现子类:

  • DashboardFragment
  • SettingsPreferenceFragment

功能设置页中的菜单又是通过Controller去实现业务并进行UI动态更新控制。

代码

基于 Android U 平台的实现进行介绍。

公用基类

DashboardFragment

packages/apps/Settings/src/com/android/settings/dashboard/DashboardFragment.java

package com.android.settings.dashboard;

/**
 * Base fragment for dashboard style UI containing a list of static and dynamic setting items.
 */
public abstract class DashboardFragment extends SettingsPreferenceFragment
        implements CategoryListener, Indexable, PreferenceGroup.OnExpandButtonClickListener,
        BasePreferenceController.UiBlockListener {
    public static final String CATEGORY = "category";
    private static final String TAG = "DashboardFragment";
    private static final long TIMEOUT_MILLIS = 50L;

    /**
     * Get a list of {@link AbstractPreferenceController} for this fragment.
     */
    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
        return null;
    }
 

模块案例

继承关系:(自上而下,底层是父类,除了MobileNetwork,其他都是抽象类,直到Fragment)

  • MobileNetworkSettings --- 移动网络设置
  • AbstractMobileNetworkSettings
  • RestrictedDashboardFragment
  • DashboardFragment
  • SettingsPreferenceFragment
  • InstrumentedPreferenceFragment -- /com/android/settings/core/
  • ObservablePreferenceFragment -- /frameworks/base/packages/SettingsLib
  • PreferenceFragmentCompat -- classes.jar/androidx.preference
  • Fragment

AbstractMobileNetworkSettings 是一个抽象类,用于提供移动网络设置相关的基本功能和行为。

RestrictedDashboardFragment 是在DashboardFragment的基础上添加了一些限制或额外的安全措施,以限制用户对某些设置或操作的访问或更改。可能会对一些敏感的设置进行限制,例如网络设置、安全设置等。也可能会对某些功能进行权限控制,只允许特定用户访问。

DashboardFragment 是基础的仪表盘样式 UI Fragment,用于显示静态和动态的设置项列表。通常不会加入对设置的限制,而是专注于提供用户友好的设置界面和功能。可能包含一些基本的设置项处理和展示逻辑,以便用户能够轻松地进行配置和操作。

MobileNetworkSettings 移动网络设置(界面逻辑)

packages/apps/Settings/src/com/android/settings/network/telephony/MobileNetworkSettings.java

界面布局在mobile_network_settings.xml

MobileNetworkSettings API
APIFunction
onPreferenceTreeClick定义子功能pref点击事件(override)
createPreferenceControllers创建子功能pref的控制器(override)
onAttach生命周期,use 各类 Controller(override)
onSubscriptionDetailChanged响应注册状态变化进行的操作
package com.android.settings.network.telephony;

@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class MobileNetworkSettings extends AbstractMobileNetworkSettings implements
        MobileNetworkRepository.MobileNetworkCallback {

    private static final String LOG_TAG = "NetworkSettings";


    //处理子菜单点击事件
    /**
     * Invoked on each preference click in this hierarchy, overrides
     * PreferenceActivity's implementation.  Used to make sure we track the
     * preference click events.
     */
    @Override
    public boolean onPreferenceTreeClick(Preference preference) {
        if (super.onPreferenceTreeClick(preference)) {
            return true;
        }
        final String key = preference.getKey();

        if (TextUtils.equals(key, BUTTON_CDMA_SYSTEM_SELECT_KEY)
                || TextUtils.equals(key, BUTTON_CDMA_SUBSCRIPTION_KEY)) {
            if (mTelephonyManager.getEmergencyCallbackMode()) {
                startActivityForResult(
                        new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null),
                        REQUEST_CODE_EXIT_ECM);
                mClickedPrefKey = key;
            }
            return true;
        }
        //【客制化】可通过else if 定制其他keyt的点击行为

        return false;
    }

    //创建各子菜单/功能选项的Controller
    @Override
    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
        if (!SubscriptionUtil.isSimHardwareVisible(context)) {
            finish();
            return Arrays.asList();
        }
        if (getArguments() == null) {
            Intent intent = getIntent();
            if (intent != null) {
                mSubId = intent.getIntExtra(Settings.EXTRA_SUB_ID,
                        MobileNetworkUtils.getSearchableSubscriptionId(context));
                Log.d(LOG_TAG, "display subId from intent: " + mSubId);
            } else {
                Log.d(LOG_TAG, "intent is null, can not get subId " + mSubId + " from intent.");
            }
        } else {
            mSubId = getArguments().getInt(Settings.EXTRA_SUB_ID,
                    MobileNetworkUtils.getSearchableSubscriptionId(context));
            Log.d(LOG_TAG, "display subId from getArguments(): " + mSubId);
        }
        mMobileNetworkRepository = MobileNetworkRepository.getInstance(context);
        mExecutor.execute(() -> {
            mSubscriptionInfoEntity = mMobileNetworkRepository.getSubInfoById(
                    String.valueOf(mSubId));
            mMobileNetworkInfoEntity =
                    mMobileNetworkRepository.queryMobileNetworkInfoBySubId(
                            String.valueOf(mSubId));
        });

        return Arrays.asList(
                new DataUsageSummaryPreferenceController(getActivity(), getSettingsLifecycle(),
                        this, mSubId),
                new RoamingPreferenceController(context, KEY_ROAMING_PREF, getSettingsLifecycle(),
                        this, mSubId),
                new CallsDefaultSubscriptionController(context, KEY_CALLS_PREF,
                        getSettingsLifecycle(), this),
                new SmsDefaultSubscriptionController(context, KEY_SMS_PREF, getSettingsLifecycle(),
                        this),
                new MobileDataPreferenceController(context, KEY_MOBILE_DATA_PREF,
                        getSettingsLifecycle(), this, mSubId),
                new ConvertToEsimPreferenceController(context, KEY_CONVERT_TO_ESIM_PREF,
                        getSettingsLifecycle(), this, mSubId));
    }


    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
            Log.d(LOG_TAG, "Invalid subId, get the default subscription to show.");
            SubscriptionInfo info = SubscriptionUtil.getSubscriptionOrDefault(context, mSubId);
            if (info == null) {
                Log.d(LOG_TAG, "Invalid subId request " + mSubId);
                return;
            }
            mSubId = info.getSubscriptionId();
            Log.d(LOG_TAG, "Show NetworkSettings fragment for subId" + mSubId);
        }

        Intent intent = getIntent();
        if (intent != null) {
            int updateSubscriptionIndex = intent.getIntExtra(Settings.EXTRA_SUB_ID,
                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);

            if (updateSubscriptionIndex != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
                int oldSubId = mSubId;
                mSubId = updateSubscriptionIndex;
                // If the subscription has changed or the new intent does not contain the opt in
                // action,
                // remove the old discovery dialog. If the activity is being recreated, we will see
                // onCreate -> onNewIntent, so the dialog will first be recreated for the old
                // subscription
                // and then removed.
                if (updateSubscriptionIndex != oldSubId
                        || !MobileNetworkActivity.doesIntentContainOptInAction(intent)) {
                    removeContactDiscoveryDialog(oldSubId);
                }

                // evaluate showing the new discovery dialog if this intent contains an action to
                // show the
                // opt-in.
                if (MobileNetworkActivity.doesIntentContainOptInAction(intent)) {
                    showContactDiscoveryDialog();
                }
            }

        }

        final DataUsageSummaryPreferenceController dataUsageSummaryPreferenceController =
                use(DataUsageSummaryPreferenceController.class);
        if (dataUsageSummaryPreferenceController != null) {
            dataUsageSummaryPreferenceController.init(mSubId);
        }
        use(MobileNetworkSwitchController.class).init(mSubId);
        use(CarrierSettingsVersionPreferenceController.class).init(mSubId);
        use(BillingCyclePreferenceController.class).init(mSubId);
        use(MmsMessagePreferenceController.class).init(mSubId);
        use(AutoDataSwitchPreferenceController.class).init(mSubId);
        use(DisabledSubscriptionController.class).init(mSubId);
        use(DeleteSimProfilePreferenceController.class).init(mSubId, this,
                REQUEST_CODE_DELETE_SUBSCRIPTION);
        use(DisableSimFooterPreferenceController.class).init(mSubId);
        use(NrDisabledInDsdsFooterPreferenceController.class).init(mSubId);

        final MobileDataPreferenceController mobileDataPreferenceController =
                use(MobileDataPreferenceController.class);
        if (mobileDataPreferenceController != null) {
            mobileDataPreferenceController.init(getFragmentManager(), mSubId,
                    mSubscriptionInfoEntity, mMobileNetworkInfoEntity);
            mobileDataPreferenceController.setWifiPickerTrackerHelper(
                    new WifiPickerTrackerHelper(getSettingsLifecycle(), context,
                            null /* WifiPickerTrackerCallback */));
        }

        final RoamingPreferenceController roamingPreferenceController =
                use(RoamingPreferenceController.class);
        if (roamingPreferenceController != null) {
            roamingPreferenceController.init(getFragmentManager(), mSubId,
                    mMobileNetworkInfoEntity);
        }
        use(ApnPreferenceController.class).init(mSubId);
        use(CarrierPreferenceController.class).init(mSubId);
        use(DataUsagePreferenceController.class).init(mSubId);
        use(PreferredNetworkModePreferenceController.class).init(mSubId);
        use(EnabledNetworkModePreferenceController.class).init(mSubId);
        use(DataServiceSetupPreferenceController.class).init(mSubId);
        use(Enable2gPreferenceController.class).init(mSubId);
        use(CarrierWifiTogglePreferenceController.class).init(getLifecycle(), mSubId);

        final WifiCallingPreferenceController wifiCallingPreferenceController =
                use(WifiCallingPreferenceController.class).init(mSubId);

        final OpenNetworkSelectPagePreferenceController openNetworkSelectPagePreferenceController =
                use(OpenNetworkSelectPagePreferenceController.class).init(mSubId);
        final AutoSelectPreferenceController autoSelectPreferenceController =
                use(AutoSelectPreferenceController.class)
                        .init(getLifecycle(), mSubId)
                        .addListener(openNetworkSelectPagePreferenceController);
        use(NetworkPreferenceCategoryController.class).init(mSubId)
                .setChildren(Arrays.asList(autoSelectPreferenceController));
        mCdmaSystemSelectPreferenceController = use(CdmaSystemSelectPreferenceController.class);
        mCdmaSystemSelectPreferenceController.init(getPreferenceManager(), mSubId);
        mCdmaSubscriptionPreferenceController = use(CdmaSubscriptionPreferenceController.class);
        mCdmaSubscriptionPreferenceController.init(getPreferenceManager(), mSubId);

        final VideoCallingPreferenceController videoCallingPreferenceController =
                use(VideoCallingPreferenceController.class).init(mSubId);
        use(CallingPreferenceCategoryController.class).setChildren(
                Arrays.asList(wifiCallingPreferenceController, videoCallingPreferenceController));
        use(Enhanced4gLtePreferenceController.class).init(mSubId)
                .addListener(videoCallingPreferenceController);
        use(Enhanced4gCallingPreferenceController.class).init(mSubId)
                .addListener(videoCallingPreferenceController);
        use(Enhanced4gAdvancedCallingPreferenceController.class).init(mSubId)
                .addListener(videoCallingPreferenceController);
        use(ContactDiscoveryPreferenceController.class).init(getParentFragmentManager(), mSubId);
        use(NrAdvancedCallingPreferenceController.class).init(mSubId);
        use(TransferEsimPreferenceController.class).init(mSubId, mSubscriptionInfoEntity);
        final ConvertToEsimPreferenceController convertToEsimPreferenceController =
                use(ConvertToEsimPreferenceController.class);
        if (convertToEsimPreferenceController != null) {
            convertToEsimPreferenceController.init(mSubId, mSubscriptionInfoEntity);
        }

        List<AbstractSubscriptionPreferenceController> subscriptionPreferenceControllers =
                useAll(AbstractSubscriptionPreferenceController.class);
        for (AbstractSubscriptionPreferenceController controller :
                subscriptionPreferenceControllers) {
            controller.init(mSubId);
        }
    }

    private void onSubscriptionDetailChanged() {
        if (mSubscriptionInfoEntity != null) {
            /**
             * Update the title when SIM stats got changed
             */
            final Consumer<Activity> renameTitle = activity -> {
                if (activity != null && !activity.isFinishing()) {
                    if (activity instanceof SettingsActivity) {
                        ((SettingsActivity) activity).setTitle(mSubscriptionInfoEntity.uniqueName);
                    }
                }
            };

            ThreadUtils.postOnMainThread(() -> {
                renameTitle.accept(getActivity());
                redrawPreferenceControllers();
            });
        }
    }

    //界面布局在mobile_network_settings.xml
    @Override
    protected int getPreferenceScreenResId() {
        return R.xml.mobile_network_settings;
    }

}

mobile_network_settings 布局

packages/apps/Settings/res/xml/mobile_network_settings.xml

  • MobileNetworkSwitchController SIM卡移动网络总开关逻辑
  • use_sim_switch 界面的资源key
  • mobile_network_use_sim_on 开关名称title

以下仅展示部分功能选项/菜单:

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res-auto"
    android:key="mobile_network_pref_screen">

    <com.android.settings.widget.SettingsMainSwitchPreference
        android:key="use_sim_switch"
        android:title="@string/mobile_network_use_sim_on"
        settings:controller="com.android.settings.network.telephony.MobileNetworkSwitchController"/>

    <PreferenceCategory
        android:key="enabled_state_container"
        android:title="@string/summary_placeholder"
        settings:controller="com.android.settings.network.telephony.DisabledSubscriptionController"
        android:layout="@layout/preference_category_no_label">

        <!--智能数据切换开关-->
        <SwitchPreference
            android:key="auto_data_switch"
            android:title="@string/auto_data_switch_title"
            android:summary="@string/auto_data_switch_summary"
            settings:controller="com.android.settings.network.telephony.AutoDataSwitchPreferenceController"/>
        <!--数据漫游开关-->
        <com.android.settingslib.RestrictedSwitchPreference
            android:key="button_roaming_key"
            android:title="@string/roaming"
            android:persistent="false"
            android:summaryOn="@string/roaming_enable"
            android:summaryOff="@string/roaming_disable"
            settings:userRestriction="no_data_roaming"
            settings:controller="com.android.settings.network.telephony.RoamingPreferenceController"/>

        <!--数据流量菜单入口-->
        <Preference
            android:key="data_usage_summary"
            android:title="@string/mobile_data_usage_title"
            settings:controller="com.android.settings.network.telephony.DataUsagePreferenceController"/>

        <!--网络模式列表选择-->
        <ListPreference
            android:key="enabled_networks_key"
            android:title="@string/preferred_network_mode_title"
            android:summary="@string/preferred_network_mode_summary"
            android:entries="@array/enabled_networks_choices"
            android:entryValues="@array/enabled_networks_values"
            android:dialogTitle="@string/preferred_network_mode_dialogtitle"
            settings:controller="com.android.settings.network.telephony.EnabledNetworkModePreferenceController"/>

        <!--Network目录-->
        <PreferenceCategory
            android:key="network_operators_category_key"
            android:title="@string/network_operator_category"
            settings:controller="com.android.settings.network.telephony.NetworkPreferenceCategoryController">
            <!--自动选网开关-->
            <SwitchPreference
                android:key="auto_select_key"
                android:title="@string/select_automatically"
                settings:controller="com.android.settings.network.telephony.gsm.AutoSelectPreferenceController"/>
            <!--如果上述自动选网关闭,那么此手动选网菜单,可跳转到网络列表页-->
            <Preference
                android:key="choose_network_key"
                android:title="@string/choose_network_title"
                settings:controller="com.android.settings.network.telephony.gsm.OpenNetworkSelectPagePreferenceController"/>
        </PreferenceCategory>

        <!--APN设置入口-->
        <!--We want separate APN setting from reset of settings because we want user to change it with caution-->
        <com.android.settingslib.RestrictedPreference
            android:key="telephony_apn_key"
            android:persistent="false"
            android:title="@string/mobile_network_apn_title"
            settings:allowDividerAbove="true"
            settings:keywords="@string/keywords_access_point_names"
            settings:controller="com.android.settings.network.telephony.ApnPreferenceController"/>

    </PreferenceCategory>

</PreferenceScreen>

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

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

相关文章

vue 中使用element-ui实现锚点定位表单

效果图&#xff1a; 代码&#xff1a; html代码&#xff1a; <div class"content-left"><el-tabs :tab-position"left" tab-click"goAnchor"><el-tab-pane v-for"(item,index) in anchorNameList"v-anchor-scroll:ke…

HTTP2.0如何优化HTTP1.1

目录 HTTP1.1什么缺点 如何解决头部压缩 静态字典 动态字典 如何解决队头阻塞 Stream 排序 服务器主动推送资源 HTTP1.1什么缺点 队头阻塞问题 同一连接只能在完成一个 HTTP 事务&#xff08;请求和响应&#xff09;后&#xff0c;才能处理下一个事务&#xff1b;(…

力扣每日一题 7/2 数学、数论、数组/双指针

博客主页&#xff1a;誓则盟约系列专栏&#xff1a;IT竞赛 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 3115.质数的最大距离【中等】 题目&#xff1a; 给你一个整数数组 nums。…

视频字幕提取在线工具有哪些?总结5个字幕提取工具

平时在沉浸式追剧的时候&#xff0c;我们常常都会被影视剧中的各种金句爆梗而逗得开怀大笑~而真正要用到时候却总是一片头脑空白。其实要记住它们最好的办法便是将其提取留档下来&#xff0c;每次有需要的时候打开就能一下子回顾到~ 今天就来带大家盘一盘视频字幕提取的软件好…

[C++]——同步异步日志系统(1)

同步异步日志系统 一、项⽬介绍二、开发环境三、核心技术四、环境搭建五、日志系统介绍5.1 为什么需要日志系统5.2 日志系统技术实现5.2.1 同步写日志5.2.2 异步写日志 日志系统&#xff1a; 日志&#xff1a;程序在运行过程中&#xff0c;用来记录程序运行状态信息。 作用&…

图片转pdf,图片转pdf在线转换,在线图片转pdf,图片转pdf格式怎么弄

在数字化时代&#xff0c;图片和PDF文件成为了我们日常生活中不可或缺的元素。有时候&#xff0c;我们需要将图片转换成PDF格式以便更好地分享、存储或打印。那么&#xff0c;图片格式怎么转换成PDF呢&#xff1f;本文将为您详细介绍简单实用的转换方法。 方法一、使用pdf转换软…

Python容器 之 字典--字典的常用操作方法

1.增加和修改 字典[键] 值 键 存在, 修改 键 不存在, 添加 # 定义非空字典, 姓名, 年龄, 身高, 性别 my_dict {"name": "小明", "age": 18, "height": 1.78, "isMen": True} print(my_dict) # {name: 小明, age: 18, h…

【知识】DGL中graph默认的稀疏矩阵格式和coo格式不对的坑

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 先给结论 源码解读 代码验证 网上没找到相关的讨论&#xff0c;因此只能从源码上一步步查。 先给结论 对于自己使用dgl.graph接口创建的图&am…

【HTML入门】列表与表格

文章目录 前言一、列表与表格是什么&#xff1f;列表表格 二、使用标签列表标签表格标签 三、组合情况列表的组合表格的组合 四、示例代码总结 好的&#xff0c;以下是一个关于HTML列表与表格的文章示例&#xff1a; 前言 随着网页开发的普及&#xff0c;HTML成为了构建网页的…

LVS FILTER UNUSED OPTION

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 过滤一些版图与spice网表对不上的器件。 一般后端遇不到这个问题,因为通常是需要写到网表中的decap没有写出来造成的,如下图。

来不及啦!怎样找回删除的照片,2个解决方案【安卓通用】

一不小心手滑&#xff0c;手机里那些珍贵的照片竟然全都被删了&#xff01;现在心急如焚&#xff0c;怎样找回删除的照片呢&#xff1f;别担心&#xff0c;小编我这就为你带来2个快速有效的解决方案&#xff0c;让你在分分钟找回那些失去的照片&#xff01;快来学习一下吧&…

Java语法系列 小白入门参考资料 方法

方法的概念及使用 方法概念 方法出现的原因 在编程中&#xff0c;某段功能的代码可能频繁使用到&#xff0c;如果在每个位置都重新实现一遍&#xff0c;会&#xff1a; 1. 使程序变得繁琐 2. 开发效率低下&#xff0c;做了大量重复性的工作 3. 不利于维护&#xff0c;需要…

自动驾驶水泥搅拌车在梁场的应用(下)

自动驾驶水泥搅拌车在梁场的应用&#xff08;下&#xff09; 北京渡众机器人科技有限公司的自动驾驶水泥搅拌车在梁场&#xff08;也称为预制梁场&#xff09;的应用可以带来多方面的优势和效益&#xff1a; 1. 自动化搅拌和运输 在梁场中&#xff0c;通常需要大量的混凝土搅…

探索大型语言模型自动评估 LLM 输出长句准确性的方法

LLM现在能够自动评估较长文本中的事实真实性 源码地址&#xff1a;https://github.com/google-deepmind/long-form-factuality 论文地址&#xff1a;https://arxiv.org/pdf/2403.18802.pdf 这篇论文是关于谷歌DeepMind的&#xff0c;提出了新的数据集、评估方法和衡量标准&am…

零基础学习MySQL---MySQL入门

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、什么是数据库 问&#xff1a;存储数据用文件就可以了&#xff0c;为什么还要弄个数据库呢&#xff1f; 这就不得不提…

收集了很久的全网好用的磁力搜索站列表分享

之前找资源的时候&#xff0c;收集了一波国内外大部分主流的磁力链接搜索站点。每一个站可能都有对应的优缺点&#xff0c;多试试&#xff0c;就能知道自己要哪个了。 全网好用的磁力链接 大部分的时候&#xff0c;我们用国内的就可以了&#xff0c;速度块&#xff0c;而且不…

Free Pascal语言基础学习:定义变量、数据类型、循环语句、case语句、条件判断、with语句、运算符

Pascal是一种结构化编程语言&#xff0c;而Free Pascal作为其现代编译器&#xff0c;不仅支持跨多种操作系统和处理器架构&#xff0c;还提供了高效的内存使用和函数重载等先进功能。Free Pascal继承了Pascal语言的核心特性&#xff0c;同时进行了扩展和优化&#xff0c;使其成…

操作系统真象还原:编写硬盘驱动程序

第13章-编写硬盘驱动程序 这是一个网站有所有小节的代码实现&#xff0c;同时也包含了Bochs等文件 13.1 硬盘及分区表 13.1.1 创建从盘及获取安装的磁盘数 要实现文件系统&#xff0c;必须先有个磁盘介质&#xff0c;虽然咱们己经有个虚拟磁盘 hd60M.img&#xff0c;但它只…

理解MySQL存储引擎:掌握数据存储与管理

在工作或学习过程中&#xff0c;作为一名数据库管理员或开发者&#xff0c;我们常常需处理大量数据&#xff0c;同时确保数据的可靠性与高效性。MySQL作为最受欢迎的开源数据库之一&#xff0c;其强大的性能和灵活性广为人知。而在MySQL背后的存储引擎则起到了至关重要的作用。…

深度学习项目GPU开发环境安装

注安装环境&#xff1a;ubuntu22.04, cuda 11.7, cudnn8.9 1.安装nvidia驱动 看可安装的Nvidia驱动版本&#xff1a;执行 ubuntu-drivers devices 安装合适版本的Nvidia驱动&#xff1a; sudo apt-get install nvidia-driver-515 注意&#xff1a;合适的版本需要尝试&#x…