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

Android 镜像模式和扩展模式区别探讨-Android14

Android 镜像模式和扩展模式区别探讨

  • 1、区分镜像模式和扩展模式
    • 1.1 扩展屏是否有显示内容
    • 1.2 镜像模式显示条件
  • 2、镜像模式界面

同屏显示和异屏显示探讨

DisplayManagerService启动及主屏添加-Android13
Android主副屏显示-Android14


1、区分镜像模式和扩展模式

LogicalDisplay.java#mHasContent    当前LogicalDisplay是否有内容显示
DisplayContent.java#mLastHasContent  当前DisplayContent是否有内容显示


Android14上默认扩展屏没有显示内容mHasContent=false,扩展屏显示的是主屏DEFAULT_DISPLAY镜像

1.1 扩展屏是否有显示内容

ActivityOptions副屏启动 有Activity启动到扩展屏上,就表示扩展屏上有显示内容,即 mHasContent=true

  • LogicalDisplay.java#mHasContent是由DisplayContent.java#mLastHasContent设置下去的,就是mTmpApplySurfaceChangesTransactionState.displayHasContent
  • RootWindowContainer.java界面刷新时,在mApplySurfaceChangesTransaction中同步,forAllWindows(mApplySurfaceChangesTransaction, true) 当扩展屏没有界面mChildren就会返回 false
  • RootWindowContainer.java#handleNotObscuredLocked 有界面时,界面对应 DisplayContent 是主屏isDefaultDisplay就为 displayHasContent = true; ,而扩展屏界面对应的 DisplayContent 判断主屏不是屏保和锁屏(!mObscureApplicationContentOnSecondaryDisplays)、或者 扩展屏是解锁状态(displayContent.isKeyguardAlwaysUnlocked(),默认无Display.FLAG_ALWAYS_UNLOCKED标志,是未解锁状态,该条件为false)、或者 界面覆盖且为TYPE_KEYGUARD_DIALOG类型((obscured && type == TYPE_KEYGUARD_DIALOG)),某个条件满足就为 displayHasContent = true;

frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java#applySurfaceChangesTransaction

mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
if (!inTransition() && !mDisplayRotation.isRotatingSeamlessly()) {mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,mLastHasContent,mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,mTmpApplySurfaceChangesTransactionState.preferredModeId,mTmpApplySurfaceChangesTransactionState.preferredMinRefreshRate,mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate,mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,mTmpApplySurfaceChangesTransactionState.disableHdrConversion,true /* inTraversal, must call performTraversalInTrans... below */);
}
private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;final boolean obscuredChanged = w.mObscured !=mTmpApplySurfaceChangesTransactionState.obscured;final RootWindowContainer root = mWmService.mRoot;// Update effect.w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured;if (!mTmpApplySurfaceChangesTransactionState.obscured) {final boolean isDisplayed = w.isDisplayed();if (isDisplayed && w.isObscuringDisplay()) {// This window completely covers everything behind it, so we want to leave all// of them as undimmed (for performance reasons).mObscuringWindow = w;mTmpApplySurfaceChangesTransactionState.obscured = true;}final boolean displayHasContent = root.handleNotObscuredLocked(w,mTmpApplySurfaceChangesTransactionState.obscured,mTmpApplySurfaceChangesTransactionState.syswin);if (!mTmpApplySurfaceChangesTransactionState.displayHasContent&& !getDisplayPolicy().isWindowExcludedFromContent(w)) {mTmpApplySurfaceChangesTransactionState.displayHasContent |= displayHasContent;}if (w.mHasSurface && isDisplayed) {if ((w.mAttrs.flags & FLAG_KEEP_SCREEN_ON) != 0) {mTmpHoldScreenWindow = w;} else if (w == mLastWakeLockHoldingWindow) {ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON,"handleNotObscuredLocked: %s was holding screen wakelock but no longer "+ "has FLAG_KEEP_SCREEN_ON!!! called by%s",w, Debug.getCallers(10));}final int type = w.mAttrs.type;if (type == TYPE_SYSTEM_DIALOG|| type == TYPE_SYSTEM_ERROR|| (type == TYPE_NOTIFICATION_SHADE&&  mWmService.mPolicy.isKeyguardShowing())) {mTmpApplySurfaceChangesTransactionState.syswin = true;}if (mTmpApplySurfaceChangesTransactionState.preferredRefreshRate == 0&& w.mAttrs.preferredRefreshRate != 0) {mTmpApplySurfaceChangesTransactionState.preferredRefreshRate= w.mAttrs.preferredRefreshRate;}mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing|= w.mAttrs.preferMinimalPostProcessing;mTmpApplySurfaceChangesTransactionState.disableHdrConversion|= !(w.mAttrs.isHdrConversionEnabled());final int preferredModeId = getDisplayPolicy().getRefreshRatePolicy().getPreferredModeId(w);if (w.getWindowingMode() != WINDOWING_MODE_PINNED&& mTmpApplySurfaceChangesTransactionState.preferredModeId == 0&& preferredModeId != 0) {mTmpApplySurfaceChangesTransactionState.preferredModeId = preferredModeId;}final float preferredMinRefreshRate = getDisplayPolicy().getRefreshRatePolicy().getPreferredMinRefreshRate(w);if (mTmpApplySurfaceChangesTransactionState.preferredMinRefreshRate == 0&& preferredMinRefreshRate != 0) {mTmpApplySurfaceChangesTransactionState.preferredMinRefreshRate =preferredMinRefreshRate;}final float preferredMaxRefreshRate = getDisplayPolicy().getRefreshRatePolicy().getPreferredMaxRefreshRate(w);if (mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate == 0&& preferredMaxRefreshRate != 0) {mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate =preferredMaxRefreshRate;}}}if (obscuredChanged && w.isVisible() && mWallpaperController.isWallpaperTarget(w)) {// This is the wallpaper target and its obscured state changed... make sure the// current wallpaper's visibility has been updated accordingly.mWallpaperController.updateWallpaperVisibility();}w.handleWindowMovedIfNeeded();final WindowStateAnimator winAnimator = w.mWinAnimator;//Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");w.resetContentChanged();// Moved from updateWindowsAndWallpaperLocked().if (w.mHasSurface) {// Take care of the window being ready to display.final boolean committed = winAnimator.commitFinishDrawingLocked();if (isDefaultDisplay && committed) {if (w.hasWallpaper()) {ProtoLog.v(WM_DEBUG_WALLPAPER,"First draw done in potential wallpaper target %s", w);mWallpaperMayChange = true;pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;if (DEBUG_LAYOUT_REPEATS) {surfacePlacer.debugLayoutRepeats("wallpaper and commitFinishDrawingLocked true",pendingLayoutChanges);}}}}final ActivityRecord activity = w.mActivityRecord;if (activity != null && activity.isVisibleRequested()) {activity.updateLetterboxSurface(w);final boolean updateAllDrawn = activity.updateDrawnWindowStates(w);if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(activity)) {mTmpUpdateAllDrawn.add(activity);}}w.updateResizingWindowIfNeeded();
};

frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java#handleNotObscuredLocked

boolean handleNotObscuredLocked(WindowState w, boolean obscured, boolean syswin) {final WindowManager.LayoutParams attrs = w.mAttrs;final int attrFlags = attrs.flags;final boolean onScreen = w.isOnScreen();final boolean canBeSeen = w.isDisplayed();final int privateflags = attrs.privateFlags;boolean displayHasContent = false;ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON,"handleNotObscuredLocked w: %s, w.mHasSurface: %b, w.isOnScreen(): %b, w"+ ".isDisplayedLw(): %b, w.mAttrs.userActivityTimeout: %d",w, w.mHasSurface, onScreen, w.isDisplayed(), w.mAttrs.userActivityTimeout);if (w.mHasSurface && onScreen) {if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {mUserActivityTimeout = w.mAttrs.userActivityTimeout;ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON, "mUserActivityTimeout set to %d",mUserActivityTimeout);}}if (w.mHasSurface && canBeSeen) {if (!syswin && w.mAttrs.screenBrightness >= 0&& Float.isNaN(mScreenBrightnessOverride)) {mScreenBrightnessOverride = w.mAttrs.screenBrightness;}final int type = attrs.type;// This function assumes that the contents of the default display are processed first// before secondary displays.final DisplayContent displayContent = w.getDisplayContent();if (displayContent != null && displayContent.isDefaultDisplay) {// While a dream or keyguard is showing, obscure ordinary application content on// secondary displays (by forcibly enabling mirroring unless there is other content// we want to show) but still allow opaque keyguard dialogs to be shown.if (w.isDreamWindow() || mWmService.mPolicy.isKeyguardShowing()) {mObscureApplicationContentOnSecondaryDisplays = true;}displayHasContent = true;} else if (displayContent != null &&(!mObscureApplicationContentOnSecondaryDisplays|| displayContent.isKeyguardAlwaysUnlocked()|| (obscured && type == TYPE_KEYGUARD_DIALOG))) {// Allow full screen keyguard presentation dialogs to be seen, or simply ignore the// keyguard if this display is always unlocked.displayHasContent = true;}if ((privateflags & PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE) != 0) {mSustainedPerformanceModeCurrent = true;}}return displayHasContent;
}

1.2 镜像模式显示条件

Android14上默认扩展屏没有显示内容mHasContent=false,扩展屏显示的是主屏DEFAULT_DISPLAY镜像。其实还有两个条件。

  1. 扩展屏信息 getDisplayDeviceInfoLocked 的flag没有DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY
  2. 扩展屏是否可以显示镜像 displayDevice.isWindowManagerMirroringLocked() ,如果为 false,则SurfaceFlinger不会在此显示器上执行层镜像,该方法目前Android14返回就是false固定值。
  3. mContentRecorder.updateRecording()更新镜像时,如果扩展屏有内容 mDisplayContent.getLastHasContent() 或者扩展屏时关闭状态 mDisplayContent.getDisplayInfo().state == Display.STATE_OFF ,就会停止镜像pauseRecording()

frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

void updateRecording() {if (mContentRecorder == null || !mContentRecorder.isContentRecordingSessionSet()) {if (!setDisplayMirroring()) {return;}}mContentRecorder.updateRecording();
}boolean setDisplayMirroring() {int mirrorDisplayId = mWmService.mDisplayManagerInternal.getDisplayIdToMirror(mDisplayId);if (mirrorDisplayId == INVALID_DISPLAY) {return false;}if (mirrorDisplayId == mDisplayId) {if (mDisplayId != DEFAULT_DISPLAY) {ProtoLog.w(WM_DEBUG_CONTENT_RECORDING,"Content Recording: Attempting to mirror self on %d", mirrorDisplayId);}return false;}// This is very unlikely, and probably impossible, but if the current display is// DEFAULT_DISPLAY and the displayId to mirror results in an invalid display, we don't want// to mirror the DEFAULT_DISPLAY so instead we just returnDisplayContent mirrorDc = mRootWindowContainer.getDisplayContentOrCreate(mirrorDisplayId);if (mirrorDc == null && mDisplayId == DEFAULT_DISPLAY) {ProtoLog.w(WM_DEBUG_CONTENT_RECORDING,"Content Recording: Found no matching mirror display for id=%d for "+ "DEFAULT_DISPLAY. Nothing to mirror.",mirrorDisplayId);return false;}if (mirrorDc == null) {mirrorDc = mRootWindowContainer.getDefaultDisplay();ProtoLog.w(WM_DEBUG_CONTENT_RECORDING,"Content Recording: Attempting to mirror %d from %d but no DisplayContent "+ "associated. Changing to mirror default display.",mirrorDisplayId, mDisplayId);}// Create a session for mirroring the display content to this virtual display.ContentRecordingSession session = ContentRecordingSession.createDisplaySession(mirrorDc.getDisplayId()).setVirtualDisplayId(mDisplayId);setContentRecordingSession(session);ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,"Content Recording: Successfully created a ContentRecordingSession for "+ "displayId=%d to mirror content from displayId=%d",mDisplayId, mirrorDisplayId);return true;
}

frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java

public int getDisplayIdToMirror(int displayId) {synchronized (mSyncRoot) {final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);if (display == null) {return Display.INVALID_DISPLAY;}final DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();final boolean ownContent = (displayDevice.getDisplayDeviceInfoLocked().flags& DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;// If the display has enabled mirroring, but specified that it will be managed by// WindowManager, return an invalid display id. This is to ensure we don't// accidentally select the display id to mirror based on DM logic and instead allow// the caller to specify what area to mirror.if (ownContent || displayDevice.isWindowManagerMirroringLocked()) {return Display.INVALID_DISPLAY;}int displayIdToMirror = displayDevice.getDisplayIdToMirrorLocked();LogicalDisplay displayToMirror = mLogicalDisplayMapper.getDisplayLocked(displayIdToMirror);// If the displayId for the requested mirror doesn't exist, fallback to mirroring// default display.if (displayToMirror == null) {displayIdToMirror = Display.DEFAULT_DISPLAY;}return displayIdToMirror;}
}

frameworks/base/services/core/java/com/android/server/wm/ContentRecorder.java

/*** Start recording if this DisplayContent no longer has content. Pause recording if it now* has content or the display is not on.*/
@VisibleForTesting void updateRecording() {if (isCurrentlyRecording() && (mDisplayContent.getLastHasContent()|| mDisplayContent.getDisplayInfo().state == Display.STATE_OFF)) {pauseRecording();} else {// Display no longer has content, or now has a surface to write to, so try to start// recording.startRecordingIfNeeded();}
}

2、镜像模式界面

  • 创建镜像SurfaceControlmRecordedSurface = SurfaceControl.mirrorSurface(mRecordedWindowContainer.getSurfaceControl())
  • 创建镜像SurfaceControl对应的TransactionSurfaceControl.Transaction transaction = mDisplayContent.mWmService.mTransactionFactory.get().reparent(mRecordedSurface, mDisplayContent.getSurfaceControl()).reparent(mDisplayContent.getWindowingLayer(), null).reparent(mDisplayContent.getOverlayLayer(), null);
  • 根据主屏和扩展屏大小处理:mRecordedWindowContainer.getBounds(), surfaceSize, updateMirroredSurface

frameworks/base/services/core/java/com/android/server/wm/ContentRecorder.java

/*** Start recording to this DisplayContent if it does not have its own content. Captures the* content of a WindowContainer indicated by a WindowToken. If unable to start recording, falls* back to original MediaProjection approach.*/
private void startRecordingIfNeeded() {// Only record if this display does not have its own content, is not recording already,// and if this display is on (it has a surface to write output to).if (mDisplayContent.getLastHasContent() || isCurrentlyRecording()|| mDisplayContent.getDisplayInfo().state == Display.STATE_OFF|| mContentRecordingSession == null) {return;}if (mContentRecordingSession.isWaitingForConsent()) {ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Content Recording: waiting to record, so do "+ "nothing");return;}mRecordedWindowContainer = retrieveRecordedWindowContainer();if (mRecordedWindowContainer == null) {// Either the token is missing, or the window associated with the token is missing.// Error has already been handled, so just leave.return;}final Point surfaceSize = fetchSurfaceSizeIfPresent();if (surfaceSize == null) {ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,"Content Recording: Unable to start recording for display %d since the "+ "surface is not available.",mDisplayContent.getDisplayId());return;}ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,"Content Recording: Display %d has no content and is on, so start recording for "+ "state %d",mDisplayContent.getDisplayId(), mDisplayContent.getDisplayInfo().state);// TODO(b/274790702): Do not start recording if waiting for consent - for now,//  go ahead.// Create a mirrored hierarchy for the SurfaceControl of the DisplayArea to capture.mRecordedSurface = SurfaceControl.mirrorSurface(mRecordedWindowContainer.getSurfaceControl());SurfaceControl.Transaction transaction =mDisplayContent.mWmService.mTransactionFactory.get()// Set the mMirroredSurface's parent to the root SurfaceControl for this// DisplayContent. This brings the new mirrored hierarchy under this// DisplayContent,// so SurfaceControl will write the layers of this hierarchy to the// output surface// provided by the app..reparent(mRecordedSurface, mDisplayContent.getSurfaceControl())// Reparent the SurfaceControl of this DisplayContent to null, to prevent// content// being added to it. This ensures that no app launched explicitly on the// VirtualDisplay will show up as part of the mirrored content..reparent(mDisplayContent.getWindowingLayer(), null).reparent(mDisplayContent.getOverlayLayer(), null);// Retrieve the size of the DisplayArea to mirror.updateMirroredSurface(transaction, mRecordedWindowContainer.getBounds(), surfaceSize);// Notify the client about the visibility of the mirrored region, now that we have begun// capture.if (mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK) {mMediaProjectionManager.notifyActiveProjectionCapturedContentVisibilityChanged(mRecordedWindowContainer.asTask().isVisibleRequested());} else {int currentDisplayState =mRecordedWindowContainer.asDisplayContent().getDisplayInfo().state;mMediaProjectionManager.notifyActiveProjectionCapturedContentVisibilityChanged(currentDisplayState != DISPLAY_STATE_OFF);}// No need to clean up. In SurfaceFlinger, parents hold references to their children. The// mirrored SurfaceControl is alive since the parent DisplayContent SurfaceControl is// holding a reference to it. Therefore, the mirrored SurfaceControl will be cleaned up// when the VirtualDisplay is destroyed - which will clean up this DisplayContent.
}
/*** Apply transformations to the mirrored surface to ensure the captured contents are scaled to* fit and centred in the output surface.** @param transaction           the transaction to include transformations of mMirroredSurface*                              to. Transaction is not applied before returning.* @param recordedContentBounds bounds of the content to record to the surface provided by*                              the app.* @param surfaceSize           the default size of the surface to write the display area*                              content to*/@VisibleForTesting void updateMirroredSurface(SurfaceControl.Transaction transaction,Rect recordedContentBounds, Point surfaceSize) {// Calculate the scale to apply to the root mirror SurfaceControl to fit the size of the// output surface.float scaleX = surfaceSize.x / (float) recordedContentBounds.width();float scaleY = surfaceSize.y / (float) recordedContentBounds.height();float scale = Math.min(scaleX, scaleY);int scaledWidth = Math.round(scale * (float) recordedContentBounds.width());int scaledHeight = Math.round(scale * (float) recordedContentBounds.height());// Calculate the shift to apply to the root mirror SurfaceControl to centre the mirrored// contents in the output surface.int shiftedX = 0;if (scaledWidth != surfaceSize.x) {shiftedX = (surfaceSize.x - scaledWidth) / 2;}int shiftedY = 0;if (scaledHeight != surfaceSize.y) {shiftedY = (surfaceSize.y - scaledHeight) / 2;}transaction// Crop the area to capture to exclude the 'extra' wallpaper that is used// for parallax (b/189930234)..setWindowCrop(mRecordedSurface, recordedContentBounds.width(),recordedContentBounds.height())// Scale the root mirror SurfaceControl, based upon the size difference between the// source (DisplayArea to capture) and output (surface the app reads images from)..setMatrix(mRecordedSurface, scale, 0 /* dtdx */, 0 /* dtdy */, scale)// Position needs to be updated when the mirrored DisplayArea has changed, since// the content will no longer be centered in the output surface..setPosition(mRecordedSurface, shiftedX /* x */, shiftedY /* y */).apply();mLastRecordedBounds = new Rect(recordedContentBounds);// Request to notify the client about the resize.mMediaProjectionManager.notifyActiveProjectionCapturedContentResized(mLastRecordedBounds.width(), mLastRecordedBounds.height());}

相关文章:

Android 镜像模式和扩展模式区别探讨-Android14

Android 镜像模式和扩展模式区别探讨 1、区分镜像模式和扩展模式1.1 扩展屏是否有显示内容1.2 镜像模式显示条件 2、镜像模式界面 同屏显示和异屏显示探讨DisplayManagerService启动及主屏添加-Android13 Android主副屏显示-Android14 1、区分镜像模式和扩展模式 LogicalDispla…...

深度学习笔记之BERT(五)TinyBERT

深度学习笔记之TinyBERT 引言回顾&#xff1a;DistilBERT模型TinyBERT模型结构TinyBERT模型策略Transformer层蒸馏嵌入层蒸馏预测层蒸馏 TinyBERT模型的训练效果展示 引言 上一节介绍了 DistilBERT \text{DistilBERT} DistilBERT模型&#xff0c;本节将继续介绍优化性更强的知…...

【时间序列预测】基于PyTorch实现CNN_BiLSTM算法

文章目录 1. CNN与BiLSTM2. 完整代码实现3. 代码结构解读3.1 CNN Layer3.2 BiLSTM Layer3.3 Output Layer3.4 forward Layer 4. 应用场景5. 总结 本文将详细介绍如何使用Pytorch实现一个结合卷积神经网络&#xff08;CNN&#xff09;和双向长短期记忆网络&#xff08;BiLSTM&am…...

联想Y7000 2024版本笔记本 RTX4060安装ubuntu22.04双系统及深度学习环境配置

目录 1..制作启动盘 2.Windows 磁盘分区,删除原来ubuntu的启动项 3.四个设置 4.安装ubuntu 5.ubuntu系统配置 1..制作启动盘 先下载镜像文件,注意版本对应。Rufus - 轻松创建 USB 启动盘 用rufus制作时,需要注意选择正确的分区类型和系统类型。不然安装的系统会有问题…...

VuePress学习

1.介绍 VuePress 由两部分组成&#xff1a;第一部分是一个极简静态网站生成器 (opens new window)&#xff0c;它包含由 Vue 驱动的主题系统和插件 API&#xff0c;另一个部分是为书写技术文档而优化的默认主题&#xff0c;它的诞生初衷是为了支持 Vue 及其子项目的文档需求。…...

一次“okhttp访问间隔60秒,提示unexpected end of stream“的问题排查过程

一、现象 okhttp调用某个服务&#xff0c;如果第二次访问间隔上一次访问时间超过60s&#xff0c;返回错误&#xff1a;"unexpected end of stream"。 二、最终定位原因&#xff1a; 空闲连接如果超过60秒&#xff0c;服务端会主动关闭连接。此时客户端恰巧访问了这…...

SQL最佳实践:避免使用COUNT=0

如果你遇到类似下面的 SQL 查询&#xff1a; SELECT * FROM customer c WHERE 0 (SELECT COUNT(*)FROM orders oWHERE o.customer_id c.customer_id);意味着有人没有遵循 SQL 最佳实践。该语句的作用是查找没有下过订单的客户&#xff0c;其中子查询使用了 COUNT 函数统计客…...

PG与ORACLE的差距

首先必须是XID 64&#xff0c;一个在极端环境下会FREEZE的数据库无论如何都无法承担关键业务系统的重任的&#xff0c;我们可以通过各种配置&#xff0c;提升硬件的性能&#xff0c;通过各种IT管控措施来尽可能避免在核心系统上面临FREEZE的风险&#xff0c;不过并不是每个企业…...

树莓派3B+驱动开发(2)- LED驱动(传统模式)

github主页&#xff1a;https://github.com/snqx-lqh 本项目github地址&#xff1a;https://github.com/snqx-lqh/RaspberryPiDriver 本项目硬件地址&#xff1a;https://oshwhub.com/from_zero/shu-mei-pai-kuo-zhan-ban 欢迎交流 笔记说明 如我在驱动开发总览中说的那样&…...

超详细搭建PhpStorm+PhpStudy开发环境

刚开始接触PHP开发&#xff0c;搭建开发环境是第一步&#xff0c;网上下载PhpStorm和PhpStudy软件&#xff0c;怎样安装和激活就不详细说了&#xff0c;我们重点来看一看怎样搭配这两个开发环境。 前提&#xff1a;现在假设你已经安装完PhpStorm和PhpStudy软件。 我的PhpStor…...

分析比对vuex和store模式

在 Vue 中&#xff0c;Vuex 和 store 模式 是两个不同的概念&#xff0c;它们紧密相关&#xff0c;主要用于管理应用的状态。下面我会详细介绍这两个概念&#xff0c;并通过例子帮助你更好地理解。 1. Vuex 是什么&#xff1f; Vuex 是 Vue.js 的一个状态管理库&#xff0c;用…...

C# 网络编程--基础核心内容

在现今软件开发中&#xff0c;网络编程是非常重要的一部分&#xff0c;本文简要介绍下网络编程的概念和实践。 C#网络编程的主要内容包括以下几个方面‌&#xff1a; : 上图引用大佬的图&#xff0c;大家也关注一下&#xff0c;有技术有品质&#xff0c;有国有家&#xff0c;情…...

【C++游戏程序】easyX图形库还原游戏《贪吃蛇大作战》(三)

承接上一篇文章&#xff1a;【C游戏程序】easyX图形库还原游戏《贪吃蛇大作战》&#xff08;二&#xff09;&#xff0c;我们这次来补充一些游戏细节&#xff0c;以及增加吃食物加长角色长度等设定玩法&#xff0c;也是本游戏的最后一篇文章。 一.玩家边界检测 首先是用来检测…...

uni-app H5端使用注意事项 【跨端开发系列】

&#x1f517; uniapp 跨端开发系列文章&#xff1a;&#x1f380;&#x1f380;&#x1f380; uni-app 组成和跨端原理 【跨端开发系列】 uni-app 各端差异注意事项 【跨端开发系列】uni-app 离线本地存储方案 【跨端开发系列】uni-app UI库、框架、组件选型指南 【跨端开…...

SpringBoot中的@Configuration注解

在Spring Boot中&#xff0c;Configuration注解扮演着非常重要的角色&#xff0c;它是Spring框架中用于定义配置类的一个核心注解。以下是Configuration注解的主要作用&#xff1a; 定义配置类&#xff1a; 使用Configuration注解的类表示这是一个配置类&#xff0c;Spring容器…...

十二、路由、生命周期函数

router路由 页面路由指的是在应用程序中实现不同页面之间的跳转,以及数据传递。通过 Router 模块就可以实现这个功能 2.1创建页面 之前是创建的文件,使用路由的时候需要创建页面,步骤略有不同 方法 1:直接右键新建Page(常用)方法 2:单独添加页面并配置2.1.1直接右键新建…...

【蓝桥杯每日一题】X 进制减法

X 进制减法 2024-12-6 蓝桥杯每日一题 X 进制减法 贪心 进制转换 题目大意 进制规定了数字在数位上逢几进一。 XX 进制是一种很神奇的进制, 因为其每一数位的进制并不固定&#xff01;例如说某 种 XX 进制数, 最低数位为二进制, 第二数位为十进制, 第三数位为八进制, 则 XX 进制…...

《蓝桥杯比赛规划》

大家好啊&#xff01;我是NiJiMingCheng 我的博客&#xff1a;NiJiMingCheng 这节课我们来分享蓝桥杯比赛规划&#xff0c;好的规划会给我们的学习带来良好的收益&#xff0c;废话少说接下来就让我们进入学习规划吧&#xff0c;加油哦&#xff01;&#xff01;&#xff01; 一、…...

C++算法练习day70——53.最大子序和

题目来源&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题目思路分析 题目&#xff1a;寻找最大子数组和&#xff08;也称为最大子序和&#xff09;。 给定一个整数数组 nums&#xff0c;找到一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#x…...

import是如何“占领满屏“

import是如何“占领满屏“的&#xff1f; 《拒绝使用模块重导&#xff08;Re-export&#xff09;》 模块重导是一种通用的技术。在腾讯、字节、阿里等各大厂的组件库中都有大量使用。 如&#xff1a;字节的arco-design组件库中的组件&#xff1a;github.com/arco-design… …...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

微服务通信安全:深入解析mTLS的原理与实践

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言&#xff1a;微服务时代的通信安全挑战 随着云原生和微服务架构的普及&#xff0c;服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...

自然语言处理——文本分类

文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益&#xff08;IG&#xff09; 分类器设计贝叶斯理论&#xff1a;线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别&#xff0c; 有单标签多类别文本分类和多…...

高效的后台管理系统——可进行二次开发

随着互联网技术的迅猛发展&#xff0c;企业的数字化管理变得愈加重要。后台管理系统作为数据存储与业务管理的核心&#xff0c;成为了现代企业不可或缺的一部分。今天我们要介绍的是一款名为 若依后台管理框架 的系统&#xff0c;它不仅支持跨平台应用&#xff0c;还能提供丰富…...

Redis上篇--知识点总结

Redis上篇–解析 本文大部分知识整理自网上&#xff0c;在正文结束后都会附上参考地址。如果想要深入或者详细学习可以通过文末链接跳转学习。 1. 基本介绍 Redis 是一个开源的、高性能的 内存键值数据库&#xff0c;Redis 的键值对中的 key 就是字符串对象&#xff0c;而 val…...