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.mzph.cn/bicheng/54790.shtml

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

相关文章

通过docker启动ElasticSearch后为ElasticSearch设置用户和密码

文章目录 0. 前言1. 没有设置用户名和密码的情况2. 为ElasticSearch设置用户名和密码2.1 进入 ElasticSearch 容器内部2.2 修改 ElasticSearch 的配置文件2.3 设置用户名和密码 3. 在 kibana 容器中指定访问 ElasticSearch 的用户名和密码4. 设置用户名和密码后的情况4.1 访问 …

[51单片机] 简单介绍 (一)

文章目录 1.单片机介绍2.单片机内部三大资源3.单片机最小系统4.STC89C52RC单片机 1.单片机介绍 兼容Intel的MCS-51体系架构的一系列单片机。 STC89C52&#xff1a;8K FLASH、512字节RAM、32个IO口、3个定时器、1个UART、8个中断源。 单片机简称MCU单片机内部集成了CPU、RAM、…

Maxim(美信)—MAX20079AATP/VY PMIC芯片详解

写在前面 本系列文章主要讲解Maxim&#xff08;美信&#xff09;—MAX20079AATP/VY PMIC芯片的相关知识&#xff0c;希望能帮助更多的同学认识和了解MAX20079AATP/VY芯片。 若有相关问题&#xff0c;欢迎评论沟通&#xff0c;共同进步。(*^▽^*) PMIC是Power Management Int…

CC面试准备

半导体基础 半导体是介于导体和绝缘体之间的一种介质&#xff0c;在不同条件下表现出不同的导电性或者不导电特性&#xff0c; 电子半导体器件材料大部分为硅&#xff0c;锗等元素 本征半导体&#xff1a;完全不含杂质的纯净半导体&#xff0c;因为不含杂质&#xff0c;其中…

QT widgets 窗口缩放,自适应窗口大小进行布局

1. 窗口布局 2. 尺寸策略&#xff1a;扩展 Fixed (固定): 行为&#xff1a;控件的大小是固定的&#xff0c;不会随着窗口大小的变化而改变。它的大小由控件的 sizeHint() 返回的值决定。 适用场景&#xff1a;当你希望控件的大小保持不变&#xff0c;不随布局调整时使用&#x…

RAG+Agent人工智能平台:RAGflow实现GraphRA知识库问答,打造极致多模态问答与AI编排流体验

1.RAGflow简介 全面优化的 RAG 工作流可以支持从个人应用乃至超大型企业的各类生态系统。大语言模型 LLM 以及向量模型均支持配置。基于多路召回、融合重排序。提供易用的 API&#xff0c;可以轻松集成到各类企业系统。支持丰富的文件类型&#xff0c;包括 Word 文档、PPT、exc…

前端报错401 【已解决】

前端报错401 【已解决】 在前端开发中&#xff0c;HTTP状态码401&#xff08;Unauthorized&#xff09;是一个常见的错误&#xff0c;它表明用户试图访问受保护的资源&#xff0c;但未能提供有效的身份验证信息。这个错误不仅关乎用户体验&#xff0c;也直接关系到应用的安全性…

Uniapp时间戳转时间显示/时间格式

使用uview2 time 时间格式 | uView 2.0 - 全面兼容 nvue 的 uni-app 生态框架 - uni-app UI 框架 <text class"cell-tit clamp1">{{item.create_time}} --- {{ $u.timeFormat(item.create_time, yyyy-mm-dd hh:MM:ss)}} </text>

OpenAI发布多语言MMMLU数据集;火山引擎发布AI视频生成大模型豆包

&#x1f989; AI新闻 &#x1f680; OpenAI发布多语言MMMLU数据集 摘要&#xff1a;OpenAI在Hugging Face上推出了多语言大规模多任务语言理解&#xff08;MMMLU&#xff09;数据集&#xff0c;旨在评估大型语言模型在各种语言和任务中的表现。该数据集涵盖广泛的主题与学科…

记某学校小程序漏洞挖掘

前言&#xff1a; 遇到一个学校小程序的站点&#xff0c;只在前端登录口做了校验&#xff0c;后端没有任何校验&#xff0c;奇葩弱口令离谱进去&#xff0c;站点里面越权泄露敏感信息&#xff0c;接管账号等漏洞&#xff01;&#xff01;&#xff01; 渗透思路 1.绕过前端 …

代码随想录算法训练营Day14 | 226.翻转二叉树、101. 对称二叉树、104.二叉树的最大深度、111.二叉树的最小深度

目录 226.翻转二叉树 101. 对称二叉树 104.二叉树的最大深度 111.二叉树的最小深度 226.翻转二叉树 题目 226. 翻转二叉树 - 力扣&#xff08;LeetCode&#xff09; 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例1&#…

[网络] 网络层--IP协议

目录 一、IP协议 1.1 基本概念 1.2 IP协议报头 1.3 如何将报头和有效载荷分离和分用 1.4 分片与组装 1.5 如何减少分片&#xff1f; 1.6 分片和封装的具体过程 二、网段划分 2.1 再次理解IP地址 2.2 了解DHCP 2.3 网络划分方案 2.4 为什么要进行网络划分 2.5 特殊的…

接口加解密及数据加解密

目录 一、 加解密方式介绍 1.1 Hash算法加密 1.2. 对称加密 1.3 非对称加密 二、 我们要讲什么&#xff1f; 三、 接口加解密 四、 数据加解密 一、 加解密方式介绍 所有的加密方式我们可以分为三类&#xff1a;对称加密、非对称加密、Hash算法加密。 算法内部的具体实现…

【html】基础(二)

本专栏内容为&#xff1a;前端专栏 记录学习前端&#xff0c;分为若干个子专栏&#xff0c;html js css vue等 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;js专栏 &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&#x1f69a; &am…

http增删改查四种请求方式操纵数据库

注意&#xff1a;在manage.py项目入口文件中的路由配置里&#xff0c;返回响应的 return语句后面的代码不会执行&#xff0c;所以路由配置中每个模块代码要想都执行&#xff0c;不能出现return 激活虚拟环境&#xff1a;venv(我的虚拟环境名称&#xff09;\Scripts\activate …

Unity3D 小案例 像素贪吃蛇 03 蛇的碰撞

Unity3D 小案例 像素贪吃蛇 第三期 蛇的碰撞&#xff08;完结&#xff09; 像素贪吃蛇 碰撞蛇身 当蛇头碰撞到蛇身时&#xff0c;游戏应该判定为失败。 找到蛇身预制体&#xff0c;添加 Body 标签和碰撞体&#xff0c;碰撞体的大小为 0.5&#xff0c;跟蛇头和蛇身的碰撞体范…

AlDente Pro for Mac电池健康保护工具

AlDente Pro for Mac 是一款适用于 Mac 的实用电池健康保护工具。以下是它的主要特点和优势&#xff1a; 软件下载地址 一、保护电池寿命的原理 锂离子和聚合物电池&#xff08;如 Mac 笔记本中的电池&#xff09;在 30% 到 80% 之间运行时使用寿命最长。始终将电池电量保持…

Windows 10 on ARM, version 22H2 (updated Aug 2024) ARM64 AArch64 中文版、英文版下载

Windows 10 on ARM, version 22H2 (updated Aug 2024) ARM64 AArch64 中文版、英文版下载 基于 ARM 的 Windows 10 请访问原文链接&#xff1a;https://sysin.org/blog/windows-10-arm/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;s…

【VUE3.0】动手做一套像素风的前端UI组件库---Message

目录 引言自己整一个UI设计稿代码编写1. 设计信息窗口基础样式2. 设置打开和关闭的方法3. 编写实例化组件的js文件4. 看下最终效果5. 组件完整代码6. 组件调用方式 总结 引言 本教程基于前端UI样式库 NES.css 的UI设计&#xff0c;自行研究复现。欢迎大家交流优化实现方法~ 此次…

《线性代数》学渣笔记

文章目录 1 行列式1.1 克拉默法则1.2 基本性质1.3 余子式 M i j M_{ij} Mij​1.4 代数余子式 A i j ( − 1 ) i j ⋅ M i j A_{ij} (-1)^{ij} \cdot M_{ij} Aij​(−1)ij⋅Mij​1.5 具体型行列式计算&#xff08;化为基本型&#xff09;1.5.1 主对角线行列式&#xff1a;主…