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

Android 9.0系统源码_通知服务(三)应用发送状态栏通知的流程

前言

应用发送一个显示在状态栏上的通知,对于移动设备来说是很常见的一种功能需求,本篇文章我们将会结合Android9.0系统源码具体来分析一下,应用调用notificationManager触发通知栏通知功能的源码流程。

一、应用触发状态栏通知

应用可以通过调用如下notity方法可以发送一个显示在状态栏上的通知。

    /*** 发送通知(支持8.0+)*/public void nofify() {// 1. Set the notification content - 创建通知基本内容// https://developer.android.google.cn/training/notify-user/build-notification.html#builderNotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID).setSmallIcon(R.drawable.ic_launcher_background).setContentTitle("My notification")// 这是单行//.setContentText("Much longer text that cannot fit one line...")// 这是多行.setStyle(new NotificationCompat.BigTextStyle().bigText("Much longer text that cannot fit one line..." +"Much longer text that cannot fit one line..." +"Much longer text that cannot fit one line...")).setPriority(NotificationCompat.PRIORITY_HIGH).setAutoCancel(true);// 2. Create a channel and set the importance - 8.0后需要设置Channel// https://developer.android.google.cn/training/notify-user/build-notification.html#buildercreateNotificationChannel();// 3. Set the notification's tap action - 创建一些点击事件,比如点击跳转页面// https://developer.android.google.cn/training/notify-user/build-notification.html#click// 4. Show the notification - 展示通知// https://developer.android.google.cn/training/notify-user/build-notification.html#notifyNotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);// 5.调用notificationManager的notify方法// notificationId is a unique int for each notification that you must definenotificationManager.notify((int) System.currentTimeMillis(), builder.build());}private void createNotificationChannel() {// Create the NotificationChannel, but only on API 26+ because// the NotificationChannel class is new and not in the support libraryCharSequence name = getString(R.string.app_name);String description = getString(R.string.app_name);int importance = NotificationManager.IMPORTANCE_HIGH;NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);channel.setDescription(description);// Register the channel with the system; you can't change the importance// or other notification behaviors after thisNotificationManager notificationManager = getSystemService(NotificationManager.class);notificationManager.createNotificationChannel(channel);}

notify方法先是构建Notification对象,然后调用NotificationManager的notify方法发送构建的这个对象。

二、NotificationManager的相关源码

1、NotificationManager的notify方法如下所示:

frameworks/base/core/java/android/app/NotificationManager.java

public class NotificationManager {//如果应用发送了一个相同id的通知,并且没有被取消,它将被更新的信息所取代。public void notify(int id, Notification notification){notify(null, id, notification);}//应用发送了一个相同tag和id的通知,并且没有被取消,它将被更新的信息所取代。   public void notify(String tag, int id, Notification notification){notifyAsUser(tag, id, notification, mContext.getUser());}
}

2、notify方法最终都会进一步调用notifyAsUser。

public class NotificationManager {/*** @hide*/public void notifyAsUser(String tag, int id, Notification notification, UserHandle user){INotificationManager service = getService();String pkg = mContext.getPackageName();// Fix the notification as best we can.Notification.addFieldsFromContext(mContext, notification);...代码省略...}
}

notifyAsUser方法首先获取NotificationManagerService服务,然后调用了Notification的addFieldsFromContext方法。

3、Notification的addFieldsFromContext方法如下所示:

frameworks/base/core/java/android/app/Notification.java

public class Notification implements Parcelable
{public Bundle extras = new Bundle();/*** @hide*/public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";/*** @hide*/public static void addFieldsFromContext(Context context, Notification notification) {addFieldsFromContext(context.getApplicationInfo(), notification);}/*** @hide*/public static void addFieldsFromContext(ApplicationInfo ai, Notification notification) {notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);}
}

主要是在Notification对象中类型为Bundle的属性变量extras中保存了当前应用所对应的ApplicationInfo对象。

4、继续往下看NotificationManager的notifyAsUser方法

public class NotificationManager {/*** @hide*/public void notifyAsUser(String tag, int id, Notification notification, UserHandle user){INotificationManager service = getService();String pkg = mContext.getPackageName();// Fix the notification as best we can.Notification.addFieldsFromContext(mContext, notification);if (notification.sound != null) {notification.sound = notification.sound.getCanonicalUri();if (StrictMode.vmFileUriExposureEnabled()) {notification.sound.checkFileUriExposed("Notification.sound");}}//通过包名获取对应包名的应用图标设置为通知的小图标fixLegacySmallIcon(notification, pkg);if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {//Android5.1之后,会判断notification是否有small icon,没有则抛出异常if (notification.getSmallIcon() == null) {throw new IllegalArgumentException("Invalid notification (no valid small icon): "+ notification);}}...代码省略...try {service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,copy, user.getIdentifier());} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}
}

会调用fixLegacySmallIcon方法通,过包名获取对应包名的应用图标设置为通知的小图标,在Android5.1之后的版本中,然后会判断notification是否有small icon,如果没有设icon或small icon,用notify方法时会抛出异常,最终会调用NotificationManagerService的enqueueNotificationWithTag方法。

三、NotificationManagerService和发送通知相关的源码

1、NotificationManagerService的enqueueNotificationWithTag方法如下所示。

frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java

public class NotificationManagerService extends SystemService {@Overridepublic void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,Notification notification, int userId) throws RemoteException {enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),Binder.getCallingPid(), tag, id, notification, userId);}
}

2、enqueueNotificationWithTag方法会继续调用了enqueueNotificationInternal方法,该方法发送通知的核心。

public class NotificationManagerService extends SystemService {void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,final int callingPid, final String tag, final int id, final Notification notification,int incomingUserId) {if (DBG) {Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id+ " notification=" + notification);}checkCallerIsSystemOrSameApp(pkg);// 校验UIDfinal int userId = ActivityManager.handleIncomingUser(callingPid,callingUid, incomingUserId, true, false, "enqueueNotification", pkg);final UserHandle user = new UserHandle(userId);if (pkg == null || notification == null) {throw new IllegalArgumentException("null not allowed: pkg=" + pkg+ " id=" + id + " notification=" + notification);}// The system can post notifications for any package, let us resolve that.final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);...代码省略...//上面会进行一系列验证,验证之后,会将传递进来的Notification封装成一个StatusBarNotification对象final StatusBarNotification n = new StatusBarNotification(pkg, opPkg, id, tag, notificationUid, callingPid, notification,user, null, System.currentTimeMillis());// 封装NotificationRecord对象final NotificationRecord r = new NotificationRecord(getContext(), n, channel);...代码省略...mHandler.post(new EnqueueNotificationRunnable(userId, r));}
}

enqueueNotificationInternal会进行一些列验证,待验证完成之后,会调用Handler的post方法开启线程,发起异步操作,触发EnqueueNotificationRunnable对象。

3、EnqueueNotificationRunnable对象如下所示。

public class NotificationManagerService extends SystemService {protected class EnqueueNotificationRunnable implements Runnable {private final NotificationRecord r;private final int userId;EnqueueNotificationRunnable(int userId, NotificationRecord r) {this.userId = userId;this.r = r;};@Overridepublic void run() {synchronized (mNotificationLock) {//将当前通知相关的NotificationRecord对象放到集合中mEnqueuedNotifications.add(r);scheduleTimeoutLocked(r);final StatusBarNotification n = r.sbn;if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());//获取是否存在相同的NotificationRecordNotificationRecord old = mNotificationsByKey.get(n.getKey());if (old != null) {// Retain ranking information from previous recordr.copyRankingInformation(old);}final int callingUid = n.getUid();final int callingPid = n.getInitialPid();final Notification notification = n.getNotification();final String pkg = n.getPackageName();final int id = n.getId();final String tag = n.getTag();// Handle grouped notifications and bail out early if we// can to avoid extracting signals.handleGroupedNotificationLocked(r, old, callingUid, callingPid);// if this is a group child, unsnooze parent summaryif (n.isGroup() && notification.isGroupChild()) {mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey());}// This conditional is a dirty hack to limit the logging done on//     behalf of the download manager without affecting other apps.if (!pkg.equals("com.android.providers.downloads")|| Log.isLoggable("DownloadManager", Log.VERBOSE)) {int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;if (old != null) {enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;}EventLogTags.writeNotificationEnqueue(callingUid, callingPid,pkg, id, tag, userId, notification.toString(),enqueueStatus);}mRankingHelper.extractSignals(r);// tell the assistant service about the notificationif (mAssistants.isEnabled()) {mAssistants.onNotificationEnqueued(r);//开启延时线程mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),DELAY_FOR_ASSISTANT_TIME);} else {//开启线程mHandler.post(new PostNotificationRunnable(r.getKey()));}}}}}    

NotificationManagerService 的run方法会将当前NotificationRecord存放到类型为ArrayList的mEnqueuedNotifications集合中,最终会再次调用Handler的post方法开启线程,发起异步操作,触发PostNotificationRunnable对象。

4、PostNotificationRunnable对象如下所示。

public class NotificationManagerService extends SystemService {private NotificationListeners mListeners;private GroupHelper mGroupHelper;protected class PostNotificationRunnable implements Runnable {private final String key;PostNotificationRunnable(String key) {this.key = key;}@Overridepublic void run() {synchronized (mNotificationLock) {try {NotificationRecord r = null;//从类型为ArrayList<NotificationRecord>的mEnqueuedNotifications集合中//取当前key所对应的NotificationRecord对象int N = mEnqueuedNotifications.size();for (int i = 0; i < N; i++) {final NotificationRecord enqueued = mEnqueuedNotifications.get(i);if (Objects.equals(key, enqueued.getKey())) {r = enqueued;break;}}if (r == null) {Slog.i(TAG, "Cannot find enqueued record for key: " + key);return;}r.setHidden(isPackageSuspendedLocked(r));NotificationRecord old = mNotificationsByKey.get(key);final StatusBarNotification n = r.sbn;final Notification notification = n.getNotification();// 判断是否是已经发送过此notificationint index = indexOfNotificationLocked(n.getKey());if (index < 0) {//如果是新发送的notification,就走新增流程.mNotificationList.add(r);mUsageStats.registerPostedByApp(r);r.setInterruptive(isVisuallyInterruptive(null, r));} else {//如果有发送过,就获取已经存在的NtificationRecord,// 后面走更新流程 mStatusBar.updateNotification(r.statusBarKey, n)old = mNotificationList.get(index);mNotificationList.set(index, r);mUsageStats.registerUpdatedByApp(r, old);// Make sure we don't lose the foreground service state.notification.flags |=old.getNotification().flags & FLAG_FOREGROUND_SERVICE;r.isUpdate = true;r.setTextChanged(isVisuallyInterruptive(old, r));}//将当前NotificationRecord对象以StatusBarNotification为键存放到mNotificationsByKey中mNotificationsByKey.put(n.getKey(), r);// Ensure if this is a foreground service that the proper additional// flags are set.if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0) {notification.flags |= Notification.FLAG_ONGOING_EVENT| Notification.FLAG_NO_CLEAR;}applyZenModeLocked(r);mRankingHelper.sort(mNotificationList);if (notification.getSmallIcon() != null) {// 如果notification设置了smallIcon,调用所有NotificationListeners的notifyPostedLocked方法,// 通知有新的notification,传入的参数为上面的NotificationRecord对象StatusBarNotification oldSbn = (old != null) ? old.sbn : null;//通知监听者回调方法mListeners.notifyPostedLocked(r, old);if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {mHandler.post(new Runnable() {@Overridepublic void run() {mGroupHelper.onNotificationPosted(n, hasAutoGroupSummaryLocked(n));}});}} else {//移除已经存在的Slog.e(TAG, "Not posting notification without small icon: " + notification);if (old != null && !old.isCanceled) {mListeners.notifyRemovedLocked(r,NotificationListenerService.REASON_ERROR, null);mHandler.post(new Runnable() {@Overridepublic void run() {mGroupHelper.onNotificationRemoved(n);}});}// ATTENTION: in a future release we will bail out here// so that we do not play sounds, show lights, etc. for invalid// notificationsSlog.e(TAG, "WARNING: In a future release this will crash the app: "+ n.getPackageName());}if (!r.isHidden()) {//如果不是隐藏,则触发振动/铃声/呼吸灯buzzBeepBlinkLocked(r);}maybeRecordInterruptionLocked(r);} finally {int N = mEnqueuedNotifications.size();for (int i = 0; i < N; i++) {final NotificationRecord enqueued = mEnqueuedNotifications.get(i);if (Objects.equals(key, enqueued.getKey())) {mEnqueuedNotifications.remove(i);break;}}}}}}
}

5、如果验证通过,还会调用buzzBeepBlinkLocked方法触发震动、铃声、呼吸灯

public class NotificationManagerService extends SystemService {//触发振动/铃声/呼吸灯void buzzBeepBlinkLocked(NotificationRecord record) {boolean buzz = false;//震动boolean beep = false;//铃声boolean blink = false;//呼吸灯final Notification notification = record.sbn.getNotification();final String key = record.getKey();// Should this notification make noise, vibe, or use the LED?final boolean aboveThreshold =record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;// Remember if this notification already owns the notification channels.boolean wasBeep = key != null && key.equals(mSoundNotificationKey);boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);// These are set inside the conditional if the notification is allowed to make noise.boolean hasValidVibrate = false;boolean hasValidSound = false;boolean sentAccessibilityEvent = false;// If the notification will appear in the status bar, it should send an accessibility// eventif (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {sendAccessibilityEvent(notification, record.sbn.getPackageName());sentAccessibilityEvent = true;}if (aboveThreshold && isNotificationForCurrentUser(record)) {if (mSystemReady && mAudioManager != null) {Uri soundUri = record.getSound();hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);long[] vibration = record.getVibration();// Demote sound to vibration if vibration missing & phone in vibration mode.if (vibration == null&& hasValidSound&& (mAudioManager.getRingerModeInternal()== AudioManager.RINGER_MODE_VIBRATE)&& mAudioManager.getStreamVolume(AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) == 0) {vibration = mFallbackVibrationPattern;}hasValidVibrate = vibration != null;boolean hasAudibleAlert = hasValidSound || hasValidVibrate;if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {if (!sentAccessibilityEvent) {sendAccessibilityEvent(notification, record.sbn.getPackageName());sentAccessibilityEvent = true;}if (DBG) Slog.v(TAG, "Interrupting!");if (hasValidSound) {mSoundNotificationKey = key;if (mInCall) {playInCallNotification();beep = true;} else {//播放铃声beep = playSound(record, soundUri);}}final boolean ringerModeSilent =mAudioManager.getRingerModeInternal()== AudioManager.RINGER_MODE_SILENT;if (!mInCall && hasValidVibrate && !ringerModeSilent) {mVibrateNotificationKey = key;//震动buzz = playVibration(record, vibration, hasValidSound);}}}}// If a notification is updated to remove the actively playing sound or vibrate,// cancel that feedback nowif (wasBeep && !hasValidSound) {clearSoundLocked();}if (wasBuzz && !hasValidVibrate) {clearVibrateLocked();}// light// release the light// 呼吸灯boolean wasShowLights = mLights.remove(key);if (record.getLight() != null && aboveThreshold&& ((record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_LIGHTS) == 0)) {mLights.add(key);updateLightsLocked();if (mUseAttentionLight) {mAttentionLight.pulse();}blink = true;} else if (wasShowLights) {updateLightsLocked();}//应用请求了振动/铃声/呼吸灯,输出notification_alert日志if (buzz || beep || blink) {record.setInterruptive(true);MetricsLogger.action(record.getLogMaker().setCategory(MetricsEvent.NOTIFICATION_ALERT).setType(MetricsEvent.TYPE_OPEN).setSubtype((buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)));EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);}}
}

四、总结

在这里插入图片描述

相关文章:

Android 9.0系统源码_通知服务(三)应用发送状态栏通知的流程

前言 应用发送一个显示在状态栏上的通知&#xff0c;对于移动设备来说是很常见的一种功能需求&#xff0c;本篇文章我们将会结合Android9.0系统源码具体来分析一下&#xff0c;应用调用notificationManager触发通知栏通知功能的源码流程。 一、应用触发状态栏通知 应用可以通…...

python中的序列——笔记

一、介绍 ABC语言时一个致力于为初学者设计编程环境的长达十年的研究项目。 Python也从ABC那里继承了用统一的风格去处理序列数据这一特点。不管是哪种数据结构&#xff0c;字符串、列表、字节序列、数组、XML元素&#xff0c;抑或是数据库查询结果&#xff0c;它们都共用一套…...

taobao.user.seller.get( 查询卖家用户信息 )

&#xffe5;开放平台基础API必须用户授权 查询卖家用户信息&#xff08;只能查询有店铺的用户&#xff09; 只能卖家类应用调用。 公共参数 请求地址: HTTP地址 http://gw.api.taobao.com/router/rest 公共请求参数: 公共响应参数: 请求参数 点击获取key和secret请求示例…...

WebRTC Qos策略

1.WebRTC 用于提升 QoS 的方法&#xff1a;NACK、FEC、SVC、JitterBuffer、IDR Request、PACER、Sender Side BWE、VFR&#xff08;动态帧率调整策略&#xff09;https://blog.csdn.net/CrystalShaw/article/details/80432267丢包重传NACK&#xff1a;一种通知技术&#xff0c;…...

Mysql数据查询

文章目录1 group by子句2 回溯统计3 having子句1 group by子句 group by子句**&#xff1a;分组统计&#xff0c;根据某个字段将所有的结果分类&#xff0c;并进行数据统计分析 分组的目的不是为了显示数据&#xff0c;一定是为了统计数据group by子句一定是出现在where子句之…...

Kafka入门(五)

下面聊聊Kafka常用命令 1、Topic管理命令 以topic&#xff1a;test_1为例 1.1、创建topic ./bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 3 --partitions 3 --topic test_1参数说明&#xff1a; –bootstrap-server&#xff1a;…...

如何快速在windows系统中切换node.js版本

前言 最近在同时维护公司的两个项目&#xff0c;一个是新项目&#xff0c;另一个是老项目&#xff0c;二者所依赖的node版本是不一致的。 这就导致我在切换项目的时候必须重新安装对应版本的 node.js&#xff0c;否则就会报各种神马错误。 但这一卸一装可着实烦死个银&#xf…...

设计模式-单例模式(java)

单例是一种常用的设计模式&#xff0c;它的目的是确保一个类只有一个实例&#xff0c;并提供一个全局访问点。在Java编程语言中&#xff0c;实现单例有多种方法&#xff0c;本篇文章将介绍其中的两种实现方式。 方式一&#xff1a;饿汉式单例模式 饿汉式单例模式是最简单的实…...

Revit中复合墙图层的规则和CAD识别翻模墙

一、Revit中用于指定复合墙图层的规则&#xff0c;具体内容? 在编辑复合墙的结构时&#xff0c;请使用“指定图层”工具将“编辑部件”对话框中的行指定给图层或预览窗格中的区域&#xff0c;并遵循这些原则。 在预览窗格中&#xff0c;样本墙的各个行必须保持从左到右的顺序显…...

【DL】Paddle BML Codelab环境使用说明 - 知识点目录

《Paddle BML Codelab环境使用说明》 1. 编辑区 Code Cell 1.1 Code Cell 操作 Magic关键字/魔术命令 Magic命令含义%timeit测试单行语句的执行时间%%timeit测试代码块的执行时间%matplotlib inline显示matplotlib生成的图形%run调用外部python脚本%pdb 调试程序%pwd 查看当…...

python正则表达式处理文本-re模块

python正则表达式处理文本-re模块 1.概述 正则表达式通常用于含有大量文本处理的应用当中。例如&#xff0c;它们经常用作开发者使用的文本编辑程序的搜索模式&#xff0c;包括 vi&#xff0c;emacs 和现代集成开发环境。它们也是 Unix 命令行工具的组成部分&#xff0c;例如…...

换了固态硬盘需要重装系统吗?教你如何实现不重装系统!

电脑大家都用过嘛&#xff0c;如果您的计算机装的还是机械硬盘&#xff0c;想必阁下肯定是修身养性的高手&#xff0c;因为在这个浮躁的社会中&#xff0c;是很少有人能够忍受5分钟甚至更久的开机时间的&#xff0c;不仅开机慢&#xff0c;应用程序的响应速度也很慢&#xff0c…...

网上医疗预约挂号系统

技术&#xff1a;Java、JSP等摘要&#xff1a;网上医疗预约挂号系统是主要是对居民的保健、护理、疾病预防等健康信息实行有效的预约挂号管理。医疗机构为居民建立完整的健康档案&#xff0c;安排体检以及实施免疫等预防措施。而基于Web的远程保健平台以网上医疗预约挂号系统为…...

专题:一看就会的C++类模板讲解 (1)

目录 一.类模板的作用 二.类模板的定义&#xff1a; 三.类模板的声明格式&#xff1a; 四.类模板对象 五.再举一个例子 一.类模板的作用 面向对象的程序设计编程实践中&#xff0c;我们可能会面临这样的问题&#xff1a;要实现比较两个数的大小。明明比较两个数的方法都一样…...

什么是“奥卡姆剃刀”,如何用“奥卡姆剃刀”解决复杂问题?复杂问题简单化

什么是“奥卡姆剃刀”&#xff0c;如何用“奥卡姆剃刀”解决复杂问题&#xff1f;复杂问题简单化问题什么是“奥卡姆剃刀”?如何使用“奥卡姆剃刀”解决问题复杂问题简单化“汉隆剃刀”小结问题 假设你在夜空中看到一颗闪闪发光的「不明飞行物」&#xff0c;你认为这会是什么呢…...

角谷定理(递归)

已知有角谷定理&#xff1a; 输入一个自然数&#xff0c;若为偶数&#xff0c;则把它除以2&#xff0c;若为奇数&#xff0c;则把它乘以3加1。经过如此有限次运算后&#xff0c;总可以得到自然数值1。求经过多少次可得到自然数1。如&#xff1a;例如数据22的变化过程&#xff…...

数学小课堂:微积分复盘(高等数学本质上是对趋势的动态描述,是对各种相关性抽象的表述。)

文章目录 引言I 复盘1.1 概念和表述1.2 现实与虚构1.3 有穷和无穷1.4 静态和动态1.5 直觉和逻辑II 通过数学逻辑,理解人生。2.1 精明与聪明2.2 朋友和理性的对手2.3 攒钱和赚钱2.4 荣誉和财富引言 高等数学本质上是对趋势的动态描述,是对各种相关性抽象的表述。 I 复盘 1.…...

JAVA线程池原理详解一

JAVA线程池原理详解一 一. 线程池的优点 线程是稀缺资源&#xff0c;使用线程池可以减少创建和销毁线程的次数&#xff0c;每个工作线程都可以重复使用。可以根据系统的承受能力&#xff0c;调整线程池中工作线程的数量&#xff0c;防止因为消耗过多内存导致服务器崩溃。 二…...

Windows平台Unity Camera场景实现轻量级RTSP服务和RTMP推送

技术背景随着VR技术在医疗、军事、农业、学校、景区、消防、公共安全、研学机构、展厅展馆&#xff0c;商场等场所普及&#xff0c;开发者对Unity平台下的直播体验提出了更高的要求。技术实现Unity平台下的RTMP推流、RTMP、RTSP播放前几年已经覆盖了Windows、Linux、Android、i…...

LSB 题解

今天来刷一道Misc的题目&#xff0c;LSB原理进行图片隐写 LSB原理 LSB是一种利用人类视觉的局限性设计的幻术 PNG和BMP图片中的图像像素一般是由RGB(RED红 GREEN绿 BLUE蓝)三原色组成 记住&#xff0c;JPG图片是不适合使用LSB隐写的&#xff0c;JPG图片对像数进行了有损压缩…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比

在机器学习的回归分析中&#xff0c;损失函数的选择对模型性能具有决定性影响。均方误差&#xff08;MSE&#xff09;作为经典的损失函数&#xff0c;在处理干净数据时表现优异&#xff0c;但在面对包含异常值的噪声数据时&#xff0c;其对大误差的二次惩罚机制往往导致模型参数…...

Vue ③-生命周期 || 脚手架

生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09; 什么时候可以开始操作dom&#xff1f;&#xff08;至少dom得渲染出来&#xff09; Vue生命周期&#xff1a; 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用

前言&#xff1a;我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM&#xff08;Java Virtual Machine&#xff09;让"一次编写&#xff0c;到处运行"成为可能。这个软件层面的虚拟化让我着迷&#xff0c;但直到后来接触VMware和Doc…...