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

Android 蓝牙服务启动

蓝牙是Android设备中非常常见的一个feature,设备厂家可以用BT来做RC、连接音箱、设备本身做Sink等常见功能。如果一些设备不需要BT功能,Android也可以通过配置来disable此模块,方便厂家为自己的设备做客制化。APP操作设备的蓝牙功能,一般是通过标准API-BluetoothAdapter实现,这里我们先不关心具体API的实现flow,先来了解Bluetooth framework的启动过程,这样可以为后面介绍一些BT 常见flow,做铺垫。

我们都知道Android的主要系统服务都是在system_server中启动的,BT也不例外:

            // Skip Bluetooth if we have an emulator kernel// TODO: Use a more reliable check to see if this product should// support Bluetooth - see bug 988521if (isEmulator) {Slog.i(TAG, "No Bluetooth Service (emulator)");} else if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {Slog.i(TAG, "No Bluetooth Service (factory test)");} else if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {Slog.i(TAG, "No Bluetooth Service (Bluetooth Hardware Not Present)");} else {traceBeginAndSlog("StartBluetoothService");mSystemServiceManager.startService(BluetoothService.class);traceEnd();}

在SystemServer中,BT framework启动的起点以服务启动的形式表现,这在Android中非常常见;我们可以通过配置feature xml文件来表明系统当前是否支持蓝牙,在一些没有蓝牙模组的设备上,就可以去掉hardware feature bluetooth的声明,这样设备启动时就不会启动BT framework相关的模块了:

@frameworks/base/services/core/java/com/android/server/BluetoothManagerService.javaclass BluetoothService extends SystemService {private BluetoothManagerService mBluetoothManagerService;public BluetoothService(Context context) {super(context);//创建BluetoothManagerService的实例mBluetoothManagerService = new BluetoothManagerService(context);}........@Overridepublic void onBootPhase(int phase) {if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {//将BluetoothManagerService实例发布到系统中,这样就可以Context根据BT的service名去获取它的Binder代理操作API了publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE,mBluetoothManagerService);} else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {//此时系统应该启动到一个比较晚的阶段了,可以使用AMS去Bind需要的Service了mBluetoothManagerService.handleOnBootPhase();}}........
}

BluetoothManagerService构造过程中,跟开机flow中自动enable bt相关的主要是mEnableExternal这个flag:

    BluetoothManagerService(Context context) {mHandler = new BluetoothHandler(IoThread.get().getLooper());//创建内部处理msg的handlermContext = context;mPermissionReviewRequired = context.getResources().getBoolean(com.android.internal.R.bool.config_permissionReviewRequired);mCrashes = 0;mBluetooth = null;mBluetoothBinder = null;mBluetoothGatt = null;mBinding = false;mUnbinding = false;mEnable = false;mState = BluetoothAdapter.STATE_OFF;mQuietEnableExternal = false;//false表示此次enable需要触发auto connect device和保存状态,BluetoothAdapter::enableNoAutoConnect()可以改变此状态mEnableExternal = false;mAddress = null;mName = null;mErrorRecoveryRetryCounter = 0;mContentResolver = context.getContentResolver();// Observe BLE scan only mode settings change.registerForBleScanModeChange();mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();IntentFilter filter = new IntentFilter();filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);//监听App通过接口修改BT local name的广播(对端设备搜索你显示的字符串)filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);//监听bt地址改变的广播filter.addAction(Intent.ACTION_SETTING_RESTORED);//监听当前设置需要restore回上一次设置的广播,此时需要重新保存name和addr为上一次的信息filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);mContext.registerReceiver(mReceiver, filter);loadStoredNameAndAddress();//从数据库中加载本机Bt的local name和addressif (isBluetoothPersistedStateOn()) {//查看上一次关机时,BT是否为enable状态;如果是,这次开机也需要enable BTif (DBG) {Slog.d(TAG, "Startup: Bluetooth persisted state is ON.");}mEnableExternal = true;//表明开机过程中需要enable BT}String airplaneModeRadios =Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS);if (airplaneModeRadios == null || airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH)) {mContentResolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,mAirplaneModeObserver);//监听Airplane mode改变,相应改变BT状态;这在TV plateform上基本没用}int systemUiUid = -1;try {// Check if device is configured with no home screen, which implies no SystemUI.boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen);if (!noHome) {systemUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,UserHandle.USER_SYSTEM);}Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid));} catch (PackageManager.NameNotFoundException e) {// Some platforms, such as wearables do not have a system ui.Slog.w(TAG, "Unable to resolve SystemUI's UID.", e);}mSystemUiUid = systemUiUid;}

它为true,表明上一次关机前BT就是enable的,这次开机也需要enable;其他的主要是一些广播、数据库字段的初始化动作。

回到前面BluetoothService启动的过程,当系统启动到合适的阶段,就回调SystemService相应的函数,当phase为PHASE_ACTIVITY_MANAGER_READY时,调用到BMS::handleOnBootPhase(),进行BT framework的启动:

    /*** Send enable message and set adapter name and address. Called when the boot phase becomes* PHASE_SYSTEM_SERVICES_READY.*/public void handleOnBootPhase() {if (DBG) {Slog.d(TAG, "Bluetooth boot completed");}UserManagerInternal userManagerInternal =LocalServices.getService(UserManagerInternal.class);userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);final boolean isBluetoothDisallowed = isBluetoothDisallowed();if (isBluetoothDisallowed) {return;}if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) {if (DBG) {Slog.d(TAG, "Auto-enabling Bluetooth.");}sendEnableMsg(mQuietEnableExternal/*默认false,表示此次enable需要自动连接device/保存enable状态*/,BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT,mContext.getPackageName());} else if (!isNameAndAddressSet()) {if (DBG) {Slog.d(TAG, "Getting adapter name and address");}Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);mHandler.sendMessage(getMsg);}}

handleOnBootPhase()的内容比较单一,根据一些flag判断是否需要enable BT;而enable蓝牙这里是通过触发send msg实现:

    private void sendEnableMsg(boolean quietMode, int reason, String packageName) {//发送MESSAGE_ENABLE msgmHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0));addActiveLog(reason, packageName, true);mLastEnabledTime = SystemClock.elapsedRealtime();}case MESSAGE_ENABLE:if (DBG) {Slog.d(TAG, "MESSAGE_ENABLE(" + msg.arg1 + "): mBluetooth = " + mBluetooth);}mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);mEnable = true;// Use service interface to get the exact statetry {mBluetoothLock.readLock().lock();if (mBluetooth != null) { //开机第一次enable,值为nullint state = mBluetooth.getState();if (state == BluetoothAdapter.STATE_BLE_ON) {//大部分设备一般都不是ble only modeSlog.w(TAG, "BT Enable in BLE_ON State, going to ON");mBluetooth.onLeServiceUp();persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);break;}}} catch (RemoteException e) {Slog.e(TAG, "", e);} finally {mBluetoothLock.readLock().unlock();}mQuietEnable = (msg.arg1 == 1);//此时为falseif (mBluetooth == null) {//mBluetooth是后面绑定Bt apk中AdapterService时拿到的Binder代理对象;用以把操作bypass到BT核心框架中handleEnable(mQuietEnable);} else {//如果mBluetooth不是null,说明之前已经启动过了;此时是Restart flow,以MESSAGE_RESTART_BLUETOOTH_SERVICE触发//// We need to wait until transitioned to STATE_OFF and// the previous Bluetooth process has exited. The// waiting period has three components:// (a) Wait until the local state is STATE_OFF. This//     is accomplished by "waitForOnOff(false, true)".// (b) Wait until the STATE_OFF state is updated to//     all components.// (c) Wait until the Bluetooth process exits, and//     ActivityManager detects it.// The waiting for (b) and (c) is accomplished by// delaying the MESSAGE_RESTART_BLUETOOTH_SERVICE// message. On slower devices, that delay needs to be// on the order of (2 * SERVICE_RESTART_TIME_MS).//waitForOnOff(false, true);Message restartMsg =mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);mHandler.sendMessageDelayed(restartMsg, 2 * SERVICE_RESTART_TIME_MS);}break;

handleEnable()去Bind AdapterService拿到它的Binder句柄,bindServiceAsUser如果服务端没有启动,就会去启动服务端:

 private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
......private class BluetoothServiceConnection implements ServiceConnection {public void onServiceConnected(ComponentName componentName, IBinder service) {String name = componentName.getClassName();if (DBG) {Slog.d(TAG, "BluetoothServiceConnection: " + name);}Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);if (name.equals("com.android.bluetooth.btservice.AdapterService")) {msg.arg1 = SERVICE_IBLUETOOTH;} else if (name.equals("com.android.bluetooth.gatt.GattService")) {msg.arg1 = SERVICE_IBLUETOOTHGATT;} else {Slog.e(TAG, "Unknown service connected: " + name);return;}msg.obj = service;mHandler.sendMessage(msg);}public void onServiceDisconnected(ComponentName componentName) {// Called if we unexpectedly disconnect.String name = componentName.getClassName();if (DBG) {Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + name);}Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);if (name.equals("com.android.bluetooth.btservice.AdapterService")) {msg.arg1 = SERVICE_IBLUETOOTH;} else if (name.equals("com.android.bluetooth.gatt.GattService")) {msg.arg1 = SERVICE_IBLUETOOTHGATT;} else {Slog.e(TAG, "Unknown service disconnected: " + name);return;}mHandler.sendMessage(msg);}}
......boolean doBind(Intent intent, ServiceConnection conn, int flags, UserHandle user) {ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);intent.setComponent(comp);if (comp == null || !mContext.bindServiceAsUser(intent, conn, flags, user)) {Slog.e(TAG, "Fail to bind to: " + intent);return false;}return true;}

IBluetooth.class对于的Service声明在Bluetooth apk的manifest xml中,AdapterService是真正的服务端:

@packages/apps/Bluetooth/AndroidManifest.xml<application android:name=".btservice.AdapterApp"android:icon="@mipmap/bt_share"android:persistent="false"android:label="@string/app_name"android:supportsRtl="true"android:usesCleartextTraffic="false"android:directBootAware="trueandroid:defaultToDeviceProtectedStorage="true"<serviceandroid:process="@string/process"android:name = ".btservice.AdapterService"><intent-filter><action android:name="android.bluetooth.IBluetooth" /></intent-filter></service>
@packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java
public class AdapterService extends Service {private static final String TAG = "BluetoothAdapterService";public void onCreate() {super.onCreate();debugLog("onCreate()");    

去Bind AdapterService服务,并拿到它的Binder句柄,用以后面操作它的接口。AdapterService的初始化下篇再讲,现在先回到BMS binder成功之后发送MESSAGE_BLUETOOTH_SERVICE_CONNECTED(arg1:SERVICE_IBLUETOOTH)的处理:

case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: {if (DBG) {Slog.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);}IBinder service = (IBinder) msg.obj; //保存Service的onBinder()句柄try {mBluetoothLock.writeLock().lock();if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {mBluetoothGatt =IBluetoothGatt.Stub.asInterface(Binder.allowBlocking(service));continueFromBleOnState();break;} // else must be SERVICE_IBLUETOOTH//Remove timeoutmHandler.removeMessages(MESSAGE_TIMEOUT_BIND);mBinding = false;mBluetoothBinder = service;mBluetooth = IBluetooth.Stub.asInterface(Binder.allowBlocking(service));//再转成IBluetooth对象if (!isNameAndAddressSet()) {Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);mHandler.sendMessage(getMsg);if (mGetNameAddressOnly) {return;}}//Register callback objecttry {mBluetooth.registerCallback(mBluetoothCallback);//mBluetoothCallback用来监听BT的enable状态,如state_on/state_off的状态变化} catch (RemoteException re) {Slog.e(TAG, "Unable to register BluetoothCallback", re);}//Inform BluetoothAdapter instances that service is upsendBluetoothServiceUpCallback();//Do enable requesttry {if (!mQuietEnable) {if (!mBluetooth.enable()) {//Service bind成功之后,call AdpaterService的enable()接口;做一些初始化动作,并向stack发送enable命令启动蓝牙Slog.e(TAG, "IBluetooth.enable() returned false");}} else {if (!mBluetooth.enableNoAutoConnect()) {Slog.e(TAG, "IBluetooth.enableNoAutoConnect() returned false");}}} catch (RemoteException e) {Slog.e(TAG, "Unable to call enable()", e);}} finally {mBluetoothLock.writeLock().unlock();}if (!mEnable) {waitForOnOff(true, false);handleDisable();waitForOnOff(false, false);}break;}

 

主要操作:

拿到bind服务的onBinder()句柄,并转成IBluetooth类型
通过IBluetooth类型的obj,调用enable()接口,将flow转到AdapterService中,做一些初始化、并向stack下enable bt的cmd
也要注意到,如果BMS中和AdapterService的连接断掉了,最终会通过MESSAGE_RESTART_BLUETOOTH_SERVICE msg去重新bind它;这在场景可能是由于模组异常导致Bluetooth apk crash(会产生tombstone),与AdapterService的bind连接断掉;这里触发Restart,会导致重走一遍介绍的enable BT流程。

到此,enable BT的flow就从BluetoothManagerService转到AdapterService中了;实际上,通过BluetoothAdapter下来的大部分API调用最终都是call到AdapterService,再通过它下cmd给stack。

AdapterService是蓝牙框架中一个非常重要的服务,它的启动和初始化部分,我们下篇再单独介绍。

PS:

可以看到BT启动过程中有两个常见到的flag:

mEnable:它主要是用来标记系统运行时,蓝牙状态的变化,它有些时候时跟mEnableExternal值一致的。但如果蓝牙的状态是因为某些原因,如stack崩溃,导致蓝牙需要reset、重新启动时,需要靠这个flag来标记这种case的enable/disable状态
mEnableExternal:它主要是记录通过用户手动操作导致的BT使能状态,如通过蓝牙功能按钮来enable/disable BT

@packages/apps/Bluetooth/AndroidManifest.xml
    <application android:name=".btservice.AdapterApp"
         android:icon="@mipmap/bt_share"
         android:persistent="false"
         android:label="@string/app_name"
         android:supportsRtl="true"
         android:usesCleartextTraffic="false"
         android:directBootAware="true
         android:defaultToDeviceProtectedStorage="true"
        <service
            android:process="@string/process"
            android:name = ".btservice.AdapterService">
            <intent-filter>
                <action android:name="android.bluetooth.IBluetooth" />
            </intent-filter>
        </service>

相关文章:

Android 蓝牙服务启动

蓝牙是Android设备中非常常见的一个feature&#xff0c;设备厂家可以用BT来做RC、连接音箱、设备本身做Sink等常见功能。如果一些设备不需要BT功能&#xff0c;Android也可以通过配置来disable此模块&#xff0c;方便厂家为自己的设备做客制化。APP操作设备的蓝牙功能&#xff…...

【安全系列--处理挖矿】

现象&#xff1a;我们云上waf提示有台服务器存在挖矿行为 解决思路&#xff1a; 1、查看服务器的进程情况 top发现服务的CPU使用率非常高 2、使用性能分析工具perf查看占用的cpu进程 sudo apt install linux-tools-common发现一些kernel进程存在异常 3、使用find查一下这…...

SpringBoot集成Thymeleaf模板引擎,为什么使用(详细介绍)

学习本技术第一件事&#xff1a;你为什么要使用&#xff0c;解决什么问题的&#xff1f; 1.为什么使用&#xff08;使用背景&#xff09;&#xff1f; 首先应用场景是单体项目&#xff0c;如果是前后端分离就不用关注这个了&#xff0c;因为单体项目你前后端都是写在一个项目…...

Docker突然宣布:涨价80%

从11月15日起&#xff0c;Docker的付费订阅中Pro和Team的价格都将大幅上调&#xff1a;Pro从原来的5美元每月激增到9美元每月&#xff0c;直接涨了80%&#xff1b;而Team也从之前的9美元每月来到15美元每月&#xff0c;涨价66.7%。只有Business保持此前的24美元每月不变。 同时…...

工厂方法模式和抽象工厂模式

工厂方法模式 一个工厂只能创建一种产品 工厂方法模式的结构 工厂方法模式包含以下4个角色 Product&#xff08;抽象产品&#xff09; ConcreteProduct&#xff08;具体产品&#xff09; Factory&#xff08;抽象工厂&#xff09; ConcreteFactory&#xff08;具体工厂…...

【星海出品】go语言环境兼install

官网 https://golang.google.cn/dl/ go的安装包下载地址 https://go.dev/dl/ set GO111MODULEon //是否以Go modules的模式运行项目 auto,on,off set GOARCHamd64 //目标可执行程序操作系统构架 包括 386&#xff0c;amd64&#xff0c;arm set GOBIN //项目的第三方可执行文件目…...

Spring 源码解读:自定义实现BeanPostProcessor的扩展点

引言 在Spring的生命周期管理中&#xff0c;BeanPostProcessor是一个非常重要的扩展点。它允许开发者在Bean初始化的前后插入自定义的逻辑&#xff0c;从而实现更灵活的Bean管理。BeanPostProcessor是Spring框架中用于对Bean实例进行修改的机制之一。通过实现该接口&#xff0…...

Spring Boot-分布式系统问题

Spring Boot 在分布式系统中的常见问题及解决方案 随着互联网的发展&#xff0c;系统规模和复杂度越来越大&#xff0c;分布式系统成为应对高并发、大数据量场景的重要架构选择。Spring Boot 作为一种轻量级的开发框架&#xff0c;广泛应用于构建微服务和分布式系统中。然而&a…...

面试题总结(三) -- 内存管理篇

面试题总结(三) – 内存管理篇 文章目录 面试题总结(三) -- 内存管理篇<1> C 中堆内存和栈内存的区别是什么&#xff1f;<2> 如何在 C 中手动管理内存&#xff08;new/delete 操作符&#xff09;&#xff1f;<3> C 中内存泄漏的原因和避免方法<4> 谈谈…...

Qt 定时器-定时备份

定时备份 在Qt 中&#xff0c;可以使用QTimer类来实现定时备份功能。以下是一个示例代码&#xff0c;每隔一段时间自动执行备份操作&#xff1a; #include <QTimer>QTimer timer; int backupInterval 24 * 60 * 60 * 1000;//备份间隔为24小时connect(&timer, &…...

天融信把桌面explorer.exe删了,导致开机之后无windows桌面,只能看到鼠标解决方法

win10开机进入桌面&#xff0c;发现桌面无了&#xff0c;但是可以ctrlaltdelete调出任务管理器 用管理员权限打开cmd&#xff0c;输入&#xff1a; sfc /scanfilec:\windowslexplorer.exe 在运行C:\windows\Explorer.exe&#xff1b;可以进入桌面&#xff0c;但是隔离几秒钟…...

视频分割操作教程

1、打开剪映 2、点击开始创作上面的“”&#xff0c;选择视频&#xff0c;点击添加按钮&#xff0c;导入一个视频素材到剪映 3、滑动视频&#xff0c;让视频竖线到合适位置 4、点击视频&#xff0c;出现白色边框 5、点击工具栏“分割”&#xff0c;然后点击需要删除的视频部分 …...

唯品会大数据面试题及参考答案(3万字长文)

synchronized 和 volatile 的区别 synchronized是 Java 中的关键字,用于实现同步机制,确保在同一时刻只有一个线程可以访问被它修饰的代码块或方法。volatile也是 Java 中的关键字,主要用于保证变量的可见性。 功能方面: synchronized可以保证原子性、可见性和有序性。它通…...

使用容器技术快速入门MinIO

使用容器技术快速入门MinIO 使用容器技术&#xff08;docker或者podman&#xff09;快速部署一个单节点单磁盘 MinIO 服务器&#xff0c;用于对MinIO对象存储及其兼容 S3 的 API 层进行早期的开发和评估。 1. 准备工作 机器已经安装了 Podman 或者 Docker 。 对用于持久卷的…...

ros2教程(一):使用python和C++发布摄像头原始图像和压缩图像

1. 使用python发布图像 在ROS 2中&#xff0c;可以通过使用rclpy库来发布压缩图像和原始图像。发布原始图像可以使用sensor_msgs.msg.Image消息类型&#xff0c;压缩图像则使用sensor_msgs.msg.CompressedImage消息类型。 #!/usr/bin/env python3# function: usbcam publish r…...

【自动化测试】UI自动化的分类、如何选择合适的自动化测试工具以及其中appium的设计理念、引擎和引擎如何工作

引言 UI自动化测试主要针对软件的用户界面进行测试&#xff0c;以确保用户界面元素的交互和功能符合预期 文章目录 引言一、UI自动化的分类1.1 基于代码的自动化测试1.2 基于录制/回放的自动化测试1.3 基于框架的自动化测试1.4 按测试对象分类1.5 按测试层次分类1.6 按测试执行…...

深入理解Python中的“_,”:一个实用的语法特性

在Python编程中&#xff0c;你可能经常会看到一个特殊的标识符“_”。这个符号在Python中有多种用途&#xff0c;其具体含义依上下文而定。本文将探讨其中一种常见用法——作为一个临时性的占位符——并解释它在实际编程中的实用性和应用场景。 1. “_”作为占位符 在Python中…...

Mac清理其他文件:释放存储空间的高效指南

每个Mac用户都可能遇到存储空间不足的问题&#xff0c;尤其是当“其他”文件积累到一定体积时。在Mac上&#xff0c;“其他”文件通常包括各种系统文件、缓存、文档以及不被归类为应用程序、照片、电影或音乐的其他类型的文件。这些文件往往不易被注意&#xff0c;但逐渐占用了…...

html+css+js网页设计 旅游 龙门石窟4个页面

htmlcssjs网页设计 旅游 龙门石窟4个页面 网页作品代码简单&#xff0c;可使用任意HTML辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&#…...

CISSP一站通关

依托轻速云维护了一个专注于CISSP备考通关的在线学习平台&#xff0c;提供知识串讲视频&#xff0c;配合大量针对性的习题和重难点习题解析&#xff0c;帮助备考学习者高效学习和巩固知识点。已经帮助100考友顺利通过考试。 知识串讲视频是我主讲的5天直播课程的录屏&#xff0…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...