Android 12系統源碼_分屏模式(一)從最近任務觸發分屏模式

前言

打開MainActivity,然后進入最近任務觸發分屏,可以成功進入分屏模式。在這里插入圖片描述
本篇文章我們來具體梳理一下這個過程的源碼調用流程。

一 launcher3階段

1.1 源碼

//packages/apps/Launcher3/quickstep/src/com/android/quickstep/views/TaskView.java
public class TaskView extends FrameLayout implements Reusable {private void onClick(View view) {if (getTask() == null) {return;}if (confirmSecondSplitSelectApp()) {return;}launchTasks();...代碼省略...}private boolean confirmSecondSplitSelectApp() {boolean isSelectingSecondSplitApp = getRecentsView().isSplitSelectionActive();if (isSelectingSecondSplitApp) {//調用RecentsView的confirmSplitSelect方法getRecentsView().confirmSplitSelect(this);}return isSelectingSecondSplitApp;}
}//packages/apps/Launcher3/quickstep/src/com/android/quickstep/views/RecentsView.java
public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>,STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,TaskVisualsChangeListener {protected SplitSelectStateController mSplitSelectStateController;public void confirmSplitSelect(TaskView taskView) {...代碼省略...pendingAnimation.addEndListener(aBoolean -> {pendingAnimation.addEndListener(aBoolean ->//待動畫結束,執行SplitSelectStateController的setSecondTaskId方法mSplitSelectStateController.setSecondTaskId(taskView.getTask(),aBoolean1 -> RecentsView.this.resetFromSplitSelectionState()));  ...代碼省略...}
}//packages/apps/Launcher3/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
public class SplitSelectStateController {public void setSecondTaskId(Task task, Consumer<Boolean> callback) {mSecondTask = task;launchTasks(mInitialTask, mSecondTask, mStagePosition, callback,false /* freezeTaskList */, DEFAULT_SPLIT_RATIO);}private final SystemUiProxy mSystemUiProxy;public void launchTasks(Task task1, Task task2, @StagePosition int stagePosition,Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {...代碼省略...mSystemUiProxy.startTasksWithLegacyTransition(taskIds[0], mainOpts.toBundle(),taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,splitRatio, adapter);    ...代碼省略...}}//packages/apps/Launcher3/quickstep/src/com/android/quickstep/SystemUiProxy.java
public class SystemUiProxy implements ISystemUiProxy, NavHandle {private ISplitScreen mSplitScreen;public void startTasksWithLegacyTransition(int mainTaskId, Bundle mainOptions, int sideTaskId,Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition,float splitRatio, RemoteAnimationAdapter adapter) {if (mSystemUiProxy != null) {try {mSplitScreen.startTasksWithLegacyTransition(mainTaskId, mainOptions, sideTaskId,sideOptions, sidePosition, splitRatio, adapter);} catch (RemoteException e) {Log.w(TAG, "Failed call startTasksWithLegacyTransition");}}}
}

1.2 時序圖

時序圖

二 SystemUI階段

2.1 源碼

public class SplitScreenController implements DragAndDropPolicy.Starter,RemoteCallable<SplitScreenController> {private static class ISplitScreenImpl extends ISplitScreen.Stub {@Overridepublic void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,float splitRatio, RemoteAnimationAdapter adapter) {executeRemoteCallWithTaskPermission(mController, "startTasks",(controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,splitRatio, adapter));}}
}class StageCoordinator implements SplitLayout.SplitLayoutHandler,RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler {private final ShellTaskOrganizer mTaskOrganizer;void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,float splitRatio, RemoteAnimationAdapter adapter) {//設置分割線的可見性setDividerVisibility(false /* visible */);//初始化分屏的分割線的布局mSplitLayout.init();// Set false to avoid record new bounds with old task still on top;mShouldUpdateRecents = false;final WindowContainerTransaction wct = new WindowContainerTransaction();final WindowContainerTransaction evictWct = new WindowContainerTransaction();prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct);prepareEvictChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, evictWct);//創建一個遠程動畫的回調binder對象IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {@Overridepublic void onAnimationStart(@WindowManager.TransitionOldType int transit,RemoteAnimationTarget[] apps,RemoteAnimationTarget[] wallpapers,RemoteAnimationTarget[] nonApps,final IRemoteAnimationFinishedCallback finishedCallback) {...代碼省略...}@Overridepublic void onAnimationCancelled() {...代碼省略...}};//創建RemoteAnimationAdapter類型的遠程動畫RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());if (mainOptions == null) {//構建出對應的mainOptions,及上分屏的啟動optionmainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();} else {ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));mainOptions = mainActivityOptions.toBundle();}//準備好對應的sideOptions,下分屏的optionsideOptions = sideOptions != null ? sideOptions : new Bundle();setSideStagePosition(sidePosition, wct);//設置分界線比例mSplitLayout.setDivideRatio(splitRatio);if (mMainStage.isActive()) {mMainStage.moveToTop(getMainStageBounds(), wct);} else {// Build a request WCT that will launch both apps such that task 0 is on the main stage// while task 1 is on the side stage.mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);}mSideStage.moveToTop(getSideStageBounds(), wct);//準備好對應的option參數// Make sure the launch options will put tasks in the corresponding split rootsaddActivityOptions(mainOptions, mMainStage);addActivityOptions(sideOptions, mSideStage);// Add task launch requestswct.startTask(mainTaskId, mainOptions);//主分屏taskwct.startTask(sideTaskId, sideOptions);//次分屏task//最后把前面準備好的參數統一apply到SystemServer里面mTaskOrganizer.applyTransaction(wct);}
}

2.2 時序圖

時序圖

三 SystemServer階段

3.1 源碼

//frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java
public class ShellTaskOrganizer extends TaskOrganizer implements CompatUIController.CompatUICallback {//父類方法public void applyTransaction(@NonNull WindowContainerTransaction t) {try {if (!t.isEmpty()) {//調用ActivityTaskManagerService的getWindowOrganizerController方法得到WindowOrganizerController對象//并調用WindowOrganizerController對象的applyTransaction方法getWindowOrganizerController().applyTransaction(t);}} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}static IWindowOrganizerController getWindowOrganizerController() {return IWindowOrganizerControllerSingleton.get();}private static final Singleton<IWindowOrganizerController> IWindowOrganizerControllerSingleton =new Singleton<IWindowOrganizerController>() {@Overrideprotected IWindowOrganizerController create() {try {return ActivityTaskManager.getService().getWindowOrganizerController();} catch (RemoteException e) {return null;}}};
}public class ActivityTaskManagerService extends IActivityTaskManager.Stub {WindowOrganizerController mWindowOrganizerController;@Overridepublic IWindowOrganizerController getWindowOrganizerController() {return mWindowOrganizerController;}
}//base/services/core/java/com/android/server/wm/WindowOrganizerController.java
class WindowOrganizerController extends IWindowOrganizerController.Stub {@Overridepublic void applyTransaction(WindowContainerTransaction t) {...代碼省略...applyTransaction(t, -1 /*syncId*/, null /*transition*/, caller);...代碼省略...}private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,@Nullable Transition transition, @NonNull CallerInfo caller) {...代碼省略...ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();//獲取WindowContainerTransaction的change部分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);//注釋1,這里一般就是前面說過的上屏和下屏的對應Task,調用applyWindowContainerChange方法進行對應處理int containerEffect = applyWindowContainerChange(wc, entry.getValue());effects |= containerEffect;// Lifecycle changes will trigger ensureConfig for everything.if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0&& (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {haveConfigChanges.add(wc);}}// Hierarchy changesfinal List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();final int hopSize = hops.size();if (hopSize > 0) {final boolean isInLockTaskMode = mService.isInLockTaskMode();for (int i = 0; i < hopSize; ++i) {//注釋2,調用applyHierarchyOp方法,對reorder和starTask的操作進行處理effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,isInLockTaskMode, caller, t.getErrorCallbackToken(),t.getTaskFragmentOrganizer());}} ...代碼省略...}private int applyWindowContainerChange(WindowContainer wc, WindowContainerTransaction.Change c) {sanitizeWindowContainer(wc);//調用applyChangesint effects = applyChanges(wc, c);if (wc instanceof DisplayArea) {effects |= applyDisplayAreaChanges(wc.asDisplayArea(), c);} else if (wc instanceof Task) {effects |= applyTaskChanges(wc.asTask(), c);}return effects;}private int applyChanges(WindowContainer container, WindowContainerTransaction.Change change) {...代碼省略...//最重要就是獲取change的configration,因為bounds變化被包在了configration里面,//這里再調用container的進行通知configration的變化final Configuration c =new Configuration(container.getRequestedOverrideConfiguration());c.setTo(change.getConfiguration(), configMask, windowMask);//Task容器調用這個onRequestedOverrideConfigurationChanged,//代表根據傳遞過來的Configration作為自己的覆蓋變化,即也就把對應的bounds設置給了Taskcontainer.onRequestedOverrideConfigurationChanged(c);...代碼省略...}
}

調用applyHierarchyOp方法將task放到最上層

class WindowOrganizerController extends IWindowOrganizerController.Stub {//HIERARCHY_OP_TYPE_REORDER和HIERARCHY_OP_TYPE_REPARENT類型private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,int syncId, @Nullable Transition transition, boolean isInLockTaskMode,@NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken,@Nullable ITaskFragmentOrganizer organizer) {final int type = hop.getType();...代碼省略...switch (type) {...代碼省略...case HIERARCHY_OP_TYPE_REORDER:case HIERARCHY_OP_TYPE_REPARENT: {//首先要從hop中獲取出WindowContainerfinal WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());...代碼省略...//調用sanitizeAndApplyHierarchyOp進行處理effects |= sanitizeAndApplyHierarchyOp(wc, hop);break;}case HIERARCHY_OP_TYPE_LAUNCH_TASK: {...代碼省略...return effects;}private int sanitizeAndApplyHierarchyOp (WindowContainer container,WindowContainerTransaction.HierarchyOp hop){//這里獲取task,其實就是RootTask,分屏最頂端那個taskId為4的final Task task = container.asTask();...代碼省略...//需要把task進行移動放到所有task的頂端位置task.getParent().positionChildAt(hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,task, false /* includingParents */);...代碼省略...return TRANSACT_EFFECTS_LIFECYCLE;}}

調用applyHierarchyOp方法對數據進行拆解,然后調用ActivityTaskSupervisor的startActivityFromRecents方法:

class WindowOrganizerController extends IWindowOrganizerController.Stub {//HIERARCHY_OP_TYPE_REORDER和HIERARCHY_OP_TYPE_REPARENT類型private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,int syncId, @Nullable Transition transition, boolean isInLockTaskMode,@NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken,@Nullable ITaskFragmentOrganizer organizer) {final int type = hop.getType();...代碼省略...switch (type) {...代碼省略...case HIERARCHY_OP_TYPE_REORDER:case HIERARCHY_OP_TYPE_REPARENT: ...代碼省略...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);final Integer[] starterResult = {null};// startActivityFromRecents should not be called in lock.mService.mH.post(() -> {try {//調用ActivityTaskSupervisor的startActivityFromRecents方法starterResult[0] = mService.mTaskSupervisor.startActivityFromRecents(caller.mPid, caller.mUid, taskId, safeOptions);} catch (Throwable t) {starterResult[0] = ActivityManager.START_CANCELED;Slog.w(TAG, t);}synchronized (mGlobalLock) {mGlobalLock.notifyAll();}});while (starterResult[0] == null) {try {mGlobalLock.wait();} catch (InterruptedException ignored) {}}break;}...代碼省略...return effects;}}
}
//base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
public class ActivityTaskSupervisor implements RecentTasks.Callbacks {RootWindowContainer mRootWindowContainer;final ActivityTaskManagerService mService;int startActivityFromRecents(int callingPid, int callingUid, int taskId,SafeActivityOptions options) {final Task task;final int taskCallingUid;final String callingPackage;final String callingFeatureId;final Intent intent;final int userId;final ActivityOptions activityOptions = options != null? options.getOptions(this): null;boolean moveHomeTaskForward = true;synchronized (mService.mGlobalLock) {int activityType = ACTIVITY_TYPE_UNDEFINED;if (activityOptions != null) {activityType = activityOptions.getLaunchActivityType();...代碼省略...//這里主要通過taskId來獲取一個Task,但是這個方法不僅僅只干了獲取task的事情,//還干了把taskId對應的Task進行reparent到新上下分屏的容器Task,這樣實現了層級結構樹上面的掛載完成,//剩下就是一系列操作來保證Activiyt生命周期正常相關task = mRootWindowContainer.anyTaskForId(taskId,MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);...代碼省略...if (!mService.mAmInternal.shouldConfirmCredentials(task.mUserId)&& task.getRootActivity() != null) {//獲取task的Activityfinal ActivityRecord targetActivity = task.getTopNonFinishingActivity();...代碼省略...//調用mService.moveTaskToFrontLocked(null /* appThread */,null /* callingPackage */, task.mTaskId, 0, options);...代碼省略...}}}}}
}

RootWindowContainer的anyTaskForId方法

class RootWindowContainer extends WindowContainer<DisplayContent>implements DisplayManager.DisplayListener {Task anyTaskForId(int id, @RootWindowContainer.AnyTaskForIdMatchTaskMode int matchMode,@Nullable ActivityOptions aOptions, boolean onTop) {final PooledPredicate p = PooledLambda.obtainPredicate(Task::isTaskId, PooledLambda.__(Task.class), id);Task task = getTask(p);//遍歷獲取Taskp.recycle();if (task != null) {if (aOptions != null) {//注意這里aOptions不為null,而且攜帶了task//這里有調用了getOrCreateRootTask來獲取targetRootTaskfinal Task targetRootTask =getOrCreateRootTask(null, aOptions, task, onTop);if (targetRootTask != null && task.getRootTask() != targetRootTask) {final int reparentMode = onTop? REPARENT_MOVE_ROOT_TASK_TO_FRONT : REPARENT_LEAVE_ROOT_TASK_IN_PLACE;task.reparent(targetRootTask, onTop, reparentMode, ANIMATE, DEFER_RESUME,"anyTaskForId");}}return task;}//省略}Task getOrCreateRootTask(@Nullable ActivityRecord r,@Nullable ActivityOptions options, @Nullable Task candidateTask,@Nullable Task sourceTask, boolean onTop,@Nullable LaunchParamsController.LaunchParams launchParams, int launchFlags) {// First preference goes to the launch root task set in the activity options.if (options != null) {//這里終于體現systemui傳遞的mainoptions作用了final Task candidateRoot = Task.fromWindowContainerToken(options.getLaunchRootTask());if (candidateRoot != null && canLaunchOnDisplay(r, candidateRoot)) {return candidateRoot;//大家看這里就直接返回了options帶的task}}//省略}}

ActivityTaskManagerService的moveTaskToFrontLocked方法

public class ActivityTaskManagerService extends IActivityTaskManager.Stub {void moveTaskToFrontLocked(@Nullable IApplicationThread appThread,@Nullable String callingPackage, int taskId, int flags, SafeActivityOptions options) {//省略try {final Task task = mRootWindowContainer.anyTaskForId(taskId);//省略ActivityOptions realOptions = options != null? options.getOptions(mTaskSupervisor): null;//這方法最為關鍵,尋找到task而且移到最前端mTaskSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront",false /* forceNonResizable */);//開始啟動StartingWindowfinal ActivityRecord topActivity = task.getTopNonFinishingActivity();if (topActivity != null) {// We are reshowing a task, use a starting window to hide the initial draw delay// so the transition can start earlier.topActivity.showStartingWindow(true /* taskSwitch */);}//省略}}
}

findTaskToMoveToFront方法

public class ActivityTaskManagerService extends IActivityTaskManager.Stub {/** This doesn't just find a task, it also moves the task to front. */void findTaskToMoveToFront(Task task, int flags, ActivityOptions options, String reason,boolean forceNonResizeable) {//這里currentRootTask就是taskId =4的根taskTask currentRootTask = task.getRootTask();//省略final ActivityRecord r = task.getTopNonFinishingActivity();//這里又調用到了關鍵moveTaskToFront方法currentRootTask.moveTaskToFront(task, false /* noAnimation */, options,r == null ? null : r.appTimeTracker, reason);//省略}final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,AppTimeTracker timeTracker, boolean deferResume, String reason) {//省略//這里又關鍵調用到了頂部ActivityRecord的moveFocusableActivityToTop方法// Set focus to the top running activity of this task and move all its parents to top.top.moveFocusableActivityToTop(reason);//省略if (!deferResume) {//進行對應resume操作mRootWindowContainer.resumeFocusedTasksTopActivities();}//省略}boolean moveFocusableActivityToTop(String reason) {final Task rootTask = getRootTask();//這里調用了rootTask把當前app的task移到最前rootTask.moveToFront(reason, task);// Report top activity change to tracking services and WMif (mRootWindowContainer.getTopResumedActivity() == this) { //注意這里可能大家有疑問為啥都可以getTopResumedActivity到了,還需要設置,那是因為getTopResumedActivity可能真正ResumedActivity為null,但是會通過獲取getFocusedActivity獲取作為ResumedActivity//這個操作關鍵,把ActivityRecord開始要變成Resumed狀態了,這個就不展開,前面課程視頻講解mAtmService.setResumedActivityUncheckLocked(this, reason);}return true;}@NullableActivityRecord getTopResumedActivity() {final Task focusedRootTask = getTopDisplayFocusedRootTask();//getTopResumedActivity這個時候是為null哦final ActivityRecord resumedActivity = focusedRootTask.getTopResumedActivity();if (resumedActivity != null && resumedActivity.app != null) {return resumedActivity;}// The top focused root task might not have a resumed activity yet - look on all displays in// focus order.//前面發現為null后就獲取getFocusedActivityreturn getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedActivity);}
}

3.2 時序圖

時序圖

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/88494.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/88494.shtml
英文地址,請注明出處:http://en.pswp.cn/web/88494.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Flask 入門教程:用 Python 快速搭建你的第一個 Web 應用

文章目錄前言一、什么是 Flask&#xff1f;&#x1f4cc; Flask 的優勢1. 輕量靈活2. 易于上手3. 可擴展性強4. 自由度高5. 社區活躍&#xff0c;資料豐富Flask 主要用來做什么&#xff1f;二、Flask快速入門1.創建一個Flask項目2.開啟debug&#xff0c;修改host&#xff0c;端…

實習第一個小需求樣式問題總結

Vue2 vxe-table Element UI 表頭下拉詳情實現總結一、核心功能實現表頭下拉按鈕交互初始嘗試 expand-change 事件無法滿足需求&#xff0c;改用 vxe-table 的 toggle-row-expand 事件&#xff1a;<vxe-table toggle-row-expand"handleExpandChange"><temp…

Linux中LVM邏輯卷擴容

在Linux系統中對根目錄所在的LVM邏輯卷進行擴容&#xff0c;需要依次完成 物理卷擴容 ? 卷組擴容 ? 邏輯卷擴容 ? 文件系統擴容 四個步驟。以下是詳細操作流程&#xff1a;一、確認當前磁盤和LVM狀態# 1. 查看磁盤空間使用情況 df -h /# 2. 查看塊設備及LVM層級關系 lsblk# …

微軟365 PDF導出功能存在本地文件包含漏洞,可泄露敏感服務器數據

微軟365的"導出為PDF"功能近期被發現存在嚴重的本地文件包含(Local File Inclusion, LFI)漏洞&#xff0c;攻擊者可利用該漏洞獲取服務器端的敏感數據&#xff0c;包括配置文件、數據庫憑證和應用程序源代碼。該漏洞由安全研究員Gianluca Baldi發現并報告給微軟&…

臺球 PCOL:極致物理還原的網頁斯諾克引擎(附源碼深度解析)

> 無需下載,打開瀏覽器即可體驗專業級斯諾克!本文將揭秘網頁版臺球游戲的物理引擎與渲染核心技術 在游戲開發領域,臺球物理模擬一直被視為**剛體動力學皇冠上的明珠**。今天我們要解析的**臺球 PCOL**(Pure Canvas Online Billiards)正是一款突破性的網頁版斯諾克游戲…

springboot-2.3.3.RELEASE升級2.7.16,swagger2.9.2升級3.0.0過程

一、pom文件版本修改<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.16</version><relativePath/> </parent>如果用到了“spring-boot-starter…

Python-正則表達式-信息提取-滑動窗口-數據分發-文件加載及分析器-瀏覽器分析-學習筆記

序 欠4前年的一份筆記 &#xff0c;獻給今后的自己。 正則表達式 概述 正則表達式&#xff0c;Regular Expression&#xff0c;縮寫為regex、regexp、RE等。 正則表達式是文本處理極為重要的技術&#xff0c;用它可以對字符串按照某種規則進行檢索、替換。 1970年代&…

一文入門神經網絡:神經網絡概念初識

神經網絡的世界遠比你想象得更豐富多元。從基礎架構到前沿融合模型&#xff0c;我為你梳理了當前最值得關注的神經網絡類型&#xff0c;不僅包括那些“教科書級”的經典模型&#xff0c;也覆蓋了正在改變行業格局的新興架構。以下是系統分類與核心特點總結&#xff1a;一、基礎…

線上事故處理記錄

線上事故處理記錄 一、MySQL 導致的服務器 CPU 飆升 有一天&#xff0c;突然收到了服務器 CPU 飆升的告警信息&#xff0c;打開普羅米修斯查看 CPU 的使用情況&#xff0c;發現 CPU 確實飆升了&#xff0c;下面開始去進行問題定位了。 1. 首先連接到對應的服務器&#xff0c;然…

ParaCAD 筆記 png 圖紙標注數據集

ParaCAD-Dataset git lfs install git clone https://www.modelscope.cn/datasets/yuwenbonnie/ParaCAD-Dataset.git https://github.com/ParaCAD/ 不止100g 下個最小的 沒有三視圖

C#使用Semantic Kernel實現Embedding功能

1、背景 C#開發中&#xff0c;可以通過Semantic Kernel實現本地模型的調用和實現。 本地的Ollama的版本如下&#xff1a;安裝的Package如下&#xff1a;2、代碼實現 // See https://aka.ms/new-console-template for more information using Microsoft.Extensions.AI; using Mi…

轉轉APP逆向

APP版本 11.15.0 接口分析 # URL https://app.zhuanzhuan.com/zz/transfer/search# header cookie xxx x-zz-monitoring-metrics feMetricAntiCheatLevelV1 zztk user-agent Zhuan/11.15.0 (11015000) Dalvik/2.1.0 (Linux; U; Android 10; Pixel 3 Build/QQ3A.200805.001) z…

注解與反射的完美配合:Java中的聲明式編程實踐

注解與反射的完美配合&#xff1a;Java中的聲明式編程實踐 目錄 引言 核心概念 工作機制 實戰示例 傳統方式的痛點 注解反射的優勢 實際應用場景 最佳實踐 總結 引言 在現代Java開發中&#xff0c;我們經常看到這樣的代碼&#xff1a; Range(min 1, max 50)priva…

開源入侵防御系統——CrowdSec

1、簡介 CrowdSec 是一款現代化、開源、基于行為的入侵防御系統&#xff08;IDS/IPS&#xff09;&#xff0c;專為保護服務器、服務、容器、云原生應用而設計。它通過分析日志檢測可疑行為&#xff0c;并可基于社區協作共享惡意 IP 黑名單&#xff0c;從而實現分布式防御。 其…

imx6ull-裸機學習實驗13——串口格式化函數移植實驗

目錄 前言 格式化函數 實驗程序編寫 stdio文件夾 main.c Makefile修改 編譯下載 前言 在學習實驗12&#xff1a;imx6ull串口通信實驗&#xff0c;我們實現了 UART1 基本的數據收發功能&#xff0c;雖然可以用來調試程序&#xff0c;但是功能太單一了&#xff0c;只能輸出…

CCF-GESP 等級考試 2025年6月認證C++三級真題解析

1 單選題&#xff08;每題 2 分&#xff0c;共 30 分&#xff09;第1題 8位二進制原碼能表示的最小整數是&#xff1a;&#xff08; &#xff09;A. -127 B. -128 C. -255 …

【網絡安全】服務間身份認證與授權模式

未經許可,不得轉載。 文章目錄 問題背景用戶到服務的身份認證與授權系統對系統的通信服務與服務之間的通信需求分析Basic Auth(基本身份認證)優點缺點mTLS 證書認證優點缺點OAuth 2.0優點缺點JWS(JSON Web Signature)優點缺點結合 Open Policy Agent 的 JWS 方案優點缺點結…

【EGSR2025】材質+擴散模型+神經網絡相關論文整理隨筆(四)

An evaluation of SVBRDF Prediction from Generative Image Models for Appearance Modeling of 3D Scenes輸入3D場景的幾何和一張參考圖像&#xff0c;通過擴散模型和SVBRDF預測器獲取多視角的材質maps&#xff0c;這些maps最終合并成場景的紋理地圖集&#xff0c;并支持在任…

Grid網格布局完整功能介紹和示例演示

CSS Grid布局是一種強大的二維布局系統&#xff0c;可以將頁面劃分為行和列&#xff0c;精確控制元素的位置和大小。以下是其完整功能介紹和示例演示&#xff1a; 基本概念 網格容器&#xff08;Grid Container&#xff09;&#xff1a;應用display: grid的元素。網格項&#x…

學習C++、QT---21(QT中QFile庫的QFile讀取文件、寫入文件的講解)

每日一言把大目標拆成小步&#xff0c;每天前進一點點&#xff0c;終會抵達終點。QFile讀取文件我們記事本要進行讀取文件、寫入文件、等等的操作&#xff0c;那么這個時候我們的QT有一個QT類叫做QFile這個類的話是專門對于文件操作的&#xff0c;所以我們來學習我們在QT的幫助…