【安卓12源码】WMS的作用及其启动流程

 一、WMS 的作用

WMS 在 Android 系统的地位,它作为中间层,连接了上层的 View 框架和下层的 SurfaceFingler。

  • WMS 主要职责
  • 窗口管理:负责启动、添加、删除窗口,管理窗口大小、层级,核心成员有:WindowContainer、RootWindowContainer、DisplayContent、TaskStack、Task、AppWindowToken、WindowState;
  • 窗口动画:由其子系统 WindowAnimator 管理;
  • 输入系统中转站:通过对窗口的触摸从而产生触摸事件,由 InputMethodService(IMS)对触摸事件进行处理,它会寻找一个最合适的窗口处理触摸反馈信息;
  • Surface 管理:为每个窗口分配一块 Surface,用于绘制要显示的内容。

  •  WMS重要的类

  • WMS继承于IWindowManager.Stub,作为Binder服务端
  • 成员变量mSessions保存所有的Session对象,Session继承于IWindowSession.Stub,作为Binder服务端.Session的创建在 WindowState 的attach方法中:mSession.windowAddedLocked
  • 成员变量mPolicy,实例是PhoneWindowManager,用于实现各种窗口相关的策略
  • 成员变量mWindowMap,保存所有的WindowState对象;以IBinder为key, 是IWindow的Bp端;
  • 每个窗口都对应一个WindowState对象, 该对象的成员变量mClient用于跟应用端交互,成员变量mToken用于跟AMS交互,WindowState的attach方法与SurfaceFlinger通信。
  • WindowManager与WindowManagerService通过Session进行通信,具体的实现由WMS处理。
  • WindowManager实现类是WindowManagerImpl ;
  • WindowManagerImpl 中成员变量 mParentWindow (Window),mGlobal (WindowManagerGlobal)

AMS,WMS之间数据是对应的,通过token值可以在AMS,WMS,应用程序之后来唯一确定一组Window,token是关联着一组窗口的,可能有多个WindowState的token值是相同的. 

二、WMS 的启动流程

wms 是运行在systemsever 系统进程的,在启动system  进程的时候,去初始化了 WMS

/frameworks/base/services/java/com/android/server/SystemServer.java

    private void startOtherServices(@NonNull TimingsTraceAndSlog t) {t.traceBegin("startOtherServices");WindowManagerService wm = null;t.traceBegin("StartInputManagerService");// wms与 InputManagerService 息息相关,创建 InputManagerService 对象inputManager = new InputManagerService(context);t.traceEnd();t.traceBegin("StartWindowManagerService");// WMS needs sensor service readymSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);// 1. 创建 WindowManagerService 对象wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);// 将 WindowManagerService 和 InputManagerService 添加到服务中ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);ServiceManager.addService(Context.INPUT_SERVICE, inputManager,/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);// 2. wms 与 ams 关联mActivityManagerService.setWindowManager(wm);// 3. 初始化完成wm.onInitReady();// 4. InputManagerService 设置回调,并启动 InputManagerService inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());inputManager.start();// 5. 调用 displayReady 方法wm.displayReady();// 6. 系统准备完毕,调用 systemReadywm.systemReady();final Configuration config = wm.computeNewConfiguration(DEFAULT_DISPLAY);

1. 创建 WindowManagerService 对象

WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);public static WindowManagerService main(final Context context, final InputManagerService im,final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,ActivityTaskManagerService atm) {// policy 是值是 new PhoneWindowManager(),继承了 WindowManagerPolicyreturn main(context, im, showBootMsgs, onlyCore, policy, atm,// 一个Supplier可以通过lambda表达式、方法引用或默认构造函数来实例化。
// Supplier在Java 8中被引入,属于java.util.function包
// transactionFactory 为 SurfaceControl.Transaction::new,创建 Transaction 对象
// 通过get 可以获取到对象 Transactionnew DisplayWindowSettingsProvider(), SurfaceControl.Transaction::new, Surface::new,SurfaceControl.Builder::new);}==========@VisibleForTestingpublic static WindowManagerService main(final Context context, final InputManagerService im,final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,ActivityTaskManagerService atm, DisplayWindowSettingsProviderdisplayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory,Supplier<Surface> surfaceFactory,Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {// 在 android.display 实例化 wms,所以wms 跑在android.display线程中 DisplayThread.getHandler().runWithScissors(() ->sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,atm, displayWindowSettingsProvider, transactionFactory, surfaceFactory,surfaceControlFactory), 0);return sInstance;}

PhoneWindowManager,主要是负责窗口管理的各种策略。在 android.display 实例化 wms,所以wms 跑在android.display线程中 

/frameworks/base/services/core/java/com/android/server/DisplayThread.java

// ServiceThread 继承了 HandlerThread public final class DisplayThread extends ServiceThread {private static DisplayThread sInstance;private static Handler sHandler;private DisplayThread() {// DisplayThread runs important stuff, but these are not as important as things running in// AnimationThread. Thus, set the priority to one lower.// 线程名字为 "android.display",优先级为 -3super("android.display", Process.THREAD_PRIORITY_DISPLAY + 1, false /*allowIo*/);}private static void ensureThreadLocked() {if (sInstance == null) {sInstance = new DisplayThread();sInstance.start();sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);// 创建handler,looper 为 thread "android.display" 的loopersHandler = new Handler(sInstance.getLooper());}}// 获取到handlerpublic static Handler getHandler() {synchronized (DisplayThread.class) {ensureThreadLocked();return sHandler;}}

handler 执行 runWithScissors 方法

/frameworks/base/core/java/android/os/Handler.java

    public final boolean runWithScissors(@NonNull Runnable r, long timeout) {if (r == null) {throw new IllegalArgumentException("runnable must not be null");}if (timeout < 0) {throw new IllegalArgumentException("timeout must be non-negative");}// 当前looper 不相等if (Looper.myLooper() == mLooper) {r.run();return true;}// 将其pos 到 BlockingRunnableBlockingRunnable br = new BlockingRunnable(r);return br.postAndWait(this, timeout);}===========public BlockingRunnable(Runnable task) {mTask = task;}@Overridepublic void run() {try {mTask.run();} finally {synchronized (this) {mDone = true;
// 执行完则唤醒系统主线程notifyAll();}}}public boolean postAndWait(Handler handler, long timeout) {
// 将其增加到消息队列中if (!handler.post(this)) {return false;}synchronized (this) {if (timeout > 0) {
。。} else {while (!mDone) {try {
// 阻塞systemserver 主线程wait();} catch (InterruptedException ex) {}}}}return true;}}

DisplayThread 给系统使用的共享单例前台线程类,线程名字为 android.display,专门提供给WindowManager, DisplayManager, and InputManager来执行快速响应的实时操作。

执行构造函数

    private WindowManagerService(Context context, InputManagerService inputManager,boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,ActivityTaskManagerService atm, DisplayWindowSettingsProviderdisplayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory,Supplier<Surface> surfaceFactory,Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {installLock(this, INDEX_WINDOW);// 锁机制与 atm 一致mGlobalLock = atm.getGlobalLock();
// 缓存 AtmSmAtmService = atm;mContext = context;
。。。
// 是否允许在低电量开启动画mAllowAnimationsInLowPowerMode = context.getResources().getBoolean(com.android.internal.R.bool.config_allowAnimationsInLowPowerMode);// 缓存 inputManagermInputManager = inputManager; // Must be before createDisplayContentLocked.mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);mSurfaceControlFactory = surfaceControlFactory;mTransactionFactory = transactionFactory;mSurfaceFactory = surfaceFactory;
// 获取 Transaction  对象mTransaction = mTransactionFactory.get();// policy 为:PhoneWindowManagermPolicy = policy;
// 创建 WindowAnimator 对象mAnimator = new WindowAnimator(this);
// 创建 RootWindowContainer 对象mRoot = new RootWindowContainer(this);final ContentResolver resolver = context.getContentResolver();mUseBLAST = Settings.Global.getInt(resolver,Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_VR, 1) == 1;mSyncEngine = new BLASTSyncEngine(this);mWindowPlacerLocked = new WindowSurfacePlacer(this);
// 任务快照控制类mTaskSnapshotController = new TaskSnapshotController(this);mWindowTracing = WindowTracing.createDefaultAndStartLooper(this,Choreographer.getInstance());LocalServices.addService(WindowManagerPolicy.class, mPolicy);mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);// mH 为  final H mH = new H();mKeyguardDisableHandler = KeyguardDisableHandler.create(mContext, mPolicy, mH);mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);// 注册低电量观察者if (mPowerManagerInternal != null) {mPowerManagerInternal.registerLowPowerModeObserver(new PowerManagerInternal.LowPowerModeListener() {@Overridepublic int getServiceType() {return ServiceType.ANIMATION;}@Overridepublic void onLowPowerModeChanged(PowerSaveState result) {synchronized (mGlobalLock) {final boolean enabled = result.batterySaverEnabled;if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) {mAnimationsDisabled = enabled;dispatchNewAnimatorScaleLocked(null);}}}});mAnimationsDisabled = mPowerManagerInternal.getLowPowerState(ServiceType.ANIMATION).batterySaverEnabled;}mScreenFrozenLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN");mScreenFrozenLock.setReferenceCounted(false);mDisplayNotificationController = new DisplayWindowListenerController(this);mActivityManager = ActivityManager.getService();mActivityTaskManager = ActivityTaskManager.getService();mAmInternal = LocalServices.getService(ActivityManagerInternal.class);mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);AppOpsManager.OnOpChangedInternalListener opListener =new AppOpsManager.OnOpChangedInternalListener() {@Override public void onOpChanged(int op, String packageName) {updateAppOpsState();}};mAppOps.startWatchingMode(OP_SYSTEM_ALERT_WINDOW, null, opListener);mAppOps.startWatchingMode(AppOpsManager.OP_TOAST_WINDOW, null, opListener);mPmInternal = LocalServices.getService(PackageManagerInternal.class);mTestUtilityService = LocalServices.getService(TestUtilityService.class);final IntentFilter suspendPackagesFilter = new IntentFilter();suspendPackagesFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);suspendPackagesFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);context.registerReceiverAsUser(new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {final String[] affectedPackages =intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);final boolean suspended =Intent.ACTION_PACKAGES_SUSPENDED.equals(intent.getAction());updateHiddenWhileSuspendedState(new ArraySet<>(Arrays.asList(affectedPackages)),suspended);}}, UserHandle.ALL, suspendPackagesFilter, null, null);// Get persisted window scale settingmWindowAnimationScaleSetting = Settings.Global.getFloat(resolver,Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting);mTransitionAnimationScaleSetting = Settings.Global.getFloat(resolver,Settings.Global.TRANSITION_ANIMATION_SCALE,context.getResources().getFloat(R.dimen.config_appTransitionAnimationDurationScaleDefault));setAnimatorDurationScale(Settings.Global.getFloat(resolver,Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScaleSetting));mForceDesktopModeOnExternalDisplays = Settings.Global.getInt(resolver,DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0;final String displaySettingsPath = Settings.Global.getString(resolver,DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH);mDisplayWindowSettingsProvider = displayWindowSettingsProvider;if (displaySettingsPath != null) {mDisplayWindowSettingsProvider.setBaseSettingsFilePath(displaySettingsPath);}mDisplayWindowSettings = new DisplayWindowSettings(this, mDisplayWindowSettingsProvider);IntentFilter filter = new IntentFilter();// Track changes to DevicePolicyManager state so we can enable/disable keyguard.filter.addAction(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);mLatencyTracker = LatencyTracker.getInstance(context);mSettingsObserver = new SettingsObserver();mHoldingScreenWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);mHoldingScreenWakeLock.setReferenceCounted(false);mSurfaceAnimationRunner = new SurfaceAnimationRunner(mTransactionFactory,mPowerManagerInternal);mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);mTaskPositioningController = new TaskPositioningController(this, mInputManager, mActivityTaskManager, mH.getLooper());mDragDropController = new DragDropController(this, mH.getLooper());mHighRefreshRateDenylist = HighRefreshRateDenylist.create(context.getResources());mConstants = new WindowManagerConstants(this, DeviceConfigInterface.REAL);mConstants.start(new HandlerExecutor(mH));LocalServices.addService(WindowManagerInternal.class, new LocalService());mEmbeddedWindowController = new EmbeddedWindowController(mAtmService);mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(mContext.getResources());mDisplayHashController = new DisplayHashController(mContext);setGlobalShadowSettings();// anr 控制类,无响应,无焦点等mAnrController = new AnrController(this);mStartingSurfaceController = new StartingSurfaceController(this);mBlurController = new BlurController(mContext, mPowerManager);}

mH 为 final H mH = new H() 成员变量,因此WMS后面的消息也是运行在 android.display 线程中的。

2. wms 与 ams 关联

mActivityManagerService.setWindowManager(wm);

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

973      public void setWindowManager(WindowManagerService wm) {
974          synchronized (mGlobalLock) {
975              mWindowManager = wm;
// 缓存 RootWindowContainer
976              mRootWindowContainer = wm.mRoot;
977              mTempConfig.setToDefaults();
978              mTempConfig.setLocales(LocaleList.getDefault());
979              mConfigurationSeq = mTempConfig.seq = 1;
980              mRootWindowContainer.onConfigurationChanged(mTempConfig);
981              mLockTaskController.setWindowManager(wm);
982              mTaskSupervisor.setWindowManager(wm);// RootWindowContainer 设置 WindowManager
983              mRootWindowContainer.setWindowManager(wm);
984          }
985      }

3. 初始化完成 onInitReady

wm.onInitReady();

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

    public void onInitReady() {initPolicy();// Add ourself to the Watchdog monitors.Watchdog.getInstance().addMonitor(this);createWatermark();showEmulatorDisplayOverlayIfNeeded();}=========private void initPolicy() {// 在ui 线程执行下列函数,"android.ui"UiThread.getHandler().runWithScissors(new Runnable() {@Overridepublic void run() {WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());// 初始化 PhoneWindowManagermPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);}}, 0);}

// 初始化 PhoneWindowManager

 /frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

    public void init(Context context, IWindowManager windowManager,WindowManagerFuncs windowManagerFuncs) {mContext = context;mWindowManager = windowManager;mWindowManagerFuncs = windowManagerFuncs;mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);mAppOpsManager = mContext.getSystemService(AppOpsManager.class);mDisplayManager = mContext.getSystemService(DisplayManager.class);mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);mPackageManager = mContext.getPackageManager();mHasFeatureWatch = mPackageManager.hasSystemFeature(FEATURE_WATCH);mHasFeatureLeanback = mPackageManager.hasSystemFeature(FEATURE_LEANBACK);mHasFeatureAuto = mPackageManager.hasSystemFeature(FEATURE_AUTOMOTIVE);mHasFeatureHdmiCec = mPackageManager.hasSystemFeature(FEATURE_HDMI_CEC);mAccessibilityShortcutController =new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);mLogger = new MetricsLogger();mScreenOffSleepTokenAcquirer = mActivityTaskManagerInternal.createSleepTokenAcquirer("ScreenOff");Resources res = mContext.getResources();mWakeOnDpadKeyPress =res.getBoolean(com.android.internal.R.bool.config_wakeOnDpadKeyPress);mWakeOnAssistKeyPress =res.getBoolean(com.android.internal.R.bool.config_wakeOnAssistKeyPress);mWakeOnBackKeyPress =res.getBoolean(com.android.internal.R.bool.config_wakeOnBackKeyPress);// Init display burn-in protectionboolean burnInProtectionEnabled = context.getResources().getBoolean(com.android.internal.R.bool.config_enableBurnInProtection);// Allow a system property to override this. Used by developer settings.boolean burnInProtectionDevMode =SystemProperties.getBoolean("persist.debug.force_burn_in", false);if (burnInProtectionEnabled || burnInProtectionDevMode) {final int minHorizontal;final int maxHorizontal;final int minVertical;final int maxVertical;final int maxRadius;if (burnInProtectionDevMode) {minHorizontal = -8;maxHorizontal = 8;minVertical = -8;maxVertical = -4;maxRadius = (isRoundWindow()) ? 6 : -1;} else {
....// handler是跑在ui 线程mHandler = new PolicyHandler();

4. InputManagerService 设置回调,并启动 InputManagerService 

            inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
            inputManager.start();

    public InputManagerCallback getInputManagerCallback() {return mInputManagerCallback;}// mInputManagerCallback 的值为对象 InputManagerCallback(this)final InputManagerCallback mInputManagerCallback = new InputManagerCallback(this);

setWindowManagerCallbacks 设置回调

/frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

    public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {// 保证只有一个 callbackif (mWindowManagerCallbacks != null) {unregisterLidSwitchCallbackInternal(mWindowManagerCallbacks);}mWindowManagerCallbacks = callbacks;registerLidSwitchCallbackInternal(mWindowManagerCallbacks);}========void registerLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {synchronized (mLidSwitchLock) {// 将 callback 保存到 mLidSwitchCallbacksmLidSwitchCallbacks.add(callback);// Skip triggering the initial callback if the system is not yet ready as the switch// state will be reported as KEY_STATE_UNKNOWN. The callback will be triggered in// systemRunning().if (mSystemReady) {boolean lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID)== KEY_STATE_UP;callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen);}}}

然后启动inputmanagerservice

    public void start() {Slog.i(TAG, "Starting input manager");nativeStart(mPtr);

5. 调用 displayReady 方法

          wm.displayReady();

    public void displayReady() {synchronized (mGlobalLock) {if (mMaxUiWidth > 0) {mRoot.forAllDisplays(displayContent -> displayContent.setMaxUiWidth(mMaxUiWidth));}applyForcedPropertiesForDefaultDisplay();mAnimator.ready();mDisplayReady = true;// Reconfigure all displays to make sure that forced properties and// DisplayWindowSettings are applied.mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked);mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);mIsFakeTouchDevice = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FAKETOUCH);}try {mActivityTaskManager.updateConfiguration(null);} catch (RemoteException e) {}}

6. 系统准备完毕,调用 systemReady


           wm.systemReady();

    public void systemReady() {mSystemReady = true;// 调用 PhoneWindowManager.java 的 systemReadymPolicy.systemReady();mRoot.forAllDisplayPolicies(DisplayPolicy::systemReady);
// 快照控制类mTaskSnapshotController.systemReady();mHasWideColorGamutSupport = queryWideColorGamutSupport();mHasHdrSupport = queryHdrSupport();
// ui线程执行 loadSettingsUiThread.getHandler().post(mSettingsObserver::loadSettings);

 整个启动过程有3个线程,systemserver主线程,”android.display”,”android.ui”,整个过程采用阻塞的方式(利用runWithScissors)执行,WMS.mH的Looper运行在”android.display”进程。

参考:

WMS启动流程分析 | Skytoby

WMS—启动过程 - Gityuan博客 | 袁辉辉的技术博客

Android渲染(一)_系统服务WMS启动过程(基于Android10) - 掘金


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/18280.html

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

相关文章

Java的Hibernate框架中集合类数据结构的映射编写教程

Java的Hibernate框架中集合类数据结构的映射编写教程 一、集合映射 1.集合小介 集合映射也是基本的映射&#xff0c;但在开发过程中不会经常用到&#xff0c;所以不需要深刻了解&#xff0c;只需要理解基本的使用方法即可&#xff0c;等在开发过程中遇到了这种问题时能够查询…

elementui自定义loading图标

效果图如下&#xff1a; 一、在assets下新建一个mycss.css文件夹&#xff08;图片大小以及文字样式&#xff0c;可以根据自己的需求进行微调&#xff09; .el-loading-spinner {/*这个是自己想设置的 gif 加载动图*/background-image: url(../gif2.gif); background-repeat: n…

redis的分布式事务-redisson

一 redisson 1.1 redisson分布式事务 Redisson分布式锁是一种基于redis实现的分布式锁&#xff0c;它利用redis的setnx命令实现分布式锁的互斥访问。同时还支持锁的自动续期功能&#xff0c;可以避免因为某个进程崩溃或者网络故障导致锁无法释放的情况。 只要线程一加锁成功…

各种好看的css效果收集

CSS动画特效-多种方案实现CSS光束扫过&#xff0c;扫光特效&#xff0c;ae文字过光效果&#xff0c;光效移动效果 一个集合180种免费的线性渐变网站&#xff0c;可在任何网站使用您不仅可以复制渐变的原生CSS颜色代码&#xff0c;还可以查看下载每个优质的渐变图片。 链接&…

第二课:Figma 界面认识

创建文件 进入 Figma 后&#xff0c;可以查看最近浏览的内容&#xff0c;官方也推荐了一些基础的项目&#xff0c;点击右上角 Design file&#xff08;设计文件&#xff09;即可创建项目&#xff1b; 注&#xff1a;网页版和本地版界面样式布局一致。 创建画布 点击左上画框…

C++_简单模拟实现string的基本结构

C中&#xff0c;string早于STL问世。使用string中的构造函数可以实现对string类型的字符串的一系列操作。 今天来模拟C中的string的基本结构。注意仅仅是简单模拟&#xff0c;string内部结构其实非常复杂&#xff0c;并且不同版本的IDEstring的内部结构也不尽相同。尽管有所不…

FLAC格式如何转换为MP3?分享三种方法!

在数字音乐的世界中&#xff0c;FLAC和MP3是两种常见的音频格式。FLAC (Free Lossless Audio Codec)提供无损的音质&#xff0c;但文件大小较大。而MP3文件较小&#xff0c;更易于传输和保存&#xff0c;但可能牺牲一些音质。如果你想将FLAC音频转换成MP3格式&#xff0c;本文将…

java版本企业电子招标采购系统源码Spring Cloud + Spring Boot +二次开发

java版本企业电子招标采购系统源码Spring Cloud Spring Boot 二次开发 一、立项管理 1、招标立项申请 功能点&#xff1a;招标类项目立项申请入口&#xff0c;用户可以保存为草稿&#xff0c;提交。 2、非招标立项申请 功能点&#xff1a;非招标立项申请入口、用户可以保存为草…

数学建模学习之简单设备分配问题

简单的设备分配问题 某公司新购置了某种设备 6台&#xff0c;欲分配给下属的4 个企业&#xff0c;已知各企业获得这种设备后年创利润如表 1.1 所示&#xff0c;单位为千万元。问应如何分配这些设备能使年创总利润最大&#xff0c;最大利润是多少? 表1.1的数据为&#xff1a; 对…

MYSQL学习第一天

1.创建数据库&#xff0c;删除数据库&#xff0c;查询创建数据的语句&#xff0c;使用数据库&#xff0c;查询当前默认的数据库以及使用的编码方式校验规则 1.1 创建数据库 create database db_name; 1.2 使用数据库 use db_name; 1.3 查询当前使用的数据库 select datab…

计讯物联网关型水利遥测终端机TY910确保闸站自动化监测长效运行

闸站是我国水利建设工程的重要组成部分&#xff0c;具备调度水源、防洪排涝、灌溉等能力&#xff0c;在农业、水路运输、养殖业等行业领域起着关键作用&#xff0c;进而解决区域水资源不均衡的问题&#xff0c;促进水资源多方面的利用。当前&#xff0c;我国闸站存在数量多、分…

第三次CCF计算机软件能力认证

第一题&#xff1a;门禁系统 涛涛最近要负责图书馆的管理工作&#xff0c;需要记录下每天读者的到访情况。 每位读者有一个编号&#xff0c;每条记录用读者的编号来表示。 给出读者的来访记录&#xff0c;请问每一条记录中的读者是第几次出现。 输入格式 输入的第一行包含一个整…