Android U 多任务启动分屏——system_server流程(更新中)

前文

Android U 多任务启动分屏——SystemUI流程
前文说到Transitions的startTransition方法中,通过mOrganizer.startNewTransition(type, wct);提交WindowContainerTransaction相关事务到system_server侧,继续跟踪其流程。

system_server侧分屏处理流程

systemui跨进程通信到system_server

代码路径:frameworks/base/core/java/android/window/WindowOrganizer.java

    /*** Starts a new transition, don't use this to start an already created one.* @param type The type of the transition. This is ignored if a transitionToken is provided.* @param t The set of window operations that are part of this transition.* @return A token identifying the transition. This will be the same as transitionToken if it*         was provided.* @hide*/@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)@NonNullpublic IBinder startNewTransition(int type, @Nullable WindowContainerTransaction t) {try {return getWindowOrganizerController().startNewTransition(type, t);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}static IWindowOrganizerController getWindowOrganizerController() {return IWindowOrganizerControllerSingleton.get();}

这里可以看出getWindowOrganizerController()就是获取IWindowOrganizerController对象,调用其startNewTransition(type, t)方法,其中参数type为systemui侧传递的TRANSIT_TO_FRONT(值为3),t则是systemui侧传递的WindowContainerTransaction对象。

代码路径:frameworks/base/core/java/android/window/IWindowOrganizerController.aidl

interface IWindowOrganizerController {....../*** Starts a new transition.* @param type The transition type.* @param t Operations that are part of the transition.* @return a token representing the transition.*/IBinder startNewTransition(int type, in @nullable WindowContainerTransaction t);

找到其aidl接口,接下来找到其实现类

/*** Server side implementation for the interface for organizing windows* @see android.window.WindowOrganizer*/
class WindowOrganizerController extends IWindowOrganizerController.Stubimplements BLASTSyncEngine.TransactionReadyListener {

代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java

    @Overridepublic IBinder startNewTransition(int type, @Nullable WindowContainerTransaction t) {return startTransition(type, null /* transitionToken */, t);}

最终调用到WindowOrganizerController的startNewTransition方法,该方法就是调用一个startTransition方法,这个方法中传递了type(值为3)、transitionToken(值为null)以及WindowContainerTransaction对象。

处理动画并提交事务

    private IBinder startTransition(@WindowManager.TransitionType int type,@Nullable IBinder transitionToken, @Nullable WindowContainerTransaction t) {//检查MANAGE_ACTIVITY_TASKS权限enforceTaskPermission("startTransition()");final CallerInfo caller = new CallerInfo();final long ident = Binder.clearCallingIdentity();try {synchronized (mGlobalLock) {Transition transition = Transition.fromBinder(transitionToken);if (mTransitionController.getTransitionPlayer() == null && transition == null) {Slog.w(TAG, "Using shell transitions API for legacy transitions.");if (t == null) {throw new IllegalArgumentException("Can't use legacy transitions in"+ " compatibility mode with no WCT.");}applyTransaction(t, -1 /* syncId */, null, caller);return null;}final WindowContainerTransaction wct =t != null ? t : new WindowContainerTransaction();if (transition == null) {if (type < 0) {throw new IllegalArgumentException("Can't create transition with no type");}// This is a direct call from shell, so the entire transition lifecycle is// contained in the provided transaction if provided. Thus, we can setReady// immediately after apply.final boolean needsSetReady = t != null;final Transition nextTransition = new Transition(type, 0 /* flags */,mTransitionController, mService.mWindowManager.mSyncEngine);nextTransition.calcParallelCollectType(wct);mTransitionController.startCollectOrQueue(nextTransition,(deferred) -> {nextTransition.start();nextTransition.mLogger.mStartWCT = wct;applyTransaction(wct, -1 /* syncId */, nextTransition, caller,deferred);if (needsSetReady) {nextTransition.setAllReady();}});return nextTransition.getToken();}// The transition already started collecting before sending a request to shell,// so just start here.if (!transition.isCollecting() && !transition.isForcePlaying()) {Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably"+ " means Shell took too long to respond to a request. WM State may be"+ " incorrect now, please file a bug");applyTransaction(wct, -1 /*syncId*/, null /*transition*/, caller);return transition.getToken();}transition.start();transition.mLogger.mStartWCT = wct;applyTransaction(wct, -1 /*syncId*/, transition, caller);// Since the transition is already provided, it means WMCore is determining the// "readiness lifecycle" outside the provided transaction, so don't set ready here.return transition.getToken();}} finally {Binder.restoreCallingIdentity(ident);}}
    private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,@Nullable Transition transition, @NonNull CallerInfo caller, boolean deferred) {if (deferred) {try {return applyTransaction(t, syncId, transition, caller);} catch (RuntimeException e) {// If the transaction is deferred, the caller could be from TransitionController// #tryStartCollectFromQueue that executes on system's worker thread rather than// binder thread. And the operation in the WCT may be outdated that violates the// current state. So catch the exception to avoid crashing the system.Slog.e(TAG, "Failed to execute deferred applyTransaction", e);}return TRANSACT_EFFECTS_NONE;}return applyTransaction(t, syncId, transition, caller);}
    private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,@Nullable Transition transition, @NonNull CallerInfo caller) {return applyTransaction(t, syncId, transition, caller, null /* finishTransition */);}

处理事务

    /*** @param syncId If non-null, this will be a sync-transaction.* @param transition A transition to collect changes into.* @param caller Info about the calling process.* @param finishTransition The transition that is currently being finished.* @return The effects of the window container transaction.*/private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,@Nullable Transition transition, @NonNull CallerInfo caller,@Nullable Transition finishTransition) {int effects = TRANSACT_EFFECTS_NONE;ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);mService.deferWindowLayout();mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);try {if (transition != null) {transition.applyDisplayChangeIfNeeded();}final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();final int hopSize = hops.size();final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =t.getChanges().entrySet().iterator();while (entries.hasNext()) {final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());if (wc == null || !wc.isAttached()) {Slog.e(TAG, "Attempt to operate on detached container: " + wc);continue;}// Make sure we add to the syncSet before performing// operations so we don't end up splitting effects between the WM// pending transaction and the BLASTSync transaction.if (syncId >= 0) {addToSyncSet(syncId, wc);}if (transition != null) transition.collect(wc);if ((entry.getValue().getChangeMask()& WindowContainerTransaction.Change.CHANGE_FORCE_NO_PIP) != 0) {// Disable entering pip (eg. when recents pretends to finish itself)if (finishTransition != null) {finishTransition.setCanPipOnFinish(false /* canPipOnFinish */);} else if (transition != null) {transition.setCanPipOnFinish(false /* canPipOnFinish */);}}// A bit hacky, but we need to detect "remove PiP" so that we can "wrap" the// setWindowingMode call in force-hidden.boolean forceHiddenForPip = false;if (wc.asTask() != null && wc.inPinnedWindowingMode()&& entry.getValue().getWindowingMode() == WINDOWING_MODE_UNDEFINED) {// We are in pip and going to undefined. Now search hierarchy ops to determine// whether we are removing pip or expanding pip.for (int i = 0; i < hopSize; ++i) {final WindowContainerTransaction.HierarchyOp hop = hops.get(i);if (hop.getType() != HIERARCHY_OP_TYPE_REORDER) continue;final WindowContainer hopWc = WindowContainer.fromBinder(hop.getContainer());if (!wc.equals(hopWc)) continue;forceHiddenForPip = !hop.getToTop();}}if (forceHiddenForPip) {wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);// When removing pip, make sure that onStop is sent to the app ahead of// onPictureInPictureModeChanged.// See also PinnedStackTests#testStopBeforeMultiWindowCallbacksOnDismisswc.asTask().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);wc.asTask().mTaskSupervisor.processStoppingAndFinishingActivities(null /* launchedActivity */, false /* processPausingActivities */,"force-stop-on-removing-pip");}int containerEffect = applyWindowContainerChange(wc, entry.getValue(),t.getErrorCallbackToken());effects |= containerEffect;if (forceHiddenForPip) {wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */);}// Lifecycle changes will trigger ensureConfig for everything.if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0&& (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {haveConfigChanges.add(wc);}}// Hierarchy changesif (hopSize > 0) {final boolean isInLockTaskMode = mService.isInLockTaskMode();for (int i = 0; i < hopSize; ++i) {effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,isInLockTaskMode, caller, t.getErrorCallbackToken(),t.getTaskFragmentOrganizer(), finishTransition);}}// Queue-up bounds-change transactions for tasks which are now organized. Do// this after hierarchy ops so we have the final organized state.entries = t.getChanges().entrySet().iterator();while (entries.hasNext()) {final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());if (wc == null || !wc.isAttached()) {Slog.e(TAG, "Attempt to operate on detached container: " + wc);continue;}final Task task = wc.asTask();final Rect surfaceBounds = entry.getValue().getBoundsChangeSurfaceBounds();if (task == null || !task.isAttached() || surfaceBounds == null) {continue;}if (!task.isOrganized()) {final Task parent = task.getParent() != null ? task.getParent().asTask() : null;// Also allow direct children of created-by-organizer tasks to be// controlled. In the future, these will become organized anyways.if (parent == null || !parent.mCreatedByOrganizer) {throw new IllegalArgumentException("Can't manipulate non-organized task surface " + task);}}final SurfaceControl.Transaction sft = new SurfaceControl.Transaction();final SurfaceControl sc = task.getSurfaceControl();sft.setPosition(sc, surfaceBounds.left, surfaceBounds.top);if (surfaceBounds.isEmpty()) {sft.setWindowCrop(sc, null);} else {sft.setWindowCrop(sc, surfaceBounds.width(), surfaceBounds.height());}task.setMainWindowSizeChangeTransaction(sft);}if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);// Already calls ensureActivityConfigmService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);mService.mRootWindowContainer.resumeFocusedTasksTopActivities();} else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {haveConfigChanges.valueAt(i).forAllActivities(r -> {r.ensureActivityConfiguration(0, PRESERVE_WINDOWS);});}}if (effects != 0) {mService.mWindowManager.mWindowPlacerLocked.requestTraversal();}} finally {mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);mService.continueWindowLayout();}return effects;}

Task事务处理

接WindowOrganizerController.java中
private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId, @Nullable Transition transition, @NonNull CallerInfo caller, @Nullable Transition finishTransition)

Task区域大小bounds变化处理

int containerEffect = applyWindowContainerChange(wc, entry.getValue(),t.getErrorCallbackToken());

Task相关操作处理

该方法中applyHierarchyOp方法对象task操作进行相关处理

effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,isInLockTaskMode, caller, t.getErrorCallbackToken(),t.getTaskFragmentOrganizer(), finishTransition);

applyHierarchyOp方法中对task有很多种不同的操作,这里我们主要来看Task的启动、移除、重排序和重定向。
下面结合WindowContainerTransaction侧的构建的层级结构和applyHierarchyOp侧的实现来说明。

启动Task

WindowContainerTransaction

代码路径:frameworks/base/core/java/android/window/WindowContainerTransaction.java

    /*** Starts a task by id. The task is expected to already exist (eg. as a recent task).* @param taskId Id of task to start.* @param options bundle containing ActivityOptions for the task's top activity.* @hide*/@NonNullpublic WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) {mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options));return this;}/*** Holds information about a reparent/reorder operation in the hierarchy. This is separate from* Changes because they must be executed in the same order that they are added.* @hide*/public static final class HierarchyOp implements Parcelable {    	....../** Create a hierarchy op for launching a task. */public static HierarchyOp createForTaskLaunch(int taskId, @Nullable Bundle options) {final Bundle fullOptions = options == null ? new Bundle() : options;fullOptions.putInt(LAUNCH_KEY_TASK_ID, taskId);return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_LAUNCH_TASK).setToTop(true).setLaunchOptions(fullOptions).build();}
applyHierarchyOp

代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java

    private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,int syncId, @Nullable Transition transition, boolean isInLockTaskMode,@NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken,@Nullable ITaskFragmentOrganizer organizer, @Nullable Transition finishTransition) {final int type = hop.getType();switch (type) {......case HIERARCHY_OP_TYPE_LAUNCH_TASK: {mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,"launchTask HierarchyOp");final Bundle launchOpts = hop.getLaunchOptions();final int taskId = launchOpts.getInt(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);final SafeActivityOptions safeOptions =SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid);waitAsyncStart(() -> mService.mTaskSupervisor.startActivityFromRecents(caller.mPid, caller.mUid, taskId, safeOptions));break;}......}return effects;}

移除Task

WindowContainerTransaction

代码路径:frameworks/base/core/java/android/window/WindowContainerTransaction.java

    /*** Finds and removes a task and its children using its container token. The task is removed* from recents.* @param containerToken ContainerToken of Task to be removed*/@NonNullpublic WindowContainerTransaction removeTask(@NonNull WindowContainerToken containerToken) {mHierarchyOps.add(HierarchyOp.createForRemoveTask(containerToken.asBinder()));return this;}/*** Holds information about a reparent/reorder operation in the hierarchy. This is separate from* Changes because they must be executed in the same order that they are added.* @hide*/public static final class HierarchyOp implements Parcelable {....../** create a hierarchy op for deleting a task **/public static HierarchyOp createForRemoveTask(@NonNull IBinder container) {return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REMOVE_TASK).setContainer(container).build();}
applyHierarchyOp

代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java

    private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,int syncId, @Nullable Transition transition, boolean isInLockTaskMode,@NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken,@Nullable ITaskFragmentOrganizer organizer, @Nullable Transition finishTransition) {final int type = hop.getType();switch (type) {case HIERARCHY_OP_TYPE_REMOVE_TASK: {final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());if (wc == null || wc.asTask() == null || !wc.isAttached()) {Slog.e(TAG, "Attempt to remove invalid task: " + wc);break;}final Task task = wc.asTask();task.remove(true, "Applying remove task Hierarchy Op");break;}......}return effects;}

Task重定向和重排序

WindowContainerTransaction

代码路径:frameworks/base/core/java/android/window/WindowContainerTransaction.java

    /*** Reparents a container into another one. The effect of a {@code null} parent can vary. For* example, reparenting a stack to {@code null} will reparent it to its display.** @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to*              the bottom.*/@NonNullpublic WindowContainerTransaction reparent(@NonNull WindowContainerToken child,@Nullable WindowContainerToken parent, boolean onTop) {mHierarchyOps.add(HierarchyOp.createForReparent(child.asBinder(),parent == null ? null : parent.asBinder(),onTop));return this;}/*** Reorders a container within its parent.** @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to*              the bottom.*/@NonNullpublic WindowContainerTransaction reorder(@NonNull WindowContainerToken child, boolean onTop) {mHierarchyOps.add(HierarchyOp.createForReorder(child.asBinder(), onTop));return this;}/*** Holds information about a reparent/reorder operation in the hierarchy. This is separate from* Changes because they must be executed in the same order that they are added.* @hide*/public static final class HierarchyOp implements Parcelable {......public static HierarchyOp createForReparent(@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT).setContainer(container).setReparentContainer(reparent).setToTop(toTop).build();}public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) {return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REORDER).setContainer(container).setReparentContainer(container).setToTop(toTop).build();}
applyHierarchyOp

代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java

    private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,int syncId, @Nullable Transition transition, boolean isInLockTaskMode,@NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken,@Nullable ITaskFragmentOrganizer organizer, @Nullable Transition finishTransition) {final int type = hop.getType();switch (type) {......case HIERARCHY_OP_TYPE_REORDER:case HIERARCHY_OP_TYPE_REPARENT: {final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());if (wc == null || !wc.isAttached()) {Slog.e(TAG, "Attempt to operate on detached container: " + wc);break;}// There is no use case to ask the reparent operation in lock-task mode now, so keep// skipping this operation as usual.if (isInLockTaskMode && type == HIERARCHY_OP_TYPE_REPARENT) {Slog.w(TAG, "Skip applying hierarchy operation " + hop+ " while in lock task mode");break;}if (isLockTaskModeViolation(wc.getParent(), wc.asTask(), isInLockTaskMode)) {break;}if (syncId >= 0) {addToSyncSet(syncId, wc);}if (transition != null) {transition.collect(wc);if (hop.isReparent()) {if (wc.getParent() != null) {// Collect the current parent. It's visibility may change as// a result of this reparenting.transition.collect(wc.getParent());}if (hop.getNewParent() != null) {final WindowContainer parentWc =WindowContainer.fromBinder(hop.getNewParent());if (parentWc == null) {Slog.e(TAG, "Can't resolve parent window from token");break;}transition.collect(parentWc);}}}effects |= sanitizeAndApplyHierarchyOp(wc, hop);break;}......}return effects;}

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

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

相关文章

C语言初阶【13】——打印一个数的每一位(递归和非递归实现)

1. 题目 打印一个数的每一位 2.分析 首先先实现非递归方式&#xff0c; 以123为例。我们要获取它的每一位&#xff0c; 获取个位数&#xff1a;123 %10 3 获取十位数&#xff1a;123/10 12 之后在 12%10 2&#xff1b; 获取百位数&#xff1a;12/10 1 之后再1%10 1&#x…

webrtc学习----前端推流拉流,局域网socket版,一对多

提示&#xff1a;局域网socket版&#xff0c;一对多 文章目录 [TOC](文章目录) 前言一、教程二、webrtc工作流程三、推流端四、拉流五、socket服务六、效果七、备注总结 前言 WebRTC&#xff08;Web Real-Time Communication&#xff09;是一种实时通讯技术&#xff0c;允许网…

深入探讨 Go 中的高级表单验证与翻译:Gin 与 Validator 的实践之道20241223

深入探讨 Go 中的高级表单验证与翻译&#xff1a;Gin 与 Validator 的实践之道 在现代后端开发中&#xff0c;表单验证是保证数据完整性和服务稳定性的核心环节。如何优雅、高效地实现表单验证&#xff0c;同时提供人性化的错误提示&#xff0c;是每位开发者的必修课。在本文中…

单机游戏《野狗子》游戏运行时提示dbghelp.dll缺失是什么原因?dbghelp.dll缺失要怎么解决?

《野狗子》游戏运行时提示dbghelp.dll缺失&#xff1a;原因与解决方案 在畅游《野狗子》这款引人入胜的游戏世界时&#xff0c;突然遭遇“dbghelp.dll缺失”的错误提示&#xff0c;无疑会给玩家的探险之旅蒙上一层阴影。作为一名深耕软件开发领域的从业者&#xff0c;我深知此…

Unity复刻胡闹厨房复盘 模块一 新输入系统订阅链与重绑定

本文仅作学习交流&#xff0c;不做任何商业用途 郑重感谢siki老师的汉化教程与代码猴的免费教程以及搬运烤肉的小伙伴 版本&#xff1a;Unity6 模板&#xff1a;3D 核心 渲染管线&#xff1a;URP ------------------------------…

Flutter 异步编程简述

1、isolate 机制 1.1 基本使用 Dart 是基于单线程模型的语言。但是在开发当中我们经常会进行耗时操作比如网络请求&#xff0c;这种耗时操作会堵塞我们的代码。因此 Dart 也有并发机制 —— isolate。APP 的启动入口main函数就是一个类似 Android 主线程的一个主 isolate。与…

一键打断线(根据相交点打断)——CAD c# 二次开发

多条相交线根据交点一键打断&#xff0c;如下图&#xff1a; 部分代码如下: finally namespace IFoxDemo; public class Class1 {[CommandMethod("ddx")]public static void Demo(){//"ifox可以了".Print();Database db HostApplicationServices.Workin…

Confluent Cloud Kafka 可观测性最佳实践

Confluent Cloud 介绍 Confluent Cloud 是一个完全托管的 Apache Kafka 服务&#xff0c;提供高可用性和可扩展性&#xff0c;旨在简化数据流处理和实时数据集成。用户可以轻松创建和管理 Kafka 集群&#xff0c;而无需担心基础设施的维护和管理。Confluent Cloud 支持多种数据…

【C++】B2066救援题目分析和解决讲解

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af; 题目&#x1f4af; 题目分析每个屋顶计算的元素 &#x1f4af; 思路解析1. **读取输入**2. **计算屋顶时间**3. **结果精确取整** &#x1f4af; 完整解决代码&#x1f4a…

WPS工具栏灰色怎么办

WPS离线不登录&#xff0c;开启工具栏等相关功能 当你在使用WPS的过程中&#xff0c;若因网络问题或其他特殊原因&#xff0c;导致无法登录使用WPS时&#xff0c;可根据以下步骤开启离线兼容模式&#xff0c;开启此模式后&#xff0c;可在未登录的状态下&#xff0c;激活并使用…

反射探针.

一、在unity场景中如何添加反射探针&#xff1f; 可以先添加一个空对象&#xff0c;在空对象的上方添加反射探针组件&#xff08;Reflection Probe&#xff09; 反射探针的类型有&#xff1a;Baked、Custom、Realtime 其中“Baked”反射探针类型&#xff0c;可以将场景中的静态…

SecureCRT汉化版

目录 9.5.1版 8.1.4版 下载链接 SecureCRT 和 SecureFX 是由 VanDyke Software 开发的专业工具&#xff0c;分别专注于安全的终端仿真与文件传输。SecureCRT 提供高效的终端仿真和多协议支持&#xff0c;是网络管理和系统配置的首选工具&#xff1b;SecureFX 则致力于安全的…

回归预测 | MATLAB实现CNN-LSSVM卷积神经网络结合最小二乘支持向量机多输入单输出回归预测

回归预测 | MATLAB实现CNN-LSSVM卷积神经网络结合最小二乘支持向量机多输入单输出回归预测 目录 回归预测 | MATLAB实现CNN-LSSVM卷积神经网络结合最小二乘支持向量机多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 回归预测 | MATLAB实现CNN-LSSVM…

使用Vue的props进行组件传递校验时出现 Extraneous non-props attributes的解决方案

作者&#xff1a;CSDN-PleaSure乐事 欢迎大家阅读我的博客 希望大家喜欢 使用环境&#xff1a;WebStorm 目录 出现错误的情况 报错&#xff1a; 代码&#xff1a; 报错截图 原因分析 解决方案 方法一 方法二 出现错误的情况 以下是我遇到该错误时遇到的报错和代码&…

【知识】cuda检测GPU是否支持P2P通信及一些注意事项

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 代码流程 先检查所有GPU之间是否支持P2P通信&#xff1b;然后尝试启用GPU之间的P2P通信&#xff1b;再次检查所有GPU之间是否支持P2P通信。 test.cu&…

专栏二十三:Python读取和分析空间数据的经验杂谈

部分情况同样适合单细胞的分析结果 读取数据阶段 1.错误的library_id 包括sc和sq的两种读取方式&#xff0c;大同小异。 理论上有h5数据和spatial文件夹就可以读取成功&#xff0c;并且自动赋予和文件名一样的library_id&#xff0c;例如 slide sq.read.visium("/ho…

《软件设计的哲学》阅读摘要之设计原则

《软件设计的哲学》&#xff08;A Philosophy of Software Design&#xff09;是一本在软件架构与设计领域颇具影响力的书籍&#xff0c;作者 John Ousterhout 在书中分享了诸多深刻且实用的软件设计理念。书中列举的这些设计原则&#xff0c;汇聚了作者丰富的实战经验与深邃的…

Centos7.9安装openldap+phpldapadmin+grafana配置LDAP登录最详细步骤 亲测100%能行

一、部署LDAP 1、安装LDAP yum install -y openldap-servers openldap-clients openldap openldap-devel compat-openldap openldap-servers-sql systemctl start slapd systemctl enable slapd2、创建第一个管理账号密码&#xff08;设置为ldapadmin&#xff09; slappass…

【MySQL基础篇】多表查询(隐式/显式内连接、左/右外连接、自连接查询、联合查询、标量/列/行/表子查询)

Hiヽ(゜▽゜ )&#xff0d;欢迎来到蓝染Aizen的CSDN博客~ &#x1f525; 博客主页&#xff1a; 【✨蓝染 の Blog&#x1f618;】 &#x1f496;感谢大家点赞&#x1f44d; 收藏⭐ 评论✍ 文章目录 MySQL基础篇-多表查询一、多表关系1. 一对多2. 多对多3. 一对一 二、多表查询…

【踩坑记录】C编程变量未初始化导致的程序异常

1、在编程的时候养成良好的习惯&#xff0c;定义变量以后记得给变量初始化&#xff0c;不然可能会产生一些意想不到的Bug。 2、比如下面的例子&#xff0c;如果定义的变量没有被初始化就有可能是一个随机值。如果代码少还好&#xff0c;很容易排查出来。但如果是一个比较大的项…