当前位置: 首页 > 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图片对像数进行了有损压缩…...

离线部署docker与镜像

离线部署docker与镜像 1.离线部署docker 1).在docker官网上下载&#xff0c;合适的安装文件 本次使用的是“docker-20.10.9.tgz ” 下载地址&#xff1a;https://download.docker.com/linux/static/stable/x86_64/ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下…...

Linux文件系统介绍(上)

使用 Linux 系统时&#xff0c;需要作出的决策之一就是为存储设备选用什么文件系统。大多数 Linux 发行版在安装时会非常贴心地提供默认的文件系统&#xff0c;大多数入门级用户想都不想就用了默认的那个。 使用默认文件系统未必就不好&#xff0c;但了解一下可用的选择有时也会…...

创建SpringBoot注意事项

作为一个java小白&#xff0c;你是否因为创建SpringBoot项目那些莫名其妙的错误搞得头皮发麻。不要慌张&#xff0c;这篇文章能帮你解决90%的问题【持续更新…】 本文结合创建SpringBoot项目的完整过程来讲 在idea中新建项目 虽然SpringBoot项目是由maven内核组成的&#xff0…...

2023年全国最新二级建造师精选真题及答案9

百分百题库提供二级建造师考试试题、二建考试预测题、二级建造师考试真题、二建证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 11.关于施工合同违约赔偿损失范围的说法&#xff0c;正确的是&#xff08;&#xff09;。 A.…...

解决MySQL的 Row size too large (> 8126).

&#x1f4e2;欢迎点赞 &#xff1a;&#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;&#x1f4e2;本文作者&#xff1a;由webmote 原创&#x1f4e2;作者格言&#xff1a;无尽的折腾后&#xff0c;终于又回到…...

最优传输问题和Sinkhorn

最优传输问题 假设有M堆土&#xff0c;每堆土的大小是ama_mam​&#xff0c;有N个坑&#xff0c;每个坑的大小是bnb_nbn​&#xff0c;把单位土从土堆m运送到坑n的代价是c(m,n)c(m,n)c(m,n)&#xff0c;如何找到一种运输方法填满坑&#xff0c;并且代价最小&#xff0c;这就是…...

Netty核心组件EventLoop源码解析

源码解析目标 分析最核心组件EventLoop在Netty运行过程中所参与的事情&#xff0c;以及具体实现 源码解析 依然用netty包example下Echo目录下的案例代码&#xff0c;单我们写一个NettyServer时候&#xff0c;第一句话就是 EventLoopGroup bossGroup new NioEventLoopGroup(…...

排障命令-汇总

目录 日志查询 1. grep 2. zgrep cpu 1. top 内存 1. free tcp相关 1. netstat 2. ulimit 3. lsof jvm常用 1. jps 2. jinfo 3. jstack 4. jmap 5. jstat 进制转换 1. 十进制转16进制 日志查询 1. grep 定义&#xff1a;(global regular expression) 命令用于查…...

python+pytest接口自动化(4)-requests发送get请求

python中用于请求http接口的有自带的urllib和第三方库requests&#xff0c;但 urllib 写法稍微有点繁琐&#xff0c;所以在进行接口自动化测试过程中&#xff0c;一般使用更为简洁且功能强大的 requests 库。下面我们使用 requests 库发送get请求。requests库简介requests 库中…...

开源电子书工具Calibre 6.3 发布

Calibre 开源项目是 Calibre 官方出的电子书管理工具。它可以查看&#xff0c;转换&#xff0c;编辑和分类所有主流格式的电子书。Calibre 是个跨平台软件&#xff0c;可以在 Linux、Windows 和 macOS 上运行。Calibre 6.3 正式发布&#xff0c;此次更新内容如下&#xff1a;新…...

铁岭公司做网站/搜索词分析工具

今天内网里见到几个关于导师的问题&#xff0c;也想聊聊这个问题。 对于应届生来说&#xff0c;遇到一个好的导师是很不容易的&#xff0c;当然了&#xff0c;不同岗位不同角色&#xff0c;对好导师的理解是不一样的&#xff0c;分享一下我自己作为工程师&#xff0c;心中的好…...

外文网站建站/wordpress网站建设

五、常用配置 IntelliJ IDEA 有很多人性化的设置我们必须单独拿出来讲解&#xff0c;也因为这些人性化的设置让那些 IntelliJ IDEA 死忠粉更加死心塌地使用它和分享它。 进入设置界面&#xff1a; 目录结构如下&#xff1a; 1.Appearance & Behavior 1.1 设置主题 这里…...

武汉手机网站制作公司/宁波seo外包

C11 long long超长整形详解 C 11 标准中&#xff0c;基于整数大小的考虑&#xff0c;共提供了如表 1 所示的这些数据类型。与此同时&#xff0c;标准中还明确限定了各个数据类型最少占用的位数。 表 1 C11标准中所有的整形数据类型 整数类型等价类型C11标准规定占用最少位数…...

网站建设单位是什么意思/在线代理浏览网页

《计算机专业大学生英文简历》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《计算机专业大学生英文简历(8页珍藏版)》请在装配图网上搜索。1、计算机专业大学生英文简历计算机专业大学生英文简历ResumeCurrent Industry Computer Software Senior Software EngineerT…...

一个完美的网站怎么做/整合营销沟通

在使用TX2的过程中&#xff0c;我想要对一些代码进行调试&#xff0c;但是在Tx2上使用gdb进行调试&#xff0c;可视化效果比较差&#xff0c;于是想要使用嵌入式系统中的交叉编译方式。在主机上对Tx2进行刷机后&#xff0c;会自动在主机上安装Nsight软件。用这个软件可以对Tx2进…...

樟树网站开发/东莞网站seo公司哪家大

文章目录 目录 文章目录 前言 一、操作系统特征 二、操作系统的功能 三、广义指令就是系统调用指令 四、操作系统的发展与分类 五、操作系统的运行环境 六、408历年真题考点&#xff1a; 重点 前言 21级上岸研究生&#xff0c;笔记总结与分享&#xff0c;如有不足&am…...