Android 12系统源码_窗口管理(九)深浅主题切换流程源码分析

前言

上一篇我们简单介绍了应用的窗口属性WindowConfiguration这个类,该类存储了当前窗口的显示区域、屏幕的旋转方向、窗口模式等参数,当设备屏幕发生旋转的时候就是通过该类将具体的旋转数据传递给应用的、而应用在加载资源文件的时候也会结合该类的AppBounds属性,自动加载特定分辨率的资源文件。而在这些属性发生变化之后,系统一般都是在onConfigurationChanged方法中作出响应的,这篇文章我们将会结合系统深浅主题发生切换到时候,新的系统配置是如何通过onConfigurationChanged回调给Activity的。

一、用户行为触发深浅模式主题切换

这里我们以下拉状态栏的快捷按钮深色主题切换为切入口来做具体源码分析,我们点击该按钮,首先会触发UiModeNightTile的handleClick方法。

1.1 SystemUI阶段

frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java

public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implementsConfigurationController.ConfigurationListener,BatteryController.BatteryStateChangeCallback {private UiModeManager mUiModeManager;@Overrideprotected void handleClick(@Nullable View view) {if (getState().state == Tile.STATE_UNAVAILABLE) {return;}boolean newState = !mState.value;//注释1mUiModeManager.setNightModeActivated(newState);refreshState(newState);}
}

在注释1处调用UiModeManager的setNightModeActivated方法。

1.2 UiModeManager阶段

frameworks/base/core/java/android/app/UiModeManager.java

public class UiModeManager {private IUiModeManager mService;UiModeManager(Context context) throws ServiceNotFoundException {mService = IUiModeManager.Stub.asInterface(ServiceManager.getServiceOrThrow(Context.UI_MODE_SERVICE));mContext = context;}/*** 激活当前用户的夜间模式UI视图*/public boolean setNightModeActivated(boolean active) {if (mService != null) {try {//注释1,调用UiModeManagerService的setNightModeActivated方法return mService.setNightModeActivated(active);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}return false;}
}

在注释1处调用UiModeManagerService的setNightModeActivated方法。

1.3 UiModeManagerService阶段

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

final class UiModeManagerService extends SystemService {private Configuration mConfiguration = new Configuration();@Overridepublic boolean setNightModeActivated(boolean active) {...代码省略...synchronized (mLock) {final long ident = Binder.clearCallingIdentity();try {//自动、自定义的主题切换if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {unregisterScreenOffEventLocked();mOverrideNightModeOff = !active;mOverrideNightModeOn = active;mOverrideNightModeUser = mCurrentUser;persistNightModeOverrides(mCurrentUser);} else if (mNightMode == UiModeManager.MODE_NIGHT_NO && active) {//夜间模式mNightMode = UiModeManager.MODE_NIGHT_YES;} else if (mNightMode == UiModeManager.MODE_NIGHT_YES&& !active) {//日间模式mNightMode = UiModeManager.MODE_NIGHT_NO;}//注释1,更新ConfigurationupdateConfigurationLocked();//注释2,应用ConfigurationapplyConfigurationExternallyLocked();//为当前用户对应的Secure添加配置信息persistNightMode(mCurrentUser);return true;} finally {Binder.restoreCallingIdentity(ident);}}}private void updateConfigurationLocked() {...代码省略...if (!mHoldingConfiguration && (!mWaitForScreenOff || mPowerSave)) {//将新的UIMode赋值给mConfigurationmConfiguration.uiMode = uiMode;}}private void applyConfigurationExternallyLocked() {if (mSetUiMode != mConfiguration.uiMode) {mSetUiMode = mConfiguration.uiMode;//清除窗口户的快照缓存mWindowManager.clearSnapshotCache();try {//注释3,调用ATMS的updateConfiguration方法,更新当前用户对应的ConfigurationActivityTaskManager.getService().updateConfiguration(mConfiguration);} catch (RemoteException e) {Slog.w(TAG, "Failure communicating with activity manager", e);} catch (SecurityException e) {Slog.e(TAG, "Activity does not have the ", e);}}}}

在注释1处调用updateConfigurationLocked方法,该方法会将新的UIMode赋值给mConfiguration;在注释2处调用
applyConfigurationExternallyLocked方法,该方法内部也就是注释3处,会调用ATMS的updateConfiguration方法,将当前用户包含了新UiMode的Configuration同步给系统中的其他模块和应用。

二、ActivityTaskManagerService通知ActivityThread

2.1 ActivityTaskManagerService阶段

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

public class ActivityTaskManagerService extends IActivityTaskManager.Stub {@Overridepublic boolean updateConfiguration(Configuration values) {mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");synchronized (mGlobalLock) {if (mWindowManager == null) {Slog.w(TAG, "Skip updateConfiguration because mWindowManager isn't set");return false;}if (values == null) {//从WMS中获取当前系统默认屏幕设备对应的配置信息values = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);}mH.sendMessage(PooledLambda.obtainMessage(ActivityManagerInternal::updateOomLevelsForDisplay, mAmInternal,DEFAULT_DISPLAY));final long origId = Binder.clearCallingIdentity();try {if (values != null) {Settings.System.clearConfiguration(values);}//注释1,继续调用updateConfigurationLocked,更新配置信息updateConfigurationLocked(values, null, false, false /* persistent */,UserHandle.USER_NULL, false /* deferResume */,mTmpUpdateConfigurationResult);return mTmpUpdateConfigurationResult.changes != 0;} finally {Binder.restoreCallingIdentity(origId);}}}boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,boolean initLocale, boolean persistent, int userId, boolean deferResume,ActivityTaskManagerService.UpdateConfigurationResult result) {int changes = 0;boolean kept = true;deferWindowLayout();try {if (values != null) {//注释2,更新全局配置changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId);}if (!deferResume) {//注释4,更新后确保配置和可见性kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);}} finally {continueWindowLayout();}if (result != null) {result.changes = changes;result.activityRelaunched = !kept;}return kept;}int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,boolean persistent, int userId) {...代码省略...SparseArray<WindowProcessController> pidMap = mProcessMap.getPidMap();for (int i = pidMap.size() - 1; i >= 0; i--) {final int pid = pidMap.keyAt(i);final WindowProcessController app = pidMap.get(pid);ProtoLog.v(WM_DEBUG_CONFIGURATION, "Update process config of %s to new "+ "config %s", app.mName, mTempConfig);//注释3,调用app的onConfigurationChanged方法app.onConfigurationChanged(mTempConfig);}final Message msg = PooledLambda.obtainMessage(ActivityManagerInternal::broadcastGlobalConfigurationChanged,mAmInternal, changes, initLocale);mH.sendMessage(msg);mRootWindowContainer.onConfigurationChanged(mTempConfig);return changes;}}    

注释1处ATMS的updateConfiguration方法继续调用updateConfigurationLocked;
注释2处调用updateGlobalConfigurationLocked方法更新当前系统的全局配置;
注释3获取当前系统中的每一个窗口管理者,调用app.onConfigurationChanged方法,告知他们系统配置发生了变化。

2.2 WindowProcessController阶段

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

public class WindowProcessController extends ConfigurationContainer<ConfigurationContainer>implements ConfigurationContainerListener {private final ActivityTaskManagerService mAtm;@Overridepublic void onConfigurationChanged(Configuration newGlobalConfig) {super.onConfigurationChanged(newGlobalConfig);//调用updateConfiguration方法updateConfiguration();}private void updateConfiguration() {final Configuration config = getConfiguration();if (mLastReportedConfiguration.diff(config) == 0) {if (Build.IS_DEBUGGABLE && mHasImeService) {Slog.w(TAG_CONFIGURATION, "Current config: " + config+ " unchanged for IME proc " + mName);}return;}if (mPauseConfigurationDispatchCount > 0) {mHasPendingConfigurationChange = true;return;}//调用dispatchConfiguration方法dispatchConfiguration(config);}void dispatchConfiguration(Configuration config) {mHasPendingConfigurationChange = false;if (mThread == null) {if (Build.IS_DEBUGGABLE && mHasImeService) {Slog.w(TAG_CONFIGURATION, "Unable to send config for IME proc " + mName+ ": no app thread");}return;}try {config.seq = mAtm.increaseConfigurationSeqLocked();//注释1,获取ClientLifecycleManager实例//调用其scheduleTransaction方法,并传入ConfigurationChangeItem对象实例mAtm.getLifecycleManager().scheduleTransaction(mThread,ConfigurationChangeItem.obtain(config));setLastReportedConfiguration(config);} catch (Exception e) {Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);}}    
}    public class ActivityTaskManagerService extends IActivityTaskManager.Stub {private final ClientLifecycleManager mLifecycleManager;ClientLifecycleManager getLifecycleManager() {return mLifecycleManager;}   
}>frameworks/base/services/core/java/com/android/server/wm/ClientLifecycleManager.java
class ClientLifecycleManager {void scheduleTransaction(@NonNull IApplicationThread client,@NonNull ClientTransactionItem callback) throws RemoteException {final ClientTransaction clientTransaction = transactionWithCallback(client,null /* activityToken */, callback);//调用scheduleTransaction方法scheduleTransaction(clientTransaction);}private static ClientTransaction transactionWithCallback(@NonNull IApplicationThread client,IBinder activityToken, @NonNull ClientTransactionItem callback) {//获取clientTransaction 对象实例final clientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken);clientTransaction.addCallback(callback);return clientTransaction;}void scheduleTransaction(ClientTransaction transaction) throws RemoteException {final IApplicationThread client = transaction.getClient();//调用ClientTransaction的scheduleTransactiontransaction.schedule();if (!(client instanceof Binder)) {transaction.recycle();}}
}
>frameworks/base/core/java/android/app/servertransaction/ClientTransaction.java
public class ClientTransaction implements Parcelable, ObjectPoolItem {private IApplicationThread mClient;public void schedule() throws RemoteException {//注释2,调用IApplicationThread的scheduleTransaction方法mClient.scheduleTransaction(this);}
}public final class ActivityThread extends ClientTransactionHandlerimplements ActivityThreadInternal {private class ApplicationThread extends IApplicationThread.Stub {@Overridepublic void scheduleTransaction(ClientTransaction transaction) throws RemoteException {//注释3,调用scheduleTransaction方法,该方法位于父类ClientTransactionHandler中ActivityThread.this.scheduleTransaction(transaction);}}
}public abstract class ClientTransactionHandler {void scheduleTransaction(ClientTransaction transaction) {transaction.preExecute(this);//注释3,调用sendMessagesendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);}
}public final class ActivityThread extends ClientTransactionHandler implements ActivityThreadInternal {private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this);final H mH = new H();void sendMessage(int what, Object obj) {sendMessage(what, obj, 0, 0, false);}private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {Message msg = Message.obtain();msg.what = what;msg.obj = obj;msg.arg1 = arg1;msg.arg2 = arg2;if (async) {msg.setAsynchronous(true);}mH.sendMessage(msg);}class H extends Handler {public static final int EXECUTE_TRANSACTION = 159;public void handleMessage(Message msg) {      case EXECUTE_TRANSACTION:final ClientTransaction transaction = (ClientTransaction) msg.obj;//注释4,调用TransactionExecutor的execute方法mTransactionExecutor.execute(transaction);if (isSystem()) {transaction.recycle();}break;}}   
}public class TransactionExecutor {public void execute(ClientTransaction transaction) {...代码省略...//继续调用executeCallbacks方法executeCallbacks(transaction);executeLifecycleState(transaction);mPendingActions.clear();}public void executeCallbacks(ClientTransaction transaction) {...代码省略...  final int size = callbacks.size();for (int i = 0; i < size; ++i) {final ClientTransactionItem item = callbacks.get(i);...代码省略...//注释4,调用每个ClientTransactionItem对象实例的execute方法item.execute(mTransactionHandler, token, mPendingActions);item.postExecute(mTransactionHandler, token, mPendingActions);...代码省略...}}
}public class ConfigurationChangeItem extends ClientTransactionItem {//获取ConfigurationChangeItem实例对象public static ConfigurationChangeItem obtain(Configuration config) {ConfigurationChangeItem instance = ObjectPool.obtain(ConfigurationChangeItem.class);if (instance == null) {instance = new ConfigurationChangeItem();}instance.mConfiguration = config;return instance;}@Overridepublic void execute(ClientTransactionHandler client, IBinder token,PendingTransactionActions pendingActions) {//注释5,这里会触发ActivityThreade的handleConfigurationChanged方法client.handleConfigurationChanged(mConfiguration);}
}public class ActivityRelaunchItem extends ActivityTransactionItem {@Overridepublic void execute(ClientTransactionHandler client, ActivityClientRecord r,PendingTransactionActions pendingActions) {if (mActivityClientRecord == null) {if (DEBUG_ORDER) Slog.d(TAG, "Activity relaunch cancelled");return;}Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");//注释6,调用ActivityThread的handleRelaunchActivity方法client.handleRelaunchActivity(mActivityClientRecord, pendingActions);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);}
}public class ActivityConfigurationChangeItem extends ActivityTransactionItem {@Overridepublic void execute(ClientTransactionHandler client, ActivityClientRecord r,PendingTransactionActions pendingActions) {Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityConfigChanged");//注释7,调用ActivityThread的handleActivityConfigurationChanged方法client.handleActivityConfigurationChanged(r, mConfiguration, INVALID_DISPLAY);Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);}
}

对以上代码做个简单概述。

  • WindowProcessController的onConfigurationChanged方法经过层层调用,最终会在注释1处触发一个关键调用,调用ATM的getLifecycleManager方法获取ClientLifecycleManager实例,并调用该对象的scheduleTransaction方法,传入ConfigurationChangeItem对象实例作为参数,这个我们后面还会提到。
  • ClientLifecycleManager的scheduleTransaction方法继续往下走,会在注释2处触发IApplicationThread的scheduleTransaction方法,该方法位于父类ClientTransactionHandler中,在注释3处调用sendMessage方法,发送ActivityThread.H.EXECUTE_TRANSACTION的消息类型,最终会走到注释4处,调用TransactionExecutor的execute方法。
  • TransactionExecutor的execute方法继续调用executeCallbacks方法,然后在注释5处调用每个ClientTransactionItem对象实例的execute方法,我们在前面注释1处提过到过ConfigurationChangeItem对象作为参数。
  • 默认情况下在UIMode发生切换的时候,这里的ClientTransactionItem对象实例其实是ActivityRelaunchItem,执行execute方法,也就是注释6处会触发ActivityThread的handleRelaunchActivity,最终会触发Activity的onCreate方法。
  • 如果Activity页面不希望UIMode发生切换的时候重新执行onCreate方法,这里的ClientTransactionItem对象实例其实是ActivityConfigurationChangeItem,执行execute方法,也就是注释7处会触发ActivityThread的handleActivityConfigurationChanged,最终会触发Activity的onConfigurationChanged方法。

三、ActivityThread通知应用

3.1 回调Activity的onCreate方法

public final class ActivityThread extends ClientTransactionHandlerimplements ActivityThreadInternal {@Overridepublic void handleRelaunchActivity(ActivityClientRecord tmp,PendingTransactionActions pendingActions) {...代码省略...//注释1,调用handleRelaunchActivityInner方法handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");...代码省略...}private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,PendingTransactionActions pendingActions, boolean startsNotResumed,Configuration overrideConfig, String reason) {// Preserve last used intent, it may be set from Activity#setIntent().final Intent customIntent = r.activity.mIntent;// Need to ensure state is saved.if (!r.paused) {performPauseActivity(r, false, reason, null /* pendingActions */);}if (!r.stopped) {callActivityOnStop(r, true /* saveState */, reason);}//注释2,调用handleDestroyActivityhandleDestroyActivity(r, false, configChanges, true, reason);r.activity = null;r.window = null;r.hideForNow = false;r.nextIdle = null;// Merge any pending results and pending intents; don't just replace themif (pendingResults != null) {if (r.pendingResults == null) {r.pendingResults = pendingResults;} else {r.pendingResults.addAll(pendingResults);}}if (pendingIntents != null) {if (r.pendingIntents == null) {r.pendingIntents = pendingIntents;} else {r.pendingIntents.addAll(pendingIntents);}}r.startsNotResumed = startsNotResumed;r.overrideConfig = overrideConfig;//注释3,调用handleLaunchActivity方法handleLaunchActivity(r, pendingActions, customIntent);}@Overridepublic void handleDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges,...代码省略...WindowManager wm = r.activity.getWindowManager();View v = r.activity.mDecor;...代码省略...//将当前activity对应的viwe从WMS中移除wm.removeViewImmediate(v);...代码省略...}}
}

注释1处继续调用handleRelaunchActivityInner方法;
注释2处调用handleDestroyActivity方法将当前Activity对应的视图从WMS中移除。
注释3处调用handleLaunchActivity方法将当前Activity对应的视图重新添加到WMS中。

3.2 回调Activity的onConfigurationChanged方法

public final class ActivityThread extends ClientTransactionHandlerimplements ActivityThreadInternal {public void handleActivityConfigurationChanged(ActivityClientRecord r,@NonNull Configuration overrideConfig, int displayId) {...代码省略...r.overrideConfig = overrideConfig;final ViewRootImpl viewRoot = r.activity.mDecor != null? r.activity.mDecor.getViewRootImpl() : null;//注释1,调用performConfigurationChangedForActivity方法final Configuration reportedConfig = performConfigurationChangedForActivity(r,mConfigurationController.getCompatConfiguration(),movedToDifferentDisplay ? displayId : r.activity.getDisplayId());if (viewRoot != null) {if (movedToDifferentDisplay) {viewRoot.onMovedToDisplay(displayId, reportedConfig);}viewRoot.updateConfiguration(displayId);}mSomeActivitiesChanged = true;}private Configuration performConfigurationChangedForActivity(ActivityClientRecord r,Configuration newBaseConfig, int displayId) {r.tmpConfig.setTo(newBaseConfig);if (r.overrideConfig != null) {r.tmpConfig.updateFrom(r.overrideConfig);}//注释2,调用performActivityConfigurationChanged方法final Configuration reportedConfig = performActivityConfigurationChanged(r.activity,r.tmpConfig, r.overrideConfig, displayId);freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));return reportedConfig;}private Configuration performActivityConfigurationChanged(Activity activity,Configuration newConfig, Configuration amOverrideConfig, int displayId) {...代码省略...//调用activity的onConfigurationChanged方法activity.onConfigurationChanged(configToReport);...代码省略...}
}

注释1处继续调用performConfigurationChangedForActivity方法;
注释2处调用performActivityConfigurationChanged方法;
注释3处调用Activity的onConfigurationChanged方法。

参考文章:Android 13 深色主题切换流程

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

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

相关文章

河南省的教育部科技查新工作站有哪些?

郑州大学图书馆&#xff08;Z12&#xff09;&#xff1a;2007年1月被批准设立“教育部综合类科技查新工作站”&#xff0c;同年12月被河南省科技厅认定为河南省省级科技查新机构。主要面向河南省的高校、科研机构、企业提供科技查新、查收查引等服务。 河南大学图书馆&#xf…

Leetcode经典题6--买卖股票的最佳时机

买卖股票的最佳时机 题目描述&#xff1a; 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。…

MCPTT 与BTC

MCPTT&#xff08;Mission Critical Push-to-Talk&#xff09;和B-TrunC&#xff08;宽带集群&#xff09;是两种关键通信标准&#xff0c;它们分别由不同的组织制定和推广。 MCPTT&#xff08;Mission Critical Push-to-Talk&#xff09;标准由3GPP&#xff08;第三代合作伙伴…

去除账号密码自动赋值时的输入框背景色

问题描述&#xff1a; 前端使用账号密码登录&#xff0c;若在网页保存过当前页面的密码和账号&#xff0c;那么当再次进入该页面&#xff0c;网页会自动的把账号和密码赋到输入框中&#xff0c;而此时输入框是带有背景色的&#xff0c;与周边的白色背景显得很不协调&#xff1…

【Pytorch】torch.reshape与torch.Tensor.reshape区别

问题引入&#xff1a; 在Pytorch文档中&#xff0c;有torch.reshape与torch.Tensor.reshape两个reshape操作&#xff0c;他们的区别是什么呢&#xff1f; 我们先来看一下官方文档的定义&#xff1a; torch.reshape&#xff1a; torch.Tensor.reshape: 解释&#xff1a; 在p…

扫码与短信验证码登录JS逆向分析与Python纯算法还原

文章目录 1. 写在前面2. 扫码接口分析2. 短信接口分析3. 加密算法还原【🏠作者主页】:吴秋霖 【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研究与开发工作!…

spring6:3容器:IoC

spring6&#xff1a;3容器&#xff1a;IoC 目录 spring6&#xff1a;3容器&#xff1a;IoC3、容器&#xff1a;IoC3.1、IoC容器3.1.1、控制反转&#xff08;IoC&#xff09;3.1.2、依赖注入3.1.3、IoC容器在Spring的实现 3.2、基于XML管理Bean3.2.1、搭建子模块spring6-ioc-xml…

【认证法规】安全隔离变压器

文章目录 定义反激电源变压器 定义 安全隔离变压器&#xff08;safety isolating transformer&#xff09;&#xff0c;通过至少相当于双重绝缘或加强绝缘的绝缘使输入绕组与输出绕组在电气上分开的变压器。这种变压器是为以安全特低电压向配电电路、电器或其它设备供电而设计…

车机端同步outlook日历

最近在开发一个车机上的日历助手&#xff0c;其中一个需求就是要实现手机端日历和车机端日历数据的同步。然而这种需求似乎没办法实现&#xff0c;毕竟手机日历是手机厂商自己带的系统应用&#xff0c;根本不能和车机端实现数据同步的。 那么只能去其他公共的平台寻求一些机会&…

OpenCV-图像阈值

简单阈值法 此方法是直截了当的。如果像素值大于阈值&#xff0c;则会被赋为一个值&#xff08;可能为白色&#xff09;&#xff0c;否则会赋为另一个值&#xff08;可能为黑色&#xff09;。使用的函数是 cv.threshold。第一个参数是源图像&#xff0c;它应该是灰度图像。第二…

力扣300.最长递增子序列

题目描述 题目链接300. 最长递增子序列 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 […

Vue CLI的作用

Vue CLI&#xff08;Command Line Interface&#xff09;是一个基于Vue.js的官方脚手架工具&#xff0c;其主要作用是帮助开发者快速搭建Vue项目的基础结构和开发环境。以下是Vue CLI的具体作用&#xff1a; 1、项目模板与快速生成 Vue CLI提供了一系列预设的项目模板&#x…

【蓝桥杯每日一题】扫雷

扫雷 知识点 2024-12-3 蓝桥杯每日一题 扫雷 dfs &#xff08;bfs也是可行的&#xff09; 题目大意 在一个二维平面上放置这N个炸雷&#xff0c;每个炸雷的信息有$(x_i,y_i,r_i) $&#xff0c;前两个是坐标信息&#xff0c;第三个是爆炸半径。然后会输入M个排雷火箭&#xff0…

【大数据学习 | 面经】Spark 3.x 中的AQE(自适应查询执行)

Spark 3.x 中的自适应查询执行&#xff08;Adaptive Query Execution&#xff0c;简称 AQE&#xff09;通过多种方式提升性能&#xff0c;主要包括以下几个方面&#xff1a; 动态合并 Shuffle 分区&#xff08;Coalescing Post Shuffle Partitions&#xff09;&#xff1a; 当 …

城电科技 | 光伏景观长廊 打造美丽乡村绿色低碳示范区 光伏景观设计方案

光伏景观长廊是一种结合了光伏发电技术和零碳景观设计的新型公共公共设施&#xff0c;光伏景观长廊顶上的光伏板不仅可以为周边用电设备提供清洁电能&#xff0c;而且还能作为遮阳设施使用&#xff0c;为人们提供一个美丽又实用的休闲娱乐空间。 光伏景观长廊建设对打造美丽乡…

开发系统准备与开发环境配置总结

开发前系统配置及环境搭建 系统配置0 Github打不开、速度慢怎么办1 WSL、Linux、Ubuntu、Docker都是什么鬼2 在Windows下安装WSL和Ubuntu3 配置MySQL4 配置Redis并启动服务5 Docker&#xff08;Windows和Ubuntu下&#xff09;6 Nginx 系统配置 你好&#xff01; 这是你第一次使…

uniapp 添加loading

在uniapp中添加loading可以使用uni的API uni.showLoading 方法。以下是一个简单的示例代码 // 显示loading uni.showLoading({title: 加载中 });// 假设这里是异步操作&#xff0c;比如网络请求 setTimeout(function () {// 隐藏loadinguni.hideLoading(); }, 2000);

C++(九)

前言&#xff1a; 本文主要讲述运算符的优先顺序。 一&#xff0c;运算符的优先级。 请看以下表达式&#xff1a; a32*5 运算结果为&#xff1a;13. 可以看到&#xff0c;在此代码中&#xff0c;先运行了2*5的结果&#xff0c;在此基础上在进行3操作&#xff0c;因此结果…

Android 拍照(有无存储权限两种方案,兼容Q及以上版本)

在某些行业&#xff0c;APP可能被禁止使用存储权限&#xff0c;或公司在写SDK功能&#xff0c;不方便获取权限 所以需要有 无存储权限拍照方案。这里两种方案都列出里。 对于写入权限&#xff0c;在高版本中&#xff0c;已经废弃&#xff0c; 不可用文件写入读取权限&#xf…

【Altium Designer 】AD如何使用嘉立创元器件的3D封装

1.下载3D封装 以STM32F407VGT6为例&#xff0c;进入嘉立创商城网站&#xff0c;找到需要的元器件封装 复制编号&#xff0c;打开嘉立创EDA&#xff0c;编译器选择专业版&#xff0c;新建工程&#xff0c;点击PCB1 复制编号在搜索框中&#xff0c;点击搜索&#xff0c;然后放置…