Android 11 系统开发增加低电量弹窗提示 手机 平板 车载 TV 投影 通用

1、PowerUI是系统中控制电量提示的模块,低电量提醒、低电量关机提醒、高温关机提醒、省电模式都在其中实现

SystemUIService 中启动PowerUI

public class SystemUIService extends Service {@Overridepublic void onCreate() {super.onCreate();((SystemUIApplication) getApplication()).startServicesIfNeeded();// For debugging RescuePartyif (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) {throw new RuntimeException();}if (Build.IS_DEBUGGABLE) {// b/71353150 - looking for leaked binder proxiesBinderInternal.nSetBinderProxyCountEnabled(true);BinderInternal.nSetBinderProxyCountWatermarks(1000,900);BinderInternal.setBinderProxyCountCallback(new BinderInternal.BinderProxyLimitListener() {@Overridepublic void onLimitReached(int uid) {Slog.w(SystemUIApplication.TAG,"uid " + uid + " sent too many Binder proxies to uid "+ Process.myUid());}}, Dependency.get(Dependency.MAIN_HANDLER));}}SystemUIService 启动时,启动SystemUIApplicationstartServicesIfNeeded() 来启动SystemUI的各种服务在SystemUIApplication中 启动PowerUIconfig.xml 中config_systemUIServiceComponents<string-array name="config_systemUIServiceComponents" translatable="false"><item>com.android.systemui.Dependency$DependencyCreator</item><item>com.android.systemui.util.NotificationChannels</item><item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item><item>com.android.systemui.keyguard.KeyguardViewMediator</item><item>com.android.systemui.recents.Recents</item><item>com.android.systemui.volume.VolumeUI</item><item>com.android.systemui.stackdivider.Divider</item><item>com.android.systemui.SystemBars</item><item>com.android.systemui.usb.StorageNotification</item><item>com.android.systemui.power.PowerUI</item><item>com.android.systemui.media.RingtonePlayer</item><item>com.android.systemui.keyboard.KeyboardUI</item><item>com.android.systemui.pip.PipUI</item><item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item><item>@string/config_systemUIVendorServiceComponent</item><item>com.android.systemui.util.leak.GarbageMonitor$Service</item><item>com.android.systemui.LatencyTester</item><item>com.android.systemui.globalactions.GlobalActionsComponent</item><item>com.android.systemui.ScreenDecorations</item><item>com.android.systemui.biometrics.BiometricDialogImpl</item><item>com.android.systemui.SliceBroadcastRelayHandler</item><item>com.android.systemui.SizeCompatModeActivityController</item><item>com.android.systemui.statusbar.notification.InstantAppNotifier</item><item>com.android.systemui.theme.ThemeOverlayController</item></string-array>/*** Makes sure that all the SystemUI services are running. If they are already running, this is a* no-op. This is needed to conditinally start all the services, as we only need to have it in* the main process.* <p>This method must only be called from the main thread.</p>*/public void startServicesIfNeeded() {String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);}SystemUIFactorygetSystemUIServiceComponents(Resources resources)public String[] getSystemUIServiceComponents(Resources resources) {return resources.getStringArray(R.array.config_systemUIServiceComponents);}private void startServicesIfNeeded(String[] services) {if (mServicesStarted) {return;}mServices = new SystemUI[services.length];if (!mBootCompleted) {// check to see if maybe it was already completed long before we began// see ActivityManagerService.finishBooting()if ("1".equals(SystemProperties.get("sys.boot_completed"))) {mBootCompleted = true;if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");}}Log.v(TAG, "Starting SystemUI services for user " +Process.myUserHandle().getIdentifier() + ".");TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",Trace.TRACE_TAG_APP);log.traceBegin("StartServices");final int N = services.length;for (int i = 0; i < N; i++) {String clsName = services[i];if (DEBUG) Log.d(TAG, "loading: " + clsName);log.traceBegin("StartServices" + clsName);long ti = System.currentTimeMillis();Class cls;try {cls = Class.forName(clsName);Object o = cls.newInstance();if (o instanceof SystemUI.Injector) {o = ((SystemUI.Injector) o).apply(this);}mServices[i] = (SystemUI) o;} catch(ClassNotFoundException ex){throw new RuntimeException(ex);} catch (IllegalAccessException ex) {throw new RuntimeException(ex);} catch (InstantiationException ex) {throw new RuntimeException(ex);}mServices[i].mContext = this;mServices[i].mComponents = mComponents;if (DEBUG) Log.d(TAG, "running: " + mServices[i]);mServices[i].start();log.traceEnd();// Warn if initialization of component takes too longti = System.currentTimeMillis() - ti;if (ti > 1000) {Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms");}if (mBootCompleted) {mServices[i].onBootCompleted();}}Dependency.get(InitController.class).executePostInitTasks();log.traceEnd();final Handler mainHandler = new Handler(Looper.getMainLooper());Dependency.get(PluginManager.class).addPluginListener(new PluginListener<OverlayPlugin>() {private ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();@Overridepublic void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {mainHandler.post(new Runnable() {@Overridepublic void run() {StatusBar statusBar = getComponent(StatusBar.class);if (statusBar != null) {plugin.setup(statusBar.getStatusBarWindow(),statusBar.getNavigationBarView(), new Callback(plugin));}}});}@Overridepublic void onPluginDisconnected(OverlayPlugin plugin) {mainHandler.post(new Runnable() {@Overridepublic void run() {mOverlays.remove(plugin);Dependency.get(StatusBarWindowController.class).setForcePluginOpen(mOverlays.size() != 0);}});}class Callback implements OverlayPlugin.Callback {private final OverlayPlugin mPlugin;Callback(OverlayPlugin plugin) {mPlugin = plugin;}@Overridepublic void onHoldStatusBarOpenChange() {if (mPlugin.holdStatusBarOpen()) {mOverlays.add(mPlugin);} else {mOverlays.remove(mPlugin);}mainHandler.post(new Runnable() {@Overridepublic void run() {Dependency.get(StatusBarWindowController.class).setStateListener(b -> mOverlays.forEach(o -> o.setCollapseDesired(b)));Dependency.get(StatusBarWindowController.class).setForcePluginOpen(mOverlays.size() != 0);}});}}}, OverlayPlugin.class, true /* Allow multiple plugins */);mServicesStarted = true;}

2、PowerUI 电量的分析

public void start() {
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
mWarnings = Dependency.get(WarningsUI.class);
mEnhancedEstimates = Dependency.get(EnhancedEstimates.class);
mLastConfiguration.setTo(mContext.getResources().getConfiguration());ContentObserver obs = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
updateBatteryWarningLevels();
}
};
final ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
false, obs, UserHandle.USER_ALL);
updateBatteryWarningLevels();
mReceiver.init();// Check to see if we need to let the user know that the phone previously shut down due
// to the temperature being too high.
showWarnOnThermalShutdown();// Register an observer to configure mEnableSkinTemperatureWarning and perform the
// registration of skin thermal event listener upon Settings change.
resolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.SHOW_TEMPERATURE_WARNING),
false /*notifyForDescendants*/,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
doSkinThermalEventListenerRegistration();
}
});
// Register an observer to configure mEnableUsbTemperatureAlarm and perform the
// registration of usb thermal event listener upon Settings change.
resolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.SHOW_USB_TEMPERATURE_ALARM),
false /*notifyForDescendants*/,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
doUsbThermalEventListenerRegistration();
}
});
initThermalEventListeners();
mCommandQueue.addCallback(this);
}@VisibleForTesting
final class Receiver extends BroadcastReceiver {private boolean mHasReceivedBattery = false;public void init() {
// Register for Intent broadcasts for...
IntentFilter filter = new IntentFilter();
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_USER_SWITCHED);
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler);
// Force get initial values. Relying on Sticky behavior until API for getting info.
if (!mHasReceivedBattery) {
// Get initial state
Intent intent = mContext.registerReceiver(
null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
);
if (intent != null) {
onReceive(mContext, intent);
}
}
}@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
ThreadUtils.postOnBackgroundThread(() -> {
if (mPowerManager.isPowerSaveMode()) {
mWarnings.dismissLowBatteryWarning();
}
});
} else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
mHasReceivedBattery = true;
final int oldBatteryLevel = mBatteryLevel;
mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
final int oldBatteryStatus = mBatteryStatus;
mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
final int oldPlugType = mPlugType;
mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
final int oldInvalidCharger = mInvalidCharger;
mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
mLastBatteryStateSnapshot = mCurrentBatteryStateSnapshot;final boolean plugged = mPlugType != 0;
final boolean oldPlugged = oldPlugType != 0;int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
int bucket = findBatteryLevelBucket(mBatteryLevel);if (DEBUG) {
Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel
+ " .. " + mLowBatteryReminderLevels[0]
+ " .. " + mLowBatteryReminderLevels[1]);
Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);
Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);
Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);
Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);
Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);
}mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
Slog.d(TAG, "showing invalid charger warning");
mWarnings.showInvalidChargerWarning();
return;
} else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
mWarnings.dismissInvalidChargerWarning();
} else if (mWarnings.isInvalidChargerWarningShowing()) {
// if invalid charger is showing, don't show low battery
if (DEBUG) {
Slog.d(TAG, "Bad Charger");
}
return;
}
// Show the correct version of low battery warning if needed
if (mLastShowWarningTask != null) {
mLastShowWarningTask.cancel(true);
if (DEBUG) {
Slog.d(TAG, "cancelled task");
}
}
//
mLastShowWarningTask = ThreadUtils.postOnBackgroundThread(() -> {
maybeShowBatteryWarningV2(
plugged, bucket);
});
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
mScreenOffTime = SystemClock.elapsedRealtime();
} else if (Intent.ACTION_SCREEN_ON.equals(action)) {
mScreenOffTime = -1;
} else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
mWarnings.userSwitched();
} else {
Slog.w(TAG, "unknown intent: " + intent);
}
}
}PowerUIstart()方法中启动广播监听电量变化
mLastShowWarningTask = ThreadUtils.postOnBackgroundThread(() -> {
maybeShowBatteryWarningV2(
plugged, bucket);
});
来判断是否开启低电量警告
protected void maybeShowBatteryWarningV2(boolean plugged, int bucket) {
final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
final boolean isPowerSaverMode = mPowerManager.isPowerSaveMode();
// Stick current battery state into an immutable container to determine if we should show
// a warning.
if (DEBUG) {
Slog.d(TAG, "evaluating which notification to show");
}
if (hybridEnabled) {
if (DEBUG) {
Slog.d(TAG, "using hybrid");
}
Estimate estimate = refreshEstimateIfNeeded();
mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
mLowBatteryReminderLevels[0], estimate.getEstimateMillis(),
estimate.getAverageDischargeTime(),
mEnhancedEstimates.getSevereWarningThreshold(),
mEnhancedEstimates.getLowWarningThreshold(), estimate.isBasedOnUsage(),
mEnhancedEstimates.getLowWarningEnabled());
} else {
if (DEBUG) {
Slog.d(TAG, "using standard");
}
mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
mLowBatteryReminderLevels[0]);
}
mWarnings.updateSnapshot(mCurrentBatteryStateSnapshot);
if (mCurrentBatteryStateSnapshot.isHybrid()) {
maybeShowHybridWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
} else {
//低电量警告
maybeShowBatteryWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
}
protected void maybeShowBatteryWarning(BatteryStateSnapshot currentSnapshot,BatteryStateSnapshot lastSnapshot) {final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()|| lastSnapshot.getPlugged();if (shouldShowLowBatteryWarning(currentSnapshot, lastSnapshot)) {mWarnings.showLowBatteryWarning(playSound);//低电量警告} else if (shouldDismissLowBatteryWarning(currentSnapshot, lastSnapshot)) {mWarnings.dismissLowBatteryWarning();//去掉低电量警告} else {mWarnings.updateLowBatteryWarning();}}PowerNotificationWarnings.java的低电量提醒方法@Overridepublic void showLowBatteryWarning(boolean playSound) {Slog.i(TAG,"show low battery warning: level=" + mBatteryLevel+ " [" + mBucket + "] playSound=" + playSound);mPlaySound = playSound;mWarning = true;updateNotification();}
}

3、增加低电量的弹窗 PowerNotificationWarnings.java的showLowBatteryWarning()方法

import android.app.AlertDialog;import android.view.WindowManager;import android.content.DialogInterface;private AlertDialog mbatteryLowDialog = null;// 自定义电池温度Dialog弹窗private void batterylowDialog(String lowbattery) {mbatteryLowDialog = new AlertDialog(mContext);AlertDialog.Builder builder = new AlertDialog.Builder(mContext);builder.setTitle(mContext.getResources().getString(com.android.internal.R.string.lowbatteryWarning));builder.setCancelable(false);builder.setMessage(lowbattery);builder.setIconAttribute(android.R.attr.alertDialogIcon);builder.setPositiveButton(com.android.internal.R.string.batteryLow111,new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int id) {dialog.cancel();mbatteryLowDialog = null;}});mbatteryLowDialog = builder.create();mbatteryLowDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);if (mbatteryLowDialog != null&& !mbatteryLowDialog.isShowing()) {mbatteryLowDialog.show();}}@Overridepublic void showLowBatteryWarning(boolean playSound) {Slog.i(TAG,"show low battery warning: level=" + mBatteryLevel+ " [" + mBucket + "] playSound=" + playSound);mPlaySound = playSound;mWarning = true;updateNotification();+ batterylowDialog("低电量")}

4、增加低电量提醒的xml资源

主要修改

frameworks/base/core/res/res/values/string.xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">... ...<!-- Shutdown if the battery temperature exceeds (this value * 0.1) Celsius. --><string name="lowbatteryWarning">低电量提醒</integer><!-- add code begin--><string name="batteryLow111">111</integer><!-- add code end-->... ...
</resources>

5、在symbols 文件中添加对应java-symbol方便Framework代码引用code

在symbols文件中为全部string,int值注册,方便Framework层代码的引用。
frameworks/base/core/res/res/values/symbols.xml<?xml version="1.0" encoding="utf-8"?>
<resources><!-- Private symbols that we need to reference from framework code.  Seeframeworks/base/core/res/MakeJavaSymbols.sed for how to easily generatethis.Can be referenced in java code as: com.android.internal.R.<type>.<name>and in layout xml as: "@*android:<type>/<name>"--><!-- add code begin--><java-symbol type="string" name="lowbatteryWarning" /><java-symbol type="string" name="batteryLow111" />

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

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

相关文章

【SpringMVC】RESTFul风格设计和实战 第三期

文章目录 概述一、 RESTFul风格简介二、 RESTFul风格特点三、 RESTFul风格设计规范3.1 HTTP协议请求方式要求3.2 URL路径风格要求 实战一、需求分析二、RESTFul风格接口设计三、后台接口实现总结模糊查询 有范围 返回多数据用户 添加 与 更新 请求参数接收数据显示用户详情/删除…

进腾讯工作一个月,我想辞职了......

前几天&#xff0c;我在网上看到一个微博。 一个应届的校招生&#xff0c;目前入职腾讯&#xff0c;工作了一个月。这一个月给他的感受是大量的写测试用例&#xff0c;自己写测试用例的能力熟练了不少&#xff0c;测试技能倒是没有多大的提高&#xff0c;真正需要技术的工作却…

软考71-上午题-【面向对象技术2-UML】-UML中的图2

一、用例图 上午题&#xff0c;考的少&#xff1b;下午题&#xff0c;考的多。 1-1、用例图的定义 用例图展现了一组用例、参与者以及它们之间的关系。 用例图用于对系统的静态用例图进行建模。 可以用下列两种方式来使用用例图&#xff1a; 1、对系统的语境建模&#xff1b…

【java 分页工具类】分页工具PageBean(149)

分页工具类&#xff1a; package com.itheima3;import java.util.ArrayList; import java.util.List;public class PageBean<T> {private int pageSize; // 每页记录数private int pageNo; // 当前页private int totalPages; // 总页数private int totalRecords; // 总记…

ChatGPT 升级出现「我们未能验证您的支付方式/we are unable to authenticate」怎么办?

ChatGPT 升级出现「我们未能验证您的支付方式/we are unable to authenticate」怎么办&#xff1f; 在订阅 ChatGPT Plus 时&#xff0c;有时候会出现以下报错 &#xff1a; We are unable to authenticate your payment method. 我们未能验证您的支付方式。 出现 unable to a…

低密度奇偶校验码LDPC(十)——LDPC码的密度进化

一、密度进化的概念 二、规则LDPC码的密度进化算法(SPA算法) 算法变量表 VN更新的密度进化 CN更新的密度进化 算法总结 程序仿真 参考文献 [1] 白宝明 孙韶辉 王加庆. 5G 移动通信中的信道编码[M]. 北京: 电子工业出版社, 2018. [2] William E. Ryan, Shu Lin. Channel Co…

优牛企讯司法涉诉维度全解析,了解这些小白也可以变专家!

在商业的海洋中&#xff0c;信息的掌握就如同舵手对风向的了解。每一条信息都可能成为引领航船前行的关键&#xff0c;尤其是在法律风险的管理上&#xff0c;准确而及时的信息更是企业稳健航行的保障。 优牛企讯&#xff0c;一款专业的企业司法涉诉监控查询工具&#xff0c;它…

SpringMVC03、HelloSpring

3、HelloSpring 3.1、配置版 新建一个Moudle &#xff0c; springmvc-02-hello &#xff0c; 添加web的支持&#xff01; 确定导入了SpringMVC 的依赖&#xff01; 配置web.xml &#xff0c; 注册DispatcherServlet <?xml version"1.0" encoding"UTF-8…

微调模型(Machine Learning 研习之十二)

现在正处于百模乱战的时期&#xff0c;对于模型微调&#xff0c;想必您是有所了解了&#xff0c;毕竟国外的大语言模型一开源&#xff0c;国内便纷纷基于该模型进行微调&#xff0c;从而开始宣称领先于某某、超越了谁。可到头来&#xff0c;却让人发现他们套壳了国外大语言模型…

【第58例】IPD体系进阶:战略的五个核心要素

目录 1. 内容简介 2. 计划与行动 3. 计划层面 CSDN学院 作者简介 1. 内容简介 今天继续来更新 IPD 体系进阶专栏:战

Linux Ubuntu 部署SVN

最近需要在ubuntu server上部署一个svn&#xff0c;记录 不需要特定版本 如果不需要特定版本&#xff0c;这样安装就是最简单的 sudo apt update然后开始安装 sudo apt install subversion等到安装完成之后执行查看版本命令&#xff0c;如果正常输出那就没问题了 svnadmin …

C++默认参数详解

目录 缺省参数/默认参数概念&#xff1a; 缺省参数的应用场景&#xff1a; 函数定义定义分离时的默认参数&#xff1a; 简单的编译链接&#xff1a; 缺省参数/默认参数概念&#xff1a; 缺省参数/默认参数&#xff1a;通俗点讲就是形参给的那个某个值 func(int a 0); //例…

uniapp 使用定时器和取消定时器

1. uniapp 使用定时器和清除定时器 1.1. 定义一个timer data(){return{timer: null} }1.2. 设置定时器 //选择适合需求的定时器 this.timer setTimeout( () > {// 这里添加您的逻辑 }, 1000) this.timer setInterval( () > {// 同上 }, 1000)1.3. 清除定时器 这…

差分算法:空调-USACO 2021 December Contest Bronze

4262. 空调 - AcWing题库 Farmer John 的 N 头奶牛对他们牛棚的室温非常挑剔。 有些奶牛喜欢温度低一些&#xff0c;而有些奶牛则喜欢温度高一些。 Farmer John 的牛棚包含一排 N 个牛栏&#xff0c;编号为 1…N&#xff0c;每个牛栏里有一头牛。 第 i 头奶牛希望她的牛栏中的温…

汽车协议学习

ⅠOBD 1.OBD接口 OBD有16个引脚&#xff0c;每个引脚的电压不同&#xff08;可以对应不同的协议&#xff09; 车端&#xff1a; 16- 9 (短一点点的) 8-1 &#xff08;长一点的&#xff09; 2.基于OBDⅡ的通信协议 CAN &#xff08;ISO-15765&am…

windows将bat脚本设置为开机自启动

windows将bat脚本设置为开机自启动 使用场景&#xff1a;需要在windows开机启动时直接运行一个bat脚本&#xff0c;通过这个脚本调用写好的python之类的管理工具。 步骤&#xff1a; 1、按下Win R快捷键打开“运行”对话框 2、输入shell:startup&#xff0c;然后按下回车键 3、…

Python-Flask 快学

flask是Python的一个轻型Web框架. 使用pycharm自动创建项目,也可以手动创建,以下是目录的结构: ├── app.py├── static└── templates 一、创建一个简单应用 from flask import Flaskapp Flask(__name__)app.route(/) def hello_world():return "hello world &…

20240309-2-校招前端面试常见问题-NodeJS

校招前端面试常见问题【6】——NodeJS NodeJS Q&#xff1a;NodeJS 的 IO 模型特点是什么&#xff1f;与多线程同步 IO 有什么不同&#xff1f; NodeJS 的 IO 模型&#xff08;更准确的说是 js 的执行环境&#xff0c;也就是 v8&#xff09;的特点是“单线程异步非阻塞”。 …

Linux学习-共用体和枚举,位运算

目录 共用体 ​编辑枚举 位运算 共用体 定义和访问都和结构体类似&#xff1b;区别在于共用体所有的成员变量共享同一片空间&#xff0c;内存空间就是其最大数据类型所占内存的大小&#xff1b; 如果给成员变量2的值&#xff0c;那么成员变量1的值就会被覆盖&#xff1b;主…

实现鼠标移动el-select下拉框的label上面显示出table悬浮窗

首先是对vue代码 实现思路就是在el-option里面放一个span来包裹el-popover&#xff0c;里面在放tabe实现悬浮表格 <el-form-item label"原理图编号"><el-select v-model"data.number" placeholder"请选择" clearable multiple collaps…