Android 9系统源码_SystemUI(十)SystemUIVisibility属性

前言

在Android系统中,很多应用都需要根据具体情况来控制状态栏和导航栏的显示和隐藏,又或者将状态栏透明,实现诸如沉浸式、全面屏灯效果,而要实现这些效果,都离不开SystemUIVisibility属性。由于SystemUIVisibilityy属性主要用来控制系统状态栏和导航栏的行为,而状态栏和导航栏都属于SystemUI模块的StatusBar,所以SystemUIVisibility属性的消费者肯定包含StatusBar。另外当状态栏和导航栏发生变化的时候,窗口的布局一般也会跟着发生变化,这就意味着窗口管理者PhoneWindowManager肯定也要消费SystemUIVisibility属性。本篇文章我们就来具体分析一下和这个属性有关的代码。

一、SystemUIVisibility属性常见常见取值

1、为窗口设置SystemUIVisibility属性的方式有两种,一种是直接在窗口的WindowManager.LayoutParams对象的systemUiVisibility属性上进行设置,并通过WindowManager.updateViewLayout()方法使其生效。

frameworks/base/core/java/android/view/WindowManager.java

public interface WindowManager extends ViewManager {public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {//隐藏窗口的所有装饰,比如状态栏和导航栏public static final int FLAG_FULLSCREEN      = 0x00000400;//控制窗口状态栏、导航栏的显示和隐藏public int systemUiVisibility;}
}    

2、另一种是在一个已经显示在窗口上的控件中调用setSystemUiVisibility方法,传入如下属性。

frameworks/base/core/java/android/view/View.java

public class View implements Drawable.Callback, KeyEvent.Callback,AccessibilityEventSource {
//隐藏导航栏
public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002;
//隐藏状态栏
public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;
}

这种方式最终影响的其实是窗口的WindowManager.LayoutParams对象的subtreeSystemUiVisibility属性。

public interface WindowManager extends ViewManager {public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {//控制窗口状态栏、导航栏的显示和隐藏public int subtreeSystemUiVisibility;}
}    

窗口的状态栏导航栏显示与否,最终其实是受以上两个属性共同影响的。接下来我们具体来分析一下View的setSystemUiVisibility方法是如何生效的。

二、View的setSystemUiVisibility方法调用流程

1、View的setSystemUiVisibility方法如下所示。

public class View implements Drawable.Callback, KeyEvent.Callback,AccessibilityEventSource {int mSystemUiVisibility;protected ViewParent mParent;public void setSystemUiVisibility(int visibility) {if (visibility != mSystemUiVisibility) {mSystemUiVisibility = visibility;//保存SystemUIVisibility属性if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {mParent.recomputeViewAttributes(this);//通知父控件子控件属性发生了变化}}}
}

setSystemUiVisibility方法首先将属性赋值给mSystemUiVisibility,然后会调用父控件的recomputeViewAttributes方法,通知父控件子控件属性发生了变化。ViewParent是一个接口,在Android中有两个类实现了这个接口,它们分别是ViewGroup和ViewRootImpl。

2、ViewGroup和ViewRootImpl和recomputeViewAttributes方法相关的代码如下所示。

frameworks/base/core/java/android/view/View.java

public abstract class ViewGroup extends View implements ViewParent, ViewManager {@Overridepublic void recomputeViewAttributes(View child) {if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {ViewParent parent = mParent;if (parent != null) parent.recomputeViewAttributes(this);}}
}

frameworks/base/core/java/android/view/ViewRootImpl.java

public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {final View.AttachInfo mAttachInfo;@Overridepublic void recomputeViewAttributes(View child) {checkThread();//检测线程是不是UI线程if (mView == child) {mAttachInfo.mRecomputeGlobalAttributes = true;//标记需要重新计算本地属性if (!mWillDrawSoon) {scheduleTraversals();//进一步调用scheduleTraversals方法。}}}
}

结合Android 9.0系统源码_窗口管理(二)WindowManager对窗口的管理过程,我们知道包括Activity的跟布局DecorView在内的任何View,WindowManager在将它添加到窗口上的过程中,最终都会创建一个ViewRootImpl,并将View设置给ViewRootImpl,这样根View的父类就变成了ViewRootImpl。这就意味着不管任何子View调用recomputeViewAttributes方法,最终所触发的都是ViewRootImpl的recomputeViewAttributes,而ViewRootImpl会进一步调用scheduleTraversals方法。

3、ViewRootImpl和scheduleTraversals方法相关的代码如下所示。

public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {final Choreographer mChoreographer;//编舞者final TraversalRunnable mTraversalRunnable = new TraversalRunnable();//回调对象void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();//等待系统垂直刷新同步信号,回调TraversalRunnable对象的run方法mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);notifyRendererOfFramePending();pokeDrawLockIfNeeded();}}final class TraversalRunnable implements Runnable {@Overridepublic void run() {doTraversal();//继续执行doTraversal}}void doTraversal() {if (mTraversalScheduled) {mTraversalScheduled = false;mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);if (mProfile) {Debug.startMethodTracing("ViewAncestor");}//执行performTraversalsperformTraversals();if (mProfile) {Debug.stopMethodTracing();mProfile = false;}}}
}

scheduleTraversals方法会为编舞者对象设置回调,最终会等待系统垂直刷新同步信号,回调TraversalRunnable对象的run方法,该方法会调用doTraversal方法,然后进一步调用performTraversals方法。

4、ViewRootImpl的performTraversals方法代码逻辑非常多,这里只列出了我们需要关注的代码。

public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {final IWindowSession mWindowSession;//和WMS通信的Binder对象,具体为Session对象public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口属性final View.AttachInfo mAttachInfo;//控件信息private void performTraversals() {final View host = mView;if (host == null || !mAdded) {return;}if (mWaitForBlastSyncComplete) {mRequestedTraverseWhilePaused = true;return;}mIsInTraversal = true;mWillDrawSoon = true;boolean windowSizeMayChange = false;WindowManager.LayoutParams lp = mWindowAttributes;//将当前窗口的最新属性赋值给lpint desiredWindowWidth;int desiredWindowHeight;final int viewVisibility = getHostVisibility();final boolean viewVisibilityChanged = !mFirst&& (mViewVisibility != viewVisibility || mNewSurfaceNeeded|| mAppVisibilityChanged);mAppVisibilityChanged = false;final boolean viewUserVisibilityChanged = !mFirst &&((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));WindowManager.LayoutParams params = null;...代码省略...//收集mView的属性,判断是否需要更新paramsif (collectViewAttributes()) {params = lp;}...代码省略...//此方法最终会触发WindowManagerService的relayoutWindow方法relayoutWindow(params, viewVisibility, insetsPending);...代码省略...  //测量performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);...代码省略...  //布局performLayout(lp, mWidth, mHeight);...代码省略...  //绘制performDraw();...代码省略...  }private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,boolean insetsPending) throws RemoteException {...代码省略...         //调用IWindowSession的relayout方法,该方法最终会触发WindowManagerService的relayoutWindow方法int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,(int) (mView.getMeasuredWidth() * appScale + 0.5f),(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,mPendingMergedConfiguration, mSurface);}}

performTraversals首先调用collectViewAttributes方法收集所有子View的属性,然后调用relayoutWindow方法,该方法最终会触发WindowManagerService的relayoutWindow方法,然后回继续调用触发View测量的performMeasure方法,触发View布局的performLayout方法和触发View绘制的performDraw方法。

三、获取最新的SystemUIVisibility属性

1、ViewRootImpl的collectViewAttributes方法是一个很关键的方法,此方法会重新计算最新的SystemUIVisibility属性。

public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口属性final View.AttachInfo mAttachInfo;//控件信息private boolean collectViewAttributes() {//判断是否需要重新计算本地属性if (mAttachInfo.mRecomputeGlobalAttributes) {//Log.i(mTag, "Computing view hierarchy attributes!");mAttachInfo.mRecomputeGlobalAttributes = false;boolean oldScreenOn = mAttachInfo.mKeepScreenOn;mAttachInfo.mKeepScreenOn = false;//清空已经存在的SystemUiVisibility属性mAttachInfo.mSystemUiVisibility = 0;mAttachInfo.mHasSystemUiListeners = false;//重新获取窗口视图mView最新的的SystemUI属性,赋值给mAttachInfomView.dispatchCollectViewAttributes(mAttachInfo, 0);...代码暂时省略...}return false;}}

collectViewAttributes首先会清空当前窗口视图mView已经存在的SystemUiVisibility属性,然后调用View的dispatchCollectViewAttributes方法重新获取最新的的SystemUiVisibility属性。

2、View的dispatchCollectViewAttributes方法如下所示。

public class View implements Drawable.Callback, KeyEvent.Callback,AccessibilityEventSource {int mSystemUiVisibility;void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) {performCollectViewAttributes(attachInfo, visibility);}void performCollectViewAttributes(AttachInfo attachInfo, int visibility) {if ((visibility & VISIBILITY_MASK) == VISIBLE) {if ((mViewFlags & KEEP_SCREEN_ON) == KEEP_SCREEN_ON) {attachInfo.mKeepScreenOn = true;}//将最新的systemuivisiblity赋予AttachInfo的mSystemUiVisibility 属性attachInfo.mSystemUiVisibility |= mSystemUiVisibility;//设置最新的SystemUiVisibility监听对象,如果不为空,则将AttachInfo的mHasSystemUiListeners属性设置为true。ListenerInfo li = mListenerInfo;if (li != null && li.mOnSystemUiVisibilityChangeListener != null) {attachInfo.mHasSystemUiListeners = true;}}}}    

3、接着看ViewRootImpl的collectViewAttributes方法。

public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口属性final View.AttachInfo mAttachInfo;//控件信息private boolean collectViewAttributes() {//判断是否需要重新计算本地属性if (mAttachInfo.mRecomputeGlobalAttributes) {...代码省略...//移除被禁用的SystemUiVisibility属性mAttachInfo.mSystemUiVisibility &= ~mAttachInfo.mDisabledSystemUiVisibility;//让params引用指向mWindowAttributes对象WindowManager.LayoutParams params = mWindowAttributes;mAttachInfo.mSystemUiVisibility |= getImpliedSystemUiVisibility(params);if (mAttachInfo.mKeepScreenOn != oldScreenOn|| mAttachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility|| mAttachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {applyKeepScreenOnFlag(params);//将重新获取的窗口视图mView的SystemUiVisibility保存到窗口的LayoutParams属性中params.subtreeSystemUiVisibility = mAttachInfo.mSystemUiVisibility;params.hasSystemUiListeners = mAttachInfo.mHasSystemUiListeners;//调用View的dispatchWindowSystemUiVisiblityChanged方法mView.dispatchWindowSystemUiVisiblityChanged(mAttachInfo.mSystemUiVisibility);return true;}}return false;}}

在重新获得mView的SystemUiVisibility属性之后,首先会从该属性中移除被禁用的SystemUiVisibility属性,然后让params引用指向mWindowAttributes对象,并将重新获取的保存在mAttachInfo对象中的SystemUiVisibility属性保存到当前窗口的LayoutParams属性中,最后会调用当前View的dispatchWindowSystemUiVisiblityChanged方法。

4、View的dispatchWindowSystemUiVisiblityChanged方法如下所示。

public class View implements Drawable.Callback, KeyEvent.Callback,AccessibilityEventSource {@Deprecatedpublic void dispatchWindowSystemUiVisiblityChanged(int visible) {onWindowSystemUiVisibilityChanged(visible);//调用onWindowSystemUiVisibilityChanged方法}public void onWindowSystemUiVisibilityChanged(int visible) {//默认为空实现}
}

该方法会进一步调用onWindowSystemUiVisibilityChanged方法,onWindowSystemUiVisibilityChanged方法默认为空实现,但是如果当前mView为DecorView时则不同,DecorView实现了此方法。

四、DecorView更新状态栏和导航栏背景颜色

1、DecorView的onWindowSystemUiVisibilityChanged方法如下所示。

frameworks/base/core/java/com/android/internal/policy/DecorView.java

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {@Overridepublic void onWindowSystemUiVisibilityChanged(int visible) {//调用updateColorViews方法updateColorViews(null /* insets */, true /* animate */);}}

onWindowSystemUiVisibilityChanged方法会调用一个updateColorViews这个关键方法。

2、updateColorViews会调用updateColorViewInt方法更新导航栏和状态栏的背景颜色。

    public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {WindowInsets updateColorViews(WindowInsets insets, boolean animate) {//获取窗口的SystemUIVisibility属性int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();//判断窗口类型是否是输入法final boolean isImeWindow = mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD;//判断窗口类型不是浮动窗口和输入法,则让SystemUIVisibility属性生效if (!mWindow.mIsFloating || isImeWindow) {//获取是否禁止窗口动画的标记boolean disallowAnimate = !isLaidOut();disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)& FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;mLastWindowFlags = attrs.flags;if (insets != null) {mLastTopInset = getColorViewTopInset(insets.getStableInsetTop(),insets.getSystemWindowInsetTop());mLastBottomInset = getColorViewBottomInset(insets.getStableInsetBottom(),insets.getSystemWindowInsetBottom());mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),insets.getSystemWindowInsetRight());mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(),insets.getSystemWindowInsetLeft());// Don't animate if the presence of stable insets has changed, because that// indicates that the window was either just added and received them for the// first time, or the window size or position has changed.boolean hasTopStableInset = insets.getStableInsetTop() != 0;disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);mLastHasTopStableInset = hasTopStableInset;boolean hasBottomStableInset = insets.getStableInsetBottom() != 0;disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);mLastHasBottomStableInset = hasBottomStableInset;boolean hasRightStableInset = insets.getStableInsetRight() != 0;disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);mLastHasRightStableInset = hasRightStableInset;boolean hasLeftStableInset = insets.getStableInsetLeft() != 0;disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);mLastHasLeftStableInset = hasLeftStableInset;mLastShouldAlwaysConsumeNavBar = insets.shouldAlwaysConsumeNavBar();}boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);//更新导航栏颜色,mNavigationColorViewState为导航栏的相关筛选条件updateColorViewInt(mNavigationColorViewState, sysUiVisibility,mWindow.mNavigationBarColor, mWindow.mNavigationBarDividerColor, navBarSize/*导航栏高度*/,navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,0 /* sideInset */, animate && !disallowAnimate, false /* force */);boolean statusBarNeedsRightInset = navBarToRightEdge&& mNavigationColorViewState.present;boolean statusBarNeedsLeftInset = navBarToLeftEdge&& mNavigationColorViewState.present;int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset: statusBarNeedsLeftInset ? mLastLeftInset : 0;//更新状态栏颜色,mStatusColorViewState为状态栏的相关筛选条件updateColorViewInt(mStatusColorViewState, sysUiVisibility,calculateStatusBarColor(), 0, mLastTopInset/*状态栏高度*/,false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,animate && !disallowAnimate,mForceWindowDrawsStatusBarBackground);}...代码省略...}}

关于DecorView更新状态栏、导航栏背景颜色的具体过程,请参考Android 9.0系统源码_SystemUI(八)PhoneWindow更新状态栏和导航栏背景颜色的流程解析。

五、WindowManagerService的relayoutWindow方法

1、重新回到第二节第4步ViewRootImpl的performTraversals方法中。

public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {final IWindowSession mWindowSession;//和WMS通信的Binder对象,具体为Session对象public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口属性final View.AttachInfo mAttachInfo;//控件信息private void performTraversals() {...代码省略...//收集mView的属性,判断是否需要更新paramsif (collectViewAttributes()) {params = lp;}...代码省略...//此方法最终会触发WindowManagerService的relayoutWindow方法relayoutWindow(params, viewVisibility, insetsPending);...代码省略...  //测量performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);...代码省略...  //布局performLayout(lp, mWidth, mHeight);...代码省略...  //绘制performDraw();...代码省略...  }private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,boolean insetsPending) throws RemoteException {...代码省略...         //调用IWindowSession的relayout方法,该方法最终会触发WindowManagerService的relayoutWindow方法int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,(int) (mView.getMeasuredWidth() * appScale + 0.5f),(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,mPendingMergedConfiguration, mSurface);}}

在调用collectViewAttributes获取最新的systemUIVisibiliy属性之后,会调用relayoutWindow方法,该方法进一步调用IWindowSession的relayout方法,IWindowSession的具体实现类为Session。

2、Session的relayout方法如下所示。

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

class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {final WindowManagerService mService;@Overridepublic int relayout(IWindow window, WindowManager.LayoutParams attrs,int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,SurfaceControl outSurfaceControl, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {int res = mService.relayoutWindow(this, window, attrs,requestedWidth, requestedHeight, viewFlags, flags, frameNumber,outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,outActiveControls, outSurfaceSize);return res;}
}

relayout方法会进一步调用WindowManagerService的relayoutWindow方法。

3、WindowManagerService的relayoutWindow方法如下所示。

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

public class WindowManagerService extends IWindowManager.Stubimplements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs/**窗口属性**/,int requestedWidth, int requestedHeight, int viewVisibility/**根View控件是否可见**/, int flags,long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,Surface outSurface) {int result = 0;boolean configChanged;//是否有状态栏的使用权限final boolean hasStatusBarPermission =mContext.checkCallingOrSelfPermission(permission.STATUS_BAR)== PackageManager.PERMISSION_GRANTED;//是否有状态栏服务的使用权限final boolean hasStatusBarServicePermission =mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE)== PackageManager.PERMISSION_GRANTED;long origId = Binder.clearCallingIdentity();final int displayId;synchronized(mWindowMap) {//获取当前要操作的窗口对象WindowState win = windowForClientLocked(session, client, false);if (win == null) {return 0;}//获取窗口所属的屏幕设备iddisplayId = win.getDisplayId();//窗口动画WindowStateAnimator winAnimator = win.mWinAnimator;if (viewVisibility != View.GONE) {win.setRequestedSize(requestedWidth, requestedHeight);}win.setFrameNumber(frameNumber);int attrChanges = 0;int flagChanges = 0;if (attrs != null) {//如果窗口属性不为空,这里会对窗口的相关属性进行一次预处理mPolicy.adjustWindowParamsLw(win, attrs, hasStatusBarServicePermission);// if they don't have the permission, mask out the status bar bitsif (seq == win.mSeq) {int systemUiVisibility = attrs.systemUiVisibility| attrs.subtreeSystemUiVisibility;if ((systemUiVisibility & DISABLE_MASK) != 0) {if (!hasStatusBarPermission) {systemUiVisibility &= ~DISABLE_MASK;}}win.mSystemUiVisibility = systemUiVisibility;}if (win.mAttrs.type != attrs.type) {throw new IllegalArgumentException("Window type can not be changed after the window is added.");}// Odd choice but less odd than embedding in copyFrom()if ((attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY)!= 0) {attrs.x = win.mAttrs.x;attrs.y = win.mAttrs.y;attrs.width = win.mAttrs.width;attrs.height = win.mAttrs.height;}//检测窗口标记和样式是否发生了变化flagChanges = win.mAttrs.flags ^= attrs.flags;attrChanges = win.mAttrs.copyFrom(attrs);if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED| WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) {win.mLayoutNeeded = true;}if (win.mAppToken != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0|| (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {win.mAppToken.checkKeyguardFlagsChanged();}if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0)&& (mAccessibilityController != null)&& (win.getDisplayId() == DEFAULT_DISPLAY)) {// No move or resize, but the controller checks for title changes as wellmAccessibilityController.onSomeWindowResizedOrMovedLocked();}if ((flagChanges & PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {updateNonSystemOverlayWindowsVisibilityIfNeeded(win, win.mWinAnimator.getShown());}}...代码省略... if (focusMayChange) {//System.out.println("Focus may change: " + win.mAttrs.getTitle());if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,false /*updateInputWindows*/)) {imMayMove = false;}//System.out.println("Relayout " + win + ": focus=" + mCurrentFocus);}...代码省略...           }
}

relayoutWindow方法会调用一个关键方法updateFocusedWindowLocked。

4、WindowManagerService的updateFocusedWindowLocked方法如下所示。

public class WindowManagerService extends IWindowManager.Stubimplements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {//窗口管理策略的接口类WindowManagerPolicy(WMP),它用来定义一个窗口策略所要遵循的通用规范。final WindowManagerPolicy mPolicy;//根窗口RootWindowContainer mRoot;boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {//获取当前最新的焦点窗口WindowState newFocus = mRoot.computeFocusedWindow();if (mCurrentFocus != newFocus) {//如果窗口焦点发生了变化Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");// This check makes sure that we don't already have the focus// change message pending.mH.removeMessages(H.REPORT_FOCUS_CHANGE);mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);// TODO(multidisplay): Focused windows on default display only.final DisplayContent displayContent = getDefaultDisplayContentLocked();boolean imWindowChanged = false;if (mInputMethodWindow != null) {//如果输入法窗口不为空final WindowState prevTarget = mInputMethodTarget;final WindowState newTarget =displayContent.computeImeTarget(true /* updateImeTarget*/);imWindowChanged = prevTarget != newTarget;if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS&& mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {final int prevImeAnimLayer = mInputMethodWindow.mWinAnimator.mAnimLayer;displayContent.assignWindowLayers(false /* setLayoutNeeded */);imWindowChanged |=prevImeAnimLayer != mInputMethodWindow.mWinAnimator.mAnimLayer;}}if (imWindowChanged) {//输入法窗口发生了变化mWindowsChanged = true;displayContent.setLayoutNeeded();newFocus = mRoot.computeFocusedWindow();}if (DEBUG_FOCUS_LIGHT || localLOGV) Slog.v(TAG_WM, "Changing focus from " +mCurrentFocus + " to " + newFocus + " Callers=" + Debug.getCallers(4));final WindowState oldFocus = mCurrentFocus;mCurrentFocus = newFocus;mLosingFocus.remove(newFocus);if (mCurrentFocus != null) {mWinAddedSinceNullFocus.clear();mWinRemovedSinceNullFocus.clear();}//调用WindowManagerPolicy的focusChangedLw方法int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);if (imWindowChanged && oldFocus != mInputMethodWindow) {// Focus of the input method window changed. Perform layout if needed.if (mode == UPDATE_FOCUS_PLACING_SURFACES) {displayContent.performLayout(true /*initial*/,  updateInputWindows);focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;} else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {// Client will do the layout, but we need to assign layers// for handleNewWindowLocked() below.displayContent.assignWindowLayers(false /* setLayoutNeeded */);}}if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {// The change in focus caused us to need to do a layout.  Okay.displayContent.setLayoutNeeded();if (mode == UPDATE_FOCUS_PLACING_SURFACES) {displayContent.performLayout(true /*initial*/, updateInputWindows);}}if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {// If we defer assigning layers, then the caller is responsible for// doing this part.mInputMonitor.setInputFocusLw(mCurrentFocus, updateInputWindows);}displayContent.adjustForImeIfNeeded();// We may need to schedule some toast windows to be removed. The toasts for an app that// does not have input focus are removed within a timeout to prevent apps to redress// other apps' UI.displayContent.scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);return true;}return false;}}

updateFocusedWindowLocked首先是获取最新的焦点窗口,之后还会判断当前窗口焦点是否发生了变化,如果发生了变化,则会调用WindowManagerPolicy的focusChangedLw方法。

五、PhoneWindowManager的updateSystemUiVisibilityLw方法

1、WindowManagerPolicy是一个抽象接口。

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

public interface WindowManagerPolicy extends WindowManagerPolicyConstants {/*** 一个新的窗口持有了焦点*/public int focusChangedLw(WindowState lastFocus, WindowState newFocus);
}

结合Android 9.0系统源码_窗口管理(一)WindowManagerService的启动流程这篇文章我们可以知道,SystemServer在窗口WindowManagerService对象的时候,将PhoneWindowManager对象实例赋值给了mPolicy。

2、来看下PhoneWindowManager是如何实现focusChangedLw方法的。

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

public class PhoneWindowManager implements WindowManagerPolicy {@Overridepublic int focusChangedLw(WindowState lastFocus, WindowState newFocus) {mFocusedWindow = newFocus;if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) {// If the navigation bar has been hidden or shown, we need to do another// layout pass to update that window.return FINISH_LAYOUT_REDO_LAYOUT;}return 0;}//更新窗口的SystemUiVisibility属性参数private int updateSystemUiVisibilityLw() {//获取当前的焦点窗口WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow: mTopFullscreenOpaqueWindowState;//如果不存在焦点窗口则直接返回if (winCandidate == null) {return 0;}if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {winCandidate = isStatusBarKeyguard() ? mStatusBar : mTopFullscreenOpaqueWindowState;if (winCandidate == null) {return 0;}}final WindowState win = winCandidate;if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && mKeyguardOccluded) {return 0;}int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)& ~mResettingSystemUiFlags& ~mForceClearedSystemUiFlags;if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);}//获取和SystemUIVisibility相关的各种窗口参数final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);mWindowManagerFuncs.getStackBounds(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds);mWindowManagerFuncs.getStackBounds(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);final int diff = visibility ^ mLastSystemUiFlags;final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu&& mFocusedApp == win.getAppToken()&& mLastNonDockedStackBounds.equals(mNonDockedStackBounds)&& mLastDockedStackBounds.equals(mDockedStackBounds)) {return 0;}mLastSystemUiFlags = visibility;mLastFullscreenStackSysUiFlags = fullscreenVisibility;mLastDockedStackSysUiFlags = dockedVisibility;mLastFocusNeedsMenu = needsMenu;mFocusedApp = win.getAppToken();final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);final Rect dockedStackBounds = new Rect(mDockedStackBounds);mHandler.post(new Runnable() {@Overridepublic void run() {StatusBarManagerInternal statusbar = getStatusBarManagerInternal();if (statusbar != null) {//最终会触发状态栏管理服务StatusBarManagerService的setSystemUiVisibility方法,//通知状态栏和底部栏进行样式调整statusbar.setSystemUiVisibility(visibility, fullscreenVisibility,dockedVisibility, 0xffffffff, fullscreenStackBounds,dockedStackBounds, win.toString());statusbar.topAppWindowChanged(needsMenu);}}});return diff;}
}

PhoneWindowManager的focusChangedLw方法直接调用了updateSystemUiVisibilityLw方法,此方法会对窗口的SystemUiVisibility属性做一些处理,最终调用状态栏管理服务StatusBarManagerService的setSystemUiVisibility方法,通知状态栏和底部栏进行样式调整。

3、updateSystemUiVisibilityLw方法中有用到对SystemUIVisibility属性做处理的几个关键方法:updateLightStatusBarLw、chooseNavigationColorWindowLw、updateLightNavigationBarLw和updateSystemBarsLw,这里一并贴出,有兴趣的可以看下。

public class PhoneWindowManager implements WindowManagerPolicy {private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) {final boolean onKeyguard = isStatusBarKeyguard() && !mKeyguardOccluded;final WindowState statusColorWin = onKeyguard ? mStatusBar : opaqueOrDimming;if (statusColorWin != null && (statusColorWin == opaque || onKeyguard)) {// If the top fullscreen-or-dimming window is also the top fullscreen, respect// its light flag.vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null)& View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;} else if (statusColorWin != null && statusColorWin.isDimming()) {// Otherwise if it's dimming, clear the light flag.vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;}return vis;}@VisibleForTesting@Nullablestatic WindowState chooseNavigationColorWindowLw(WindowState opaque,WindowState opaqueOrDimming, WindowState imeWindow,@NavigationBarPosition int navBarPosition) {// If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME// window can be navigation color window.final boolean imeWindowCanNavColorWindow = imeWindow != null&& imeWindow.isVisibleLw()&& navBarPosition == NAV_BAR_BOTTOM&& (PolicyControl.getWindowFlags(imeWindow, null)& WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;if (opaque != null && opaqueOrDimming == opaque) {// If the top fullscreen-or-dimming window is also the top fullscreen, respect it// unless IME window is also eligible, since currently the IME window is always show// above the opaque fullscreen app window, regardless of the IME target window.// TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed.return imeWindowCanNavColorWindow ? imeWindow : opaque;}if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) {// No dimming window is involved. Determine the result only with the IME window.return imeWindowCanNavColorWindow ? imeWindow : null;}if (!imeWindowCanNavColorWindow) {// No IME window is involved. Determine the result only with opaqueOrDimming.return opaqueOrDimming;}// The IME window and the dimming window are competing.  Check if the dimming window can be// IME target or not.if (LayoutParams.mayUseInputMethod(PolicyControl.getWindowFlags(opaqueOrDimming, null))) {// The IME window is above the dimming window.return imeWindow;} else {// The dimming window is above the IME window.return opaqueOrDimming;}}@VisibleForTestingstatic int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming,WindowState imeWindow, WindowState navColorWin) {if (navColorWin != null) {if (navColorWin == imeWindow || navColorWin == opaque) {// Respect the light flag.vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;vis |= PolicyControl.getSystemUiVisibility(navColorWin, null)& View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;} else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) {// Clear the light flag for dimming window.vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;}}return vis;}private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {final boolean dockedStackVisible =mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);final boolean freeformStackVisible =mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM);final boolean resizing = mWindowManagerInternal.isDockedDividerResizing();// We need to force system bars when the docked stack is visible, when the freeform stack// is visible but also when we are resizing for the transitions when docked stack// visibility changes.mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing;final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard;// apply translucent bar vis flagsWindowState fullscreenTransWin = isStatusBarKeyguard() && !mKeyguardOccluded? mStatusBar: mTopFullscreenOpaqueWindowState;vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);final int dockedVis = mStatusBarController.applyTranslucentFlagLw(mTopDockedOpaqueWindowState, 0, 0);final boolean fullscreenDrawsStatusBarBackground =drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState);final boolean dockedDrawsStatusBarBackground =drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState);// prevent status bar interaction from clearing certain flagsint type = win.getAttrs().type;boolean statusBarHasFocus = type == TYPE_STATUS_BAR;if (statusBarHasFocus && !isStatusBarKeyguard()) {int flags = View.SYSTEM_UI_FLAG_FULLSCREEN| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_IMMERSIVE| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;if (mKeyguardOccluded) {flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT;}vis = (vis & ~flags) | (oldVis & flags);}if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) {vis |= View.STATUS_BAR_TRANSPARENT;vis &= ~View.STATUS_BAR_TRANSLUCENT;} else if ((!areTranslucentBarsAllowed() && fullscreenTransWin != mStatusBar)|| forceOpaqueStatusBar) {vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT);}vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing);// update status barboolean immersiveSticky =(vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;final boolean hideStatusBarWM =mTopFullscreenOpaqueWindowState != null&& (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)& WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;final boolean hideStatusBarSysui =(vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;final boolean hideNavBarSysui =(vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;final boolean transientStatusBarAllowed = mStatusBar != null&& (statusBarHasFocus || (!mForceShowSystemBars&& (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));final boolean transientNavBarAllowed = mNavigationBar != null&& !mForceShowSystemBars && hideNavBarSysui && immersiveSticky;final long now = SystemClock.uptimeMillis();final boolean pendingPanic = mPendingPanicGestureUptime != 0&& now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard() && mKeyguardDrawComplete) {// The user performed the panic gesture recently, we're about to hide the bars,// we're no longer on the Keyguard and the screen is ready. We can now request the bars.mPendingPanicGestureUptime = 0;mStatusBarController.showTransient();if (!isNavBarEmpty(vis)) {mNavigationBarController.showTransient();}}final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()&& !transientStatusBarAllowed && hideStatusBarSysui;final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()&& !transientNavBarAllowed;if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) {// clear the clearable flags insteadclearClearableFlagsLw();vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;}final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;final boolean navAllowedHidden = immersive || immersiveSticky;if (hideNavBarSysui && !navAllowedHidden&& getWindowLayerLw(win) > getWindowLayerFromTypeLw(TYPE_INPUT_CONSUMER)) {// We can't hide the navbar from this window otherwise the input consumer would not get// the input events.vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);}vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis);// update navigation barboolean oldImmersiveMode = isImmersiveMode(oldVis);boolean newImmersiveMode = isImmersiveMode(vis);if (win != null && oldImmersiveMode != newImmersiveMode) {final String pkg = win.getOwningPackage();mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode,isUserSetupComplete(), isNavBarEmpty(win.getSystemUiVisibility()));}vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis);final WindowState navColorWin = chooseNavigationColorWindowLw(mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,mWindowManagerFuncs.getInputMethodWindowLw(), mNavigationBarPosition);vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState,mTopFullscreenOpaqueOrDimmingWindowState,mWindowManagerFuncs.getInputMethodWindowLw(), navColorWin);return vis;}
}

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

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

相关文章

jvm 运行时数据区

Java虚拟机定义了若干种程序运行期间会使用到的运行时数据区,其中有一些会随着虚拟机启动而创建,随着虚拟机退出而销毁。另外一些则是与线程一一对应的,这些与线程对应的数据区域会随着线程开始和结束而创建和销毁 1.1程序计数器 程序计数器也叫pc寄存器 可以看作是当前线程…

OLAP 和 OLTP区别

OLAP 和 OLTP 1、概述2、处理数据类型3、处理数据模式4、性能要求5、数据安全性6、应用场景7、结论 1、概述 OLAP在线分析处理&#xff08;Online Analytical Processing&#xff09; 是一种计算机处理数据的方式&#xff0c;主要用于处理企业级的决策分析、战略分析以及业务分…

Java进阶(4)——结合类加载JVM的过程理解创建对象的几种方式:new,反射Class,克隆clone(拷贝),序列化反序列化

目录 引出类什么时候被加载JVM中创建对象几种方式1.new 看到new : new Book()2.反射 Class.forName(“包名.类名”)如何获取Class对象【反射的基础】案例&#xff1a;连接数据库方法 3.克隆&#xff08;拷贝&#xff09;clone浅拷贝深拷贝案例 序列化和反序列化对象流-把对象存…

【面试题】前端面试复习6---性能优化

前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 性能优化 一、性能指标 要在 Chrome 中查看性能指标&#xff0c;可以按照以下步骤操作&#xff1a; 打开 Chrome 浏览器&#xff0c;并访问你想要测试…

lvs-DR

lvs-DR数据包流向分析 client向目标VIP发出请求。 DIR根据负载均衡算法一台active的RS&#xff08;RIR1&#xff09;&#xff0c;将RIP1所在的网卡的mac地址作为目标的mac地址&#xff0c;发送到局域网里。 RIRI在局域网中的收到这个帧&#xff0c;拆开后发现目标&#xff08…

C++类模板的特化(三)

本文主要介绍类模板的特化、局部特化和缺省模板实参&#xff1b; 1.类模板的特化 类模板的特化&#xff08;Class Template Specialization&#xff09;是指为特定的模板参数提供自定义实现的过程。通过特化&#xff0c;我们可以针对某些特定的类型或条件提供不同的行为或实现…

dart基础类型与方法使用

dart基础类型与方法使用 类型及方法 字符串、数字、列表、集合、映射&#xff0c;及列表、集合、映射相互转换 void foo() {var toly Person(xiaoming);toly.say();var bm toly.bmi(height: 170, weight: 60);print(bm);toly.logNumFunc(-100.5);toly.logStrFunc(你好.abd…

IPEmotion交流电功率分析计算

一 应用背景 随着国内电动汽车行业的快速发展&#xff0c;在相同的道路环境和行驶状态下&#xff0c;增加电动车的整体续航里程和提升乘员对于行驶途中用电需求的满意度尤为重要。对此&#xff0c;需要采集试验过程中交直流电压电流信号&#xff0c;以计算出车辆各种部件输出和…

Docker容器与虚拟化技术:Docker镜像创建、Dockerfile实例

目录 一、理论 1.Docker镜像的创建方法 2.Docker镜像结构的分层 3.Dockerfile 案例 4.构建Systemctl镜像&#xff08;基于SSH镜像&#xff09; 5.构建Tomcat 镜像 6.构建Mysql镜像 二、实验 1.Docker镜像的创建 2. Dockerfile 案例 3.构建Systemctl镜像&#xff08;…

redis的应用场景

Redis最适合所有数据in-momory的场景&#xff0c;虽然Redis也提供持久化功能&#xff0c;但实际更多的是一个disk-backed的功能&#xff0c;跟传统意义上的持久化有比较大的差别&#xff0c;那么可能大家就会有疑问&#xff0c;似乎Redis更像一个加强版的Memcached&#xff0c;…

大数据平台需要做等保测评吗?怎么做?

大数据时代的数据获取方式、存储规模、访问特点、关注重点都有了很大不同&#xff0c;所以保证大数据平台数据安全尤其重要。因此不少人在问&#xff0c;大数据平台需要做等保测评吗&#xff1f;怎么做&#xff1f; 大数据平台需要做等保测评吗&#xff1f; 大数据平台是需要做…

数据结构之——(手撕)顺序表

本章会介绍的知识点如下图&#xff1a; 1&#xff1a; 顺序表的概念&#xff1a;顺序表是用一段物理地址连续的存储单元依次存储数据的线性结构&#xff0c;通常我们使用数组来表示&#xff0c;对数组进行增删查改。 顺序表的结构&#xff1a;逻辑结构与物理结构都是内存中一块…

AI Agent在情景猜谜场景下的AgentBench基准测试

目录 AgentBench评估哪些场景? 近日,来自清华大学、俄亥俄州立大学和加州大学伯克利分校的研究者设计了一个测试工具——AgentBench,用于评估LLM在多维度开放式生成环境中的推理能力和决策能力。研究者对25个LLM进行了全面评估,包括基于API的商业模型和开源模型。 他们发现…

数字人学习目录

数字人学习目录 百度PaddlePaddleHub图像风格迁移模型pp-tinypose模型 PaddleGANPaddleLitePaddleDetectionPP-TinyPose 人体骨骼关键点识别 PaddleSpeechVisualDLPaddleBobo TransformerWav2LibCLIPFFMpeg模型库数据集学习天地PythonJupyter Notebook Unity3DUE 百度Paddle P…

docker之Consul环境的部署

目录 一.Docker consul的介绍 1.1template模板(更新) 1.2registrator&#xff08;自动发现&#xff09; 1.3agent(代理) 二.consul的工作原理 三.Consul的特性 四.Consul的使用场景 五.搭建Consul的集群 5.1需求 5.2部署consul 5.3主服务器[192.168.40.20] 5.4client部署&…

华为OD-最大括号深度

题目描述 一个合法的括号匹配序列有以下定义: 1、空串""是一个合法的括号匹配序列 2、如果"X"和"Y"都是合法的括号匹配序列,"XY"也是一个合法的括号匹配序列 3、如果"X"是一个合法的括号匹配序列,那么"(X)"也是一…

ChatGPT:记一次超复杂的KVM桌面系统连接问答记录

​ KVM切换器可以使多台电脑共用键盘&#xff0c;显示器&#xff0c;鼠标&#xff0c;当电脑很多&#xff0c;显示器也是分为主从&#xff0c;需要共用键盘鼠标和音响设备&#xff0c;而买KVM切换器只有2个通道4进2出不满足需求时&#xff0c;就要组合多个KVM使用&#xff0c;大…

尽管价格走势平淡,但DeFi领域仍然非常有趣

DEX代表加密货币交易的创新&#xff0c;就在去年&#xff0c;这些去中心化、非托管平台的活动与CEX比相形见绌&#xff0c;但自那时以来&#xff0c;DEX已经迎头赶上&#xff0c;并在几个月内超越了中心化服务交易量&#xff0c;让用户能够更好地控制自己的资产和进行新类型的交…

oracle通配符大全

用于where比较条件的有 &#xff1a; 等于&#xff1a;、<、<、>、>、<> >,<:大于&#xff0c;小于 >.<:大于等于&#xff0c;小于等于 :等于 !,<>,^:不等于 包含&#xff1a;in、not in exists、not exists 范围&#xff1a;betwe…

python实战【外星人入侵】游戏并改编为【梅西vsC罗】(球迷整活)——搭建环境、源码、读取最高分及生成可执行的.exe文件

文章目录 &#x1f3a5;前言&#x1f4bc;安装Pygame&#x1f50b;游戏的实现读写并存储【外星人入侵】游戏最高分游戏源码alien_invasion.pygame_functions.pyship.pyalien.pybullet.pybutton.pyscoreboard.pygame_stats.pysettings.py宇宙飞船和外星人的 .bmp类型文件 &#…