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
镜像。其实还有两个条件。
- 扩展屏信息
getDisplayDeviceInfoLocked
的flag没有DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY
- 扩展屏是否可以显示镜像
displayDevice.isWindowManagerMirroringLocked()
,如果为false
,则SurfaceFlinger
不会在此显示器上执行层镜像,该方法目前Android14
返回就是false
固定值。- 当
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、镜像模式界面
- 创建镜像
SurfaceControl
:mRecordedSurface = SurfaceControl.mirrorSurface(mRecordedWindowContainer.getSurfaceControl())
- 创建镜像
SurfaceControl
对应的Transaction
:SurfaceControl.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 引言回顾:DistilBERT模型TinyBERT模型结构TinyBERT模型策略Transformer层蒸馏嵌入层蒸馏预测层蒸馏 TinyBERT模型的训练效果展示 引言 上一节介绍了 DistilBERT \text{DistilBERT} DistilBERT模型,本节将继续介绍优化性更强的知…...
【时间序列预测】基于PyTorch实现CNN_BiLSTM算法
文章目录 1. CNN与BiLSTM2. 完整代码实现3. 代码结构解读3.1 CNN Layer3.2 BiLSTM Layer3.3 Output Layer3.4 forward Layer 4. 应用场景5. 总结 本文将详细介绍如何使用Pytorch实现一个结合卷积神经网络(CNN)和双向长短期记忆网络(BiLSTM&am…...

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

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

一次“okhttp访问间隔60秒,提示unexpected end of stream“的问题排查过程
一、现象 okhttp调用某个服务,如果第二次访问间隔上一次访问时间超过60s,返回错误:"unexpected end of stream"。 二、最终定位原因: 空闲连接如果超过60秒,服务端会主动关闭连接。此时客户端恰巧访问了这…...
SQL最佳实践:避免使用COUNT=0
如果你遇到类似下面的 SQL 查询: SELECT * FROM customer c WHERE 0 (SELECT COUNT(*)FROM orders oWHERE o.customer_id c.customer_id);意味着有人没有遵循 SQL 最佳实践。该语句的作用是查找没有下过订单的客户,其中子查询使用了 COUNT 函数统计客…...
PG与ORACLE的差距
首先必须是XID 64,一个在极端环境下会FREEZE的数据库无论如何都无法承担关键业务系统的重任的,我们可以通过各种配置,提升硬件的性能,通过各种IT管控措施来尽可能避免在核心系统上面临FREEZE的风险,不过并不是每个企业…...
树莓派3B+驱动开发(2)- LED驱动(传统模式)
github主页:https://github.com/snqx-lqh 本项目github地址:https://github.com/snqx-lqh/RaspberryPiDriver 本项目硬件地址:https://oshwhub.com/from_zero/shu-mei-pai-kuo-zhan-ban 欢迎交流 笔记说明 如我在驱动开发总览中说的那样&…...
超详细搭建PhpStorm+PhpStudy开发环境
刚开始接触PHP开发,搭建开发环境是第一步,网上下载PhpStorm和PhpStudy软件,怎样安装和激活就不详细说了,我们重点来看一看怎样搭配这两个开发环境。 前提:现在假设你已经安装完PhpStorm和PhpStudy软件。 我的PhpStor…...
分析比对vuex和store模式
在 Vue 中,Vuex 和 store 模式 是两个不同的概念,它们紧密相关,主要用于管理应用的状态。下面我会详细介绍这两个概念,并通过例子帮助你更好地理解。 1. Vuex 是什么? Vuex 是 Vue.js 的一个状态管理库,用…...
C# 网络编程--基础核心内容
在现今软件开发中,网络编程是非常重要的一部分,本文简要介绍下网络编程的概念和实践。 C#网络编程的主要内容包括以下几个方面: : 上图引用大佬的图,大家也关注一下,有技术有品质,有国有家,情…...
【C++游戏程序】easyX图形库还原游戏《贪吃蛇大作战》(三)
承接上一篇文章:【C游戏程序】easyX图形库还原游戏《贪吃蛇大作战》(二),我们这次来补充一些游戏细节,以及增加吃食物加长角色长度等设定玩法,也是本游戏的最后一篇文章。 一.玩家边界检测 首先是用来检测…...

uni-app H5端使用注意事项 【跨端开发系列】
🔗 uniapp 跨端开发系列文章:🎀🎀🎀 uni-app 组成和跨端原理 【跨端开发系列】 uni-app 各端差异注意事项 【跨端开发系列】uni-app 离线本地存储方案 【跨端开发系列】uni-app UI库、框架、组件选型指南 【跨端开…...
SpringBoot中的@Configuration注解
在Spring Boot中,Configuration注解扮演着非常重要的角色,它是Spring框架中用于定义配置类的一个核心注解。以下是Configuration注解的主要作用: 定义配置类: 使用Configuration注解的类表示这是一个配置类,Spring容器…...

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

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

《蓝桥杯比赛规划》
大家好啊!我是NiJiMingCheng 我的博客:NiJiMingCheng 这节课我们来分享蓝桥杯比赛规划,好的规划会给我们的学习带来良好的收益,废话少说接下来就让我们进入学习规划吧,加油哦!!! 一、…...
C++算法练习day70——53.最大子序和
题目来源:. - 力扣(LeetCode) 题目思路分析 题目:寻找最大子数组和(也称为最大子序和)。 给定一个整数数组 nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素&#x…...

import是如何“占领满屏“
import是如何“占领满屏“的? 《拒绝使用模块重导(Re-export)》 模块重导是一种通用的技术。在腾讯、字节、阿里等各大厂的组件库中都有大量使用。 如:字节的arco-design组件库中的组件:github.com/arco-design… …...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...