Android通知服务及相关概念

本文基于Android 14源码

1 NotificationManagerService的启动

1.1 添加服务

和其他系统服务一样,NotificationManagerService也是在SystemServer中启动的。

//framework/base/services/java/com/android/server/SystemServer.java
private void run() {t.traceBegin("StartServices");startBootstrapServices(t);startCoreServices(t);startOtherServices(t);startApexServices(t);
}private void startOtherServices(@NonNull TimingsTraceAndSlog t) {t.traceBegin("StartNotificationManager");mSystemServiceManager.startService(NotificationManagerService.class);SystemNotificationChannels.removeDeprecated(context);SystemNotificationChannels.createAll(context);notification = INotificationManager.Stub.asInterface(ServiceManager.getService(Context.NOTIFICATION_SERVICE));t.traceEnd();
}

NotificationManagerService是在startOtherServices中启动的,调用SystemServiceManager的startService之后,SystemServiceManager会通过反射创建NotificationManagerService实例对象,然后调用它的onStart()来启动服务。

//framework/base/services/core/java/com/android/server/notification/NotificationManagerService.java
@Override
public void onStart() {SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), (userId, r, muteOnReturn) -> {try {if (DBG) {Slog.d(TAG, "Reposting " + r.getKey() + " " + muteOnReturn);}enqueueNotificationInternal(r.getSbn().getPackageName(), r.getSbn().getOpPkg(),r.getSbn().getUid(), r.getSbn().getInitialPid(), r.getSbn().getTag(),r.getSbn().getId(),  r.getSbn().getNotification(), userId, muteOnReturn,false /* byForegroundService */);} catch (Exception e) {Slog.e(TAG, "Cannot un-snooze notification", e);}}, mUserProfiles);final File systemDir = new File(Environment.getDataDirectory(), "system");mRankingThread.start();WorkerHandler handler = new WorkerHandler(Looper.myLooper());mShowReviewPermissionsNotification = getContext().getResources().getBoolean(R.bool.config_notificationReviewPermissions);init(handler, new RankingHandlerWorker(mRankingThread.getLooper()),AppGlobals.getPackageManager(), getContext().getPackageManager(),getLocalService(LightsManager.class),new NotificationListeners(getContext(), mNotificationLock, mUserProfiles,AppGlobals.getPackageManager()),new NotificationAssistants(getContext(), mNotificationLock, mUserProfiles,AppGlobals.getPackageManager()),new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()),null, snoozeHelper, new NotificationUsageStats(getContext()),new AtomicFile(new File(systemDir, "notification_policy.xml"), "notification-policy"),(ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),getGroupHelper(), ActivityManager.getService(),LocalServices.getService(ActivityTaskManagerInternal.class),LocalServices.getService(UsageStatsManagerInternal.class),LocalServices.getService(DevicePolicyManagerInternal.class),UriGrantsManager.getService(),LocalServices.getService(UriGrantsManagerInternal.class),getContext().getSystemService(AppOpsManager.class),getContext().getSystemService(UserManager.class),new NotificationHistoryManager(getContext(), handler),mStatsManager = (StatsManager) getContext().getSystemService(Context.STATS_MANAGER),getContext().getSystemService(TelephonyManager.class),LocalServices.getService(ActivityManagerInternal.class),createToastRateLimiter(), new PermissionHelper(getContext(),AppGlobals.getPackageManager(),AppGlobals.getPermissionManager()),LocalServices.getService(UsageStatsManagerInternal.class),getContext().getSystemService(TelecomManager.class),new NotificationChannelLoggerImpl(), SystemUiSystemPropertiesFlags.getResolver(),getContext().getSystemService(PermissionManager.class),getContext().getSystemService(PowerManager.class),new PostNotificationTrackerFactory() {});publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);publishLocalService(NotificationManagerInternal.class, mInternalService);}

1.2 加载policy

在NotificationManagerService的init里面会指定notification policy保存路径,也就是/data/system/notification_policy.xml

// Persistent storage for notification policy
private AtomicFile mPolicyFile;
new AtomicFile(new File(systemDir, "notification_policy.xml"), "notification-policy")

然后将其保存到mPolicyFile,接着是加载policy文件。

mPolicyFile = policyFile;
loadPolicyFile();

来看loadPolicyFile

protected void loadPolicyFile() {if (DBG) Slog.d(TAG, "loadPolicyFile");synchronized (mPolicyFile) {InputStream infile = null;try {infile = mPolicyFile.openRead();readPolicyXml(infile, false /*forRestore*/, UserHandle.USER_ALL);} catch (FileNotFoundException e) {// No data yet// Load default managed services approvals//第一次的话文件没找到,加载默认的允许的管理服务loadDefaultApprovedServices(USER_SYSTEM); //末尾会保存文件,之后就不会走到这个异常里了allowDefaultApprovedServices(USER_SYSTEM);} catch (IOException e) {Log.wtf(TAG, "Unable to read notification policy", e);} catch (NumberFormatException e) {Log.wtf(TAG, "Unable to parse notification policy", e);} catch (XmlPullParserException e) {Log.wtf(TAG, "Unable to parse notification policy", e);} finally {IoUtils.closeQuietly(infile);}}
}

loadPolicyFile主要工作是在readPolicyXml来解析xml。

不过,首次加载会走catch方法里,第一次文件肯定不存在,catch里会加载默认数据。

先来看loadDefaultApprovedServices:

void loadDefaultApprovedServices(int userId) {mListeners.loadDefaultsFromConfig();mConditionProviders.loadDefaultsFromConfig();mAssistants.loadDefaultsFromConfig();
}

loadDefaultApprovedServices主要是加载一些默认的配置。

再来看allowDefaultApprovedServices。

protected void allowDefaultApprovedServices(int userId) {ArraySet<ComponentName> defaultListeners = mListeners.getDefaultComponents();for (int i = 0; i < defaultListeners.size(); i++) {ComponentName cn = defaultListeners.valueAt(i);allowNotificationListener(userId, cn);}ArraySet<String> defaultDnds = mConditionProviders.getDefaultPackages();for (int i = 0; i < defaultDnds.size(); i++) {allowDndPackage(userId, defaultDnds.valueAt(i));}setDefaultAssistantForUser(userId);
}

defaultListeners是从config_defaultListenerAccessPackages中获取的

String defaultListenerAccess = mContext.getResources().getString(R.string.config_defaultListenerAccessPackages);

不过,源码里面这个值是空的

<!-- Colon separated list of package names that should be granted Notification Listener access -->
<string name="config_defaultListenerAccessPackages" translatable="false"></string>

但是在GMS里有配置

<string name="config_defaultListenerAccessPackages" translatable="false">com.android.launcher3:com.google.android.projection.gearhead</string>

再来看readPolicyXml

void readPolicyXml(InputStream stream, boolean forRestore, int userId)throws XmlPullParserException, NumberFormatException, IOException {final TypedXmlPullParser parser;if (forRestore) {parser = Xml.newFastPullParser();parser.setInput(stream, StandardCharsets.UTF_8.name());} else {parser = Xml.resolvePullParser(stream);}XmlUtils.beginDocument(parser, TAG_NOTIFICATION_POLICY);boolean migratedManagedServices = false;UserInfo userInfo = mUmInternal.getUserInfo(userId);boolean ineligibleForManagedServices = forRestore &&(userInfo.isManagedProfile() || userInfo.isCloneProfile());int outerDepth = parser.getDepth();while (XmlUtils.nextElementWithin(parser, outerDepth)) {if (ZenModeConfig.ZEN_TAG.equals(parser.getName())) {mZenModeHelper.readXml(parser, forRestore, userId);} else if (PreferencesHelper.TAG_RANKING.equals(parser.getName())){mPreferencesHelper.readXml(parser, forRestore, userId);}if (mListeners.getConfig().xmlTag.equals(parser.getName())) {if (ineligibleForManagedServices) {continue;}mListeners.readXml(parser, mAllowedManagedServicePackages, forRestore, userId);migratedManagedServices = true;} else if (mAssistants.getConfig().xmlTag.equals(parser.getName())) {if (ineligibleForManagedServices) {continue;}mAssistants.readXml(parser, mAllowedManagedServicePackages, forRestore, userId);migratedManagedServices = true;} else if (mConditionProviders.getConfig().xmlTag.equals(parser.getName())) {if (ineligibleForManagedServices) {continue;}mConditionProviders.readXml(parser, mAllowedManagedServicePackages, forRestore, userId);migratedManagedServices = true;} else if (mSnoozeHelper.XML_TAG_NAME.equals(parser.getName())) {mSnoozeHelper.readXml(parser, System.currentTimeMillis());}if (LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG.equals(parser.getName())) {if (forRestore && userId != UserHandle.USER_SYSTEM) {continue;}mLockScreenAllowSecureNotifications = parser.getAttributeBoolean(null,LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE, true);}}if (!migratedManagedServices) {mListeners.migrateToXml();mAssistants.migrateToXml();mConditionProviders.migrateToXml();handleSavePolicyFile();}mAssistants.resetDefaultAssistantsIfNecessary();
}

xml的解析的tag是从notification-policy开始的。

如果tag是zen,则调用mZenModeHelper.readXml(parser, forRestore, userId)

如果是ranking,调用mPreferencesHelper.readXml(parser, forRestore, userId)

然后将配置保存的对应的config对象里面。

2 重要类

2.1 Notification

/*** A class that represents how a persistent notification is to be presented to* the user using the {@link android.app.NotificationManager}.** <p>The {@link Notification.Builder Notification.Builder} has been added to make it* easier to construct Notifications.</p>** <div class="special reference">* <h3>Developer Guides</h3>* <p>For a guide to creating notifications, read the* <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>* developer guide.</p>* </div>*/
public class Notification implements Parcelable

从描述看,Notification是通过NotificationManager来组装通知,呈现给用户。它是App层创建Notification使用的数据结构。Notification包含title,icon,discription等内容。

通过使用Notification.Builder可以使创建通知更加简单。

Notification实现了Parcelable,因此可以跨进程传递。

再来按他的成员变量:

public long when;
public int icon;
public PendingIntent contentIntent;
public PendingIntent deleteIntent;
public PendingIntent fullScreenIntent;
public CharSequence tickerText;
public RemoteViews tickerView;
public RemoteViews contentView;
public RemoteViews bigContentView;
public RemoteViews headsUpContentView;
public Uri sound;

Notification的成变量和成员函数比较多,从代码看,主要是提供了描述通知的逻辑。

Notification还至少有一个Action,Action是以PendingIntent的形式关联到Notification中的,也可以通过NotificationCompat.Builder.addAction(int icon, CharSequence title, PendingIntent intent)函数设定其他的action。Action在UI中是以Button的形式体现的。

2.2 NotificationRecord

/*** Holds data about notifications that should not be shared with the* {@link android.service.notification.NotificationListenerService}s.** <p>These objects should not be mutated unless the code is synchronized* on {@link NotificationManagerService#mNotificationLock}, and any* modification should be followed by a sorting of that list.</p>** <p>Is sortable by {@link NotificationComparator}.</p>** {@hide}*/
public final class NotificationRecord {}

NotificationRecord是NotificationManagerService用来管理所有Notification的数据结构。包含Notification数据结构,package,userid,id,tag,statusBarKey等内容,其中package,userid,id,tag可以用来唯一标识一个NotificationRecord,statusBarKey是可以唯一标识StatusBarManagerService中StatusBarNotification的。

再来看他的一些变量和函数。

private final StatusBarNotification sbn;
NotificationUsageStats.SingleNotificationStats stats;
private final NotificationStats mStats;
private ArraySet<String> mPhoneNumbers;public NotificationRecord(Context context, StatusBarNotification sbn,NotificationChannel channel) {this.sbn = sbn;mTargetSdkVersion = LocalServices.getService(PackageManagerInternal.class).getPackageTargetSdkVersion(sbn.getPackageName());mAm = ActivityManager.getService();mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);mOriginalFlags = sbn.getNotification().flags;mRankingTimeMs = calculateRankingTimeMs(0L);mCreationTimeMs = sbn.getPostTime();mUpdateTimeMs = mCreationTimeMs;mInterruptionTimeMs = mCreationTimeMs;mContext = context;stats = new NotificationUsageStats.SingleNotificationStats();mChannel = channel;mPreChannelsNotification = isPreChannelsNotification();mSound = calculateSound();mVibration = calculateVibration();mAttributes = calculateAttributes();mImportance = calculateInitialImportance();mLight = calculateLights();mAdjustments = new ArrayList<>();mStats = new NotificationStats();calculateUserSentiment();calculateGrantableUris();
}

2.2.1 Sound获取

private Uri calculateSound() {final Notification n = getSbn().getNotification();// No notification sounds on tvif (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {return null;}Uri sound = mChannel.getSound();if (mPreChannelsNotification && (getChannel().getUserLockedFields()& NotificationChannel.USER_LOCKED_SOUND) == 0) {final boolean useDefaultSound = (n.defaults & Notification.DEFAULT_SOUND) != 0;if (useDefaultSound) {sound = Settings.System.DEFAULT_NOTIFICATION_URI;} else {sound = n.sound;}}return sound;
}

主要是获取声音的uri,可以看到默认位置是Settings.System.DEFAULT_NOTIFICATION_URI,当然也可以从Notification创建的时候进行指定。

2.2.2 light获取

private Light calculateLights() {int defaultLightColor = mContext.getResources().getColor(com.android.internal.R.color.config_defaultNotificationColor);int defaultLightOn = mContext.getResources().getInteger(com.android.internal.R.integer.config_defaultNotificationLedOn);int defaultLightOff = mContext.getResources().getInteger(com.android.internal.R.integer.config_defaultNotificationLedOff);int channelLightColor = getChannel().getLightColor() != 0 ? getChannel().getLightColor(): defaultLightColor;Light light = getChannel().shouldShowLights() ? new Light(channelLightColor,defaultLightOn, defaultLightOff) : null;if (mPreChannelsNotification&& (getChannel().getUserLockedFields()& NotificationChannel.USER_LOCKED_LIGHTS) == 0) {final Notification notification = getSbn().getNotification();if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {light = new Light(notification.ledARGB, notification.ledOnMS,notification.ledOffMS);if ((notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {light = new Light(defaultLightColor, defaultLightOn,defaultLightOff);}} else {light = null;}}return light;
}

也是有默认颜色和默认开关设置,当然也支持自定义设置。

2.3 StatusBarNotification

/*** Class encapsulating a Notification. Sent by the NotificationManagerService to clients including* the status bar and any {@link android.service.notification.NotificationListenerService}s.*/
public StatusBarNotification(String pkg, String opPkg, int id,String tag, int uid, int initialPid, Notification notification, UserHandle user,String overrideGroupKey, long postTime) {if (pkg == null) throw new NullPointerException();if (notification == null) throw new NullPointerException();this.pkg = pkg;this.opPkg = opPkg;this.id = id;this.tag = tag;this.uid = uid;this.initialPid = initialPid;this.notification = notification;this.user = user;this.postTime = postTime;this.overrideGroupKey = overrideGroupKey;this.key = key();this.groupKey = groupKey();
}

StatusBarNotification是StatusBarManagerService中notification的数据结构,包含package,userid,id,tag和Notification数据结构等。

NotificationManagerService会将其发送给客户端,如SystemUI,因此它也实现了Parcelable。

2.3.1 key

可以看到key是一堆唯一的表示组合到一起的字符串.

private String key() {String sbnKey = user.getIdentifier() + "|" + pkg + "|" + id + "|" + tag + "|" + uid;if (overrideGroupKey != null && getNotification().isGroupSummary()) {sbnKey = sbnKey + "|" + overrideGroupKey;}return sbnKey;
}

2.3.2 groupKey

groupKey和key类似。

private String groupKey() {if (overrideGroupKey != null) {return user.getIdentifier() + "|" + pkg + "|" + "g:" + overrideGroupKey;}final String group = getNotification().getGroup();final String sortKey = getNotification().getSortKey();if (group == null && sortKey == null) {// a group of onereturn key;}return user.getIdentifier() + "|" + pkg + "|" +(group == null? "c:" + notification.getChannelId(): "g:" + group);
}

2.2.3 isGroup

/*** Returns true if this notification is part of a group.*/
public boolean isGroup() {if (overrideGroupKey != null || isAppGroup()) {return true;}return false;
}/*** Returns true if application asked that this notification be part of a group.*/
public boolean isAppGroup() {if (getNotification().getGroup() != null || getNotification().getSortKey() != null) {return true;}return false;
}

2.4 StatusBarManagerService

/*** A note on locking:  We rely on the fact that calls onto mBar are oneway or* if they are local, that they just enqueue messages to not deadlock.*/
public StatusBarManagerService(Context context) {mContext = context;LocalServices.addService(StatusBarManagerInternal.class, mInternalService);// We always have a default display.final UiState state = new UiState();mDisplayUiState.put(DEFAULT_DISPLAY, state);final DisplayManager displayManager =(DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);displayManager.registerDisplayListener(this, mHandler);mActivityTaskManager = LocalServices.getService(ActivityTaskManagerInternal.class);mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);mTileRequestTracker = new TileRequestTracker(mContext);mSessionMonitor = new SessionMonitor(mContext);
}

StatusBarManagerService是StatusBar的后台管理服务,处理StatusBar相关事件,例如显示Notification,Buttery/Signal Status,System Time等。同时也会处理Notification显示,消去,并且点击Notification时发送PendingIntent等也都需要通过这个Service。StatusBarManagerService也会跟NotificationManagerService交互,处理Notification相关的动作。

StatusBarManagerService的构造函数中,添加了StatusBarManagerInternal服务。

/*** Private API used by NotificationManagerService.*/
private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() {@Overridepublic void onCameraLaunchGestureDetected(int source) {if (mBar != null) {try {mBar.onCameraLaunchGestureDetected(source);} catch (RemoteException e) {}}}@Overridepublic void setDisableFlags(int displayId, int flags, String cause) {StatusBarManagerService.this.setDisableFlags(displayId, flags, cause);}@Overridepublic void toggleSplitScreen() {enforceStatusBarService();if (mBar != null) {try {mBar.toggleSplitScreen();} catch (RemoteException ex) {}}}
}

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

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

相关文章

为写论文头疼?推荐4款ai写毕业论文初稿的软件

写论文对于许多学生来说是一项既重要又具挑战性的任务。为了帮助大家更高效地完成这一过程&#xff0c;我将推荐四款优秀的AI写毕业论文初稿的软件&#xff0c;并详细介绍它们的功能和优势。 传送门&#xff1a;https://www.aipaperpass.com?piclLGw 千笔-AIPassPaper是一款功…

面向对象例题之例题的特性

答案&#xff1a;C 解析&#xff1a;对象里面的方法和属性数量是不确定的&#xff0c;可以不断扩展写多个属性和方法 清洗的边界是对象必备的&#xff0c;哪些是这个类的&#xff0c;哪些是其他类的都有体现。 良好的定义行为一般指定义良好的属性和方法 可扩展性指的是子类…

【问题随记】在使用 AuthenticationManager 的时候,出现循环依赖问题 —— `java.lang.StackOverflowError`

问题随记 在使用 AuthenticationManager 的时候&#xff0c;出现循环依赖问题 —— java.lang.StackOverflowError&#xff0c;查资料查了两天半&#xff0c;终于找到原因。 2024-06-16T17:54:19.48708:00 ERROR 20672 --- [nio-8789-exec-1] o.a.c.c.C.[.[.[/].[dispatcherS…

波分技术基础 -- FEC

信号在传输过程中&#xff0c;不可避免的会出现劣化、误码&#xff0c;FEC (Forward error correction) 技术确保通信系统在噪声和其他损伤的影响下&#xff0c;依然能够实现无错误传输。 应用场景&#xff1a;长途密集波分系统&#xff08;DWDM&#xff09;实现方式&#xff…

LED显示屏迎来革新:GOB封装技术引领行业新风尚

在我们日常生活中&#xff0c;LED显示屏无处不在&#xff0c;从繁华的街头广告牌到家庭娱乐中心的大屏幕电视&#xff0c;它们都以鲜明的色彩和清晰的画质吸引着我们的目光。然而&#xff0c;在LED显示屏技术日新月异的今天&#xff0c;一种名为GOB&#xff08;Glue On Board&a…

python:给1个整数,你怎么判断是否等于2的幂次方?

最近在csdn上刷到一个比较简单的题目&#xff0c;题目要求不使用循环和递归来实现检查1个整数是否等于2的幂次方&#xff0c;题目如下&#xff1a; 题目的答案如下&#xff1a; def isPowerofTwo(n):z bin(n)[2:]print(bin(n))if z[0] ! 1:return Falsefor i in z[1:]:if i !…

华为全联接大会HUAWEI Connect 2024印象(二):昇腾AI端侧推理

此次参加HUAWEI Connect 2024最主要目标是了解昇腾AI端侧推理技术&#xff0c;希望将其融合到我现在嵌入式系统课程中&#xff0c;不过刚开始在一楼找到一个小展台&#xff0c;看到了香橙派Orange Pi。香橙派是深圳迅龙的一个品牌&#xff0c;他们和很多芯片厂商都合作过&#…

IPsec-VPN中文解释

网络括谱图 IPSec-VPN 配置思路 1 配置IP地址 FWA:IP地址的配置 [FW1000-A]interface GigabitEthernet 1/0/0 [FW1000-A-GigabitEthernet1/0/0]ip address 10.1.1.1 24 //配置IP地址 [FW1000-A]interface GigabitEthernet 1/0/2 [FW1000-A-GigabitEthernet1/0/2]ip a…

计算机毕业设计 基于Python的美术馆预约系统的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

【笔记】第三节 组织与性能

3.1 基本成分 3.2 微观组织特征 0.6-0.8C%碳素钢的组织为珠光体和少量的铁素体。 如何把组织和性能联系起来&#xff1f;德国克虏伯公司的研究——珠光体片间距与渗碳体片层厚度成比例&#xff1a; t s 0 ( ρ 15 ( C % ) − 1 ) ts_0(\frac{\rho}{15(C\%)}-1) ts0​(15(C%)…

【Web】PolarCTF2024秋季个人挑战赛wp

EZ_Host 一眼丁真命令注入 payload: ?host127.0.0.1;catf* 序列一下 exp: <?phpclass Polar{public $lt;public $b; } $pnew Polar(); $p->lt"system"; $p->b"tac /f*"; echo serialize($p);payload: xO:5:"Polar":2:{s:2:"…

我的AI工具箱Tauri版-VideoDuplication视频素材去重

本教程基于自研的AI工具箱Tauri版进行VideoDuplication视频素材去重。 该项目是基于自研的AI工具箱Tauri版的视频素材去重工具&#xff0c;用于高效地处理和去除重复视频内容。用户可以通过搜索关键词"去重"或通过路径导航到"Python音频技术/视频tools"模…

MySQL高阶1907-按分类统计薪水

目录 题目 准备数据 分析数据 总结 题目 结果表 必须 包含所有三个类别。 如果某个类别中没有帐户&#xff0c;则报告 0 。 按 任意顺序 返回结果表。 查询每个工资类别的银行账户数量。 工资类别如下&#xff1a; "Low Salary"&#xff1a;所有工资 严格低于…

MQ入门(4)

Erlang&#xff1a;面向高并发的 单机的吞吐量就是并发性&#xff1a;Rabbitmq是10w左右&#xff08;现实项目中已经足够用了&#xff09;&#xff0c;RocketMQ是10w到20w&#xff0c;Kafka是100w左右。 公司里的并发&#xff08;QPS&#xff09; 大部分的公司每天的QPS大概…

自动化测试框架设计详解

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、引言 随着IT技术的快速发展&#xff0c;软件开发变得越来越快速和复杂化。在这种背景下&#xff0c;传统的手工测试方式已经无法满足测试需求&#xff0c;而自…

【RabbitMQ】⾼级特性

RabbitMQ ⾼级特性 1. 消息确认1.1 消息确认机制1.2 代码示例 2. 持久化2.1 交换机持久化2.2 队列持久化2.3 消息持久化 3. 发送⽅确认3.1 confirm确认模式3.2 return退回模式3.3 问题: 如何保证RabbitMQ消息的可靠传输? 4. 重试机制5. TTL5.1 设置消息的TTL5.2 设置队列的TTL…

C++——模拟实现string

1.再谈string string为什么要被设计成模板&#xff1f;日常使用string好像都是char*&#xff0c;char*不够使用吗&#xff0c;为什么要设计成模板呢&#xff1f; 1.1 关于编码 //计算机的存储如何区分呢&#xff1f;int main() {//比如在C语言中&#xff0c;有整型//如果是有…

msvcp140.dll0丢失的解决方法,总结6种靠谱的解决方法

再使用计算机的过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“msvcp140.dll丢失”。这个问题可能会影响到我们的正常使用&#xff0c;因此需要及时解决。经过一段时间的学习和实践&#xff0c;我总结了以下六种靠谱的解决方法&#xff0c;希望对大家…

Spring的任务调度

Spring的任务调度 1.概述 Spring框架为任务调度提供了专门的解决方案。在Spring框架的org.springframework.scheduling包中&#xff0c;通过对JDK 的ScheduledExecutorService接口的实例进行封装&#xff0c;对外提供了一些注解和接口&#xff0c;为开发者处理定时任务提供了…

STM32F407单片机编程入门(十二) FreeRTOS实时操作系统详解及实战含源码

文章目录 一.概要二.什么是实时操作系统三.FreeRTOS的特性四.FreeRTOS的任务详解1.任务函数定义2.任务的创建3.任务的调度原理 五.CubeMX配置一个FreeRTOS例程1.硬件准备2.创建工程3.调试FreeRTOS任务调度 六.CubeMX工程源代码下载七.小结 一.概要 FreeRTOS是一个迷你的实时操…