本文正在参加「金石计划 . 分割6万现金大奖」
Hi,我是小余。本文已收录到GitHub Androider-Planet中。这儿有 Android 进阶成长常识体系,重视大众号 [小余的自习室] ,在成功的路上不迷路!
前言:
前一篇文章介绍了关于Android中Window体系的介绍,首要介绍的是View层的Window体系概念,可是想要深化了解Window在体系中的机制,WMS是绕不过去的坎。有句话说的好:AMS和WMS占有了Framework层的半壁江山,所以了解这两个概念对咱们在日常开发中的功能优化或有想转Framework开发的都大有协助。
笔者花了几天时刻对WMS相关常识进行了整理。
首先咱们来说下WMS在体系中的几大职责
WMS中窗口容器树的概念
为了更好的解说个个中心成员类,咱们需求先来理清Window中的容器和树的概念。
容器:这个咱们都了解,在WMS中经过承继WindowContainer类来完结容器机制:
而树便是说每个容器在作为父容器的时分,一起也可能是其他容器的子容器。
具体咱们来看WindowContainer全体结构:
class WindowContainer<E extends WindowContainer> implements Comparable<WindowContainer> {
...
private WindowContainer mParent = null;//1
protected final WindowList<E> mChildren = new WindowList<E>();//2
}
注释1处mParent是当时WindowContainer容器的父容器
注释2处的mChildren是一个承继WindowContainer的调集目标,阐明当时WindowContainer可能会有好几个子容器。
画个图来阐明下这儿面的联系:
有了容器的概念后下面按职责来介绍WMS
窗口办理:
窗口办理中心类介绍:
窗口办理运用到的DisplayContent,WindowToken和WindowState
DisplayContent:
用来办理一个逻辑屏上的所有窗口,有几个屏幕就会有几个DisplayContent。运用displayId来区别。
处于不同DisplayContent的两个窗口在布局、显现次序以及动画处理上不会产生任何耦合。 因此,就这几个方面来说,DisplayContent就像一个孤岛,所有这些操作都能够在其内部独立履行。
DisplayContent类声明:
class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>{
// Mapping from a token IBinder to a WindowToken object on this display.
private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap();
}
1.DisplayContent的子容器是其内部类DisplayChildWindowContainer
2.DisplayContent内部运用:IBinder为key,WindowToken为value的键值对保存在HashMap中。
3.DisplayContent是在Window增加到WMS的时分初始化的:
WMS:
public int addWindow(Session session, ..){
final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
..
}
mRoot是RootWindowContainer类型的目标,看名字就知道其是窗口容器的根。 阐明在Window体系中,RootWindowContainer节点是容器最顶端的父容器。
class RootWindowContainer extends WindowContainer<DisplayContent> {
DisplayContent getDisplayContentOrCreate(int displayId) {
DisplayContent dc = getDisplayContent(displayId);
if (dc == null) {
final Display display = mService.mDisplayManager.getDisplay(displayId);
if (display != null) {
dc = createDisplayContent(display);
}
}
return dc;
}
DisplayContent getDisplayContent(int displayId) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent current = mChildren.get(i);
if (current.getDisplayId() == displayId) {
return current;
}
}
return null;
}
}
其承继了WindowContainer,这儿的DisplayContent是一个泛型声明,标明其子容器的类型是DisplayContent, 在getDisplayContent办法中也可知,其mChildren列表是DisplayContent的调集。这也变相的阐明DisplayContent也是一个容器
WindowToken:
类声明:
class WindowToken extends WindowContainer<WindowState>
标明WindowToken也是子容器,其子容器是WindowState,所以WindowState也是一个容器。
WindowToken在窗口体系中有两个作用:
- 1.运用组件标识:将属于同一个运用组件的窗口组织在一起,这儿的运用组件可所以:Activity、InputMethod、Wallpaper以及Dream。 WMS在对窗口的办理进程中:用WindowToken来指代一个运用组件。例如在进行窗口的Z-Order排序时,属于同一个WindowToken的窗口会被组织在一起。
- 2.令牌作用:WindowToken由运用组件或其办理者担任向WMS声明并持有,运用组件在需求更新窗口时,需求向WMS供给令牌标明自己的身份, 而且窗口的类型有必要与所持有的WindowToken的类型共同。
可是体系窗口是个破例,并不需求供给token,WMS会隐式声明一个WindowToken。
那是不是说谁都能够增加体系窗口了呢?非也,在addWindow开始处就会调用下面代码:
mPolicy.checkAddPermission()
它要求客户端有必要拥有SYSTEM_ALERT_WINDOW或INTERNAL_SYSTEM_WINDOW权限才能创立体系类型的窗口。
Window和WindowToken联系如下:
WindowState:
类声明:
class WindowState extends WindowContainer<WindowState>
标明WindowState也是一个WindowContainer容器,可是其子容器也是WindowState,一般窗口有子窗口SUB_WINDOW的情况下,WindowState才有子容器节点。
WindowState在WMS中标明一个Window窗口状况特点,其内部保存了一个Window所有的特点信息。
其与View以及WindowToken联系如下:
如何检查当时设备Window窗口状况指令?
adb shell dumpsys window windows
Window #9 Window{884cb45 u0 com.android.messaging/com.android.messaging.ui.conversationlist.ConversationListActivity}:
mDisplayId=0 stackId=3 mSession=Session{f1b7b8e 4307:u0a10065} mClient=android.os.BinderProxy@a512fbc
mOwnerUid=10065 mShowToOwnerOnly=true package=com.android.messaging appop=NONE
mAttrs={(0,36)(828xwrap) gr=BOTTOM CENTER sim={adjust=pan forwardNavigation} ty=APPLICATION fmt=TRANSLUCENT wanim=0x7f130015
fl=DIM_BEHIND ALT_FOCUSABLE_IM HARDWARE_ACCELERATED
vsysui=LIGHT_STATUS_BAR LIGHT_NAVIGATION_BAR}
Requested w=828 h=290 mLayoutSeq=220
mBaseLayer=21000 mSubLayer=0 mToken=AppWindowToken{3f9efb8 token=Token{2b272cc ActivityRecord{55a41e u0 com.android.messaging/.ui.conversationlist.ConversationListActivity t8}}}
mAppToken=AppWindowToken{3f9efb8 token=Token{2b272cc ActivityRecord{55a41e u0 com.android.messaging/.ui.conversationlist.ConversationListActivity t8}}}
...
下面笔者以窗口的增加操作为例解说WMS的窗口办理
窗口的增加操作
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
...
int res = mPolicy.checkAddPermission(attrs, appOp);//1
...
synchronized(mWindowMap) {
final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);//2
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
parentWindow = windowForClientLocked(null, attrs.token, false);//3
}
...
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);//4
if (token == null) {
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow);//5
}
...
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);//6
...
mPolicy.adjustWindowParamsLw(win.mAttrs);//7
...
if (openInputChannels) {
win.openInputChannel(outInputChannel);//8
}
...
mWindowMap.put(client.asBinder(), win);//9
...
win.mToken.addWindow(win);//10
...
displayContent.assignWindowLayers(false /* setLayoutNeeded */);//11
//12
if (focusChanged) {
mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
}
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
...
return res;
}
注释1:检查当时Window的token等权限合法性
注释2:这在介绍DisplayContent现已说过,运用RootWindowContainer的子容器中获取一个DisplayContent,假如子容器调集中不存在,则去获取一个,并增加到child调集中
注释3:假如是Dialog等子窗口,则获取父窗口,没有就报找不到父窗口的反常。
注释4:运用attr.token去displayContent的键值对mTokenMap中获取对应的WindowToken,WindowToken中保存了一组Window。
注释5:假如4中WindowToken为null,则创立一个WindowToken,传入app层传入的attr.token以及displayContent目标,内部会将这个创立的WindowToken保存到displayContent中
注释6:创立一个WindowState,并传入所有关于Window相关的特点,这样WindowState在WMS中便是以一个Window性质存在了、WindowState结构进程中会将其增加到WindowToken中去。
注释7:根据mPolicy调整window的attr特点,mPolicy的完结类是PhoneManagerPolicy。
注释8:履行WindowState的openInputChannel,这儿首要是打通和Input体系的通道,用于接收IMS的输入事情恳求。
注释9:将客户端app层的Window目标和WindowState相关上,这样WMS就能够经过Window找到WMS中的WindowState目标、
注释10: win.mToken是前面创立的WindowToken目标,所以此处便是将WindowState加入到WindowToken的子容器调集中。
注释11:分配窗口的层级,这儿的setLayoutNeeded参数为false,阐明不需求进行Layout操作。
这儿小结下addWindow办法: 首要便是创立了一个和Window一一对应的WindowState目标,并将WindowState刺进到父容器WindowToken的子容器调集中,而WindowToken又保存在DisplayContent的键值对调集中。三种联系能够简略总结如下:
窗口动画
咱们知道在Android内部有两种动画:Window切换移动动画以及app层的View的动画, 动画操作的是View而Window切换操作的是Surface,对不同层级的SurfaceControl进行操纵,会产生不同的动画效果, 注意区别。
咱们这里涉及到的是Window切换移动动画:
可是不管是View的动画仍是Window切换操作,对底层屏幕刷新来说都是针对不同帧动画来说,所以会涉及到VSync同步信号相关常识。
中心类:
WindowStateAnimator
类声明:
Keep track of animations and surface operations for a single WindowState.
用来办理一个Window的动画操作的,在WindowState结构办法中创立,阐明每个Window窗口都对应一个WindowStateAnimator
WindowState(WindowManagerService service...){
mWinAnimator = new WindowStateAnimator(this);
}
WindowAnimator:
/**
* Singleton class that carries out the animations and Surface operations in a separate task
* on behalf of WindowManagerService.
*/
public class WindowAnimator
看办法阐明,这个类仍是用于WMS中的窗口动画以及Surface操作的单例东西类,WMS将动画的作业都委托他来处理。其在WMS结构的时分创立了实例。
WindowAnimator(final WindowManagerService service) {
mService = service;
..
mWindowPlacerLocked = service.mWindowPlacerLocked;//1 这个类用于Surface的摆放
AnimationThread.getHandler().runWithScissors(
() -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);//2
```
mAnimationFrameCallback = frameTimeNs -> {//3
synchronized (mService.mWindowMap) {
mAnimationFrameCallbackScheduled = false;
}
animate(frameTimeNs);
};
```
}
注释1处创立了一个WindowSurfacePlacer目标,这个目标是用于Surface的摆放的操作,阐明WindowAnimator还支持Surface的各种操作 注释2处运用AnimationThread线程进行Window的动画操作,AnimationThread内部运用的是HandlerThread机制,阐明其内部也创立了一个异步音讯处理机制。 注释3处mAnimationFrameCallback类型是Choreographer.FrameCallback。FrameCallback在这篇文章中有讲过,其便是给Choreographer设置一个回调,在Choreographer接收到 VSync信号时,在doFrame中触发这个回调,一般是用来监听帧率等操作。
而这儿是在接收到doFrame的时分回调的是一个animate(frameTimeNs)动画处理的办法。animate函数履行流程很长,包括更新壁纸、转屏动画等逻辑均包含在其间。
那么mAnimationFrameCallback回调是什么时分注册到Choreographer中去的呢?
WindowAnimator的scheduleAnimation办法:
void scheduleAnimation() {
if (!mAnimationFrameCallbackScheduled) {
mAnimationFrameCallbackScheduled = true;
mChoreographer.postFrameCallback(mAnimationFrameCallback);
}
}
在外部需求进行动画的时分,就会优先scheduleAnimation,将mAnimationFrameCallback注册到Choreographer中去。
咱们重点来看animate办法:这个办法内部有这么段代码
dc.updateWindowsForAnimator(this);
标明为了动画去更新Windows: 能够进入看看:
void updateWindowsForAnimator(WindowAnimator animator) {
mTmpWindowAnimator = animator;
forAllWindows(mUpdateWindowsForAnimator, true /* traverseTopToBottom */);
}
boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
```
...
final int count = mChildren.size();
for (int i = 0; i < count; i++) {
final DisplayChildWindowContainer child = mChildren.get(i);
if (child == mImeWindowsContainers && mService.mInputMethodTarget != null) {
// In this case the Ime windows will be processed above their target so we skip
// here.
continue;
}
if (child.forAllWindows(callback, traverseTopToBottom)) {
return true;
}
}
...
return false;
```
}
forAllWindows办法会遍历整个容器树都去调用mUpdateWindowsForAnimator回调。 这个回调内部就会去履行winAnimator.stepAnimationLocked去更新Window的更新操作。 stepAnimationLocked,代表单步动画。这儿面的操作咱们自行检查也不难、
这儿对动画做个小结: 经过在需求动画的时分,post一个FrameCallBack给Choreographer,在VSync信号到来的时分,会优先履行动画操作。动画回调内部会去遍历整个容器树模型,顺次更改每个Window对应的Surface的状况。然后在制作完结后,提交给SurfaceFlinger、
进程图示:
输入事情处理:
关于Input事情在这篇文章中现已有讲过。输入子体系从驱动文件中读取事情后,再封装提交给IMS,IMS再发送给WMS进行处理。
输入体系全体架构:
今日咱们从WMS的角度来剖析下输入事情:
中心类:
InputChannel
类声明:
/**
* An input channel specifies the file descriptors used to send input events to
* a window in another process. It is Parcelable so that it can be sent
* to the process that is to receive events. Only one thread should be reading
* from an InputChannel at a time.
* @hide
*/
public final class InputChannel implements Parcelable {
注释中阐明晰:InputChannel是一个运用文件描述符fd来发送input事情给其他进程的一个输入通道,且只要一个线程能够一起读取InputChannel中的数据,阐明InputChannel是线程安全的。 其内部运用的是socket通讯。
前面在剖析Window增加进程的时分说过在WMS的addWindow中会调用
win.openInputChannel(outInputChannel)
void openInputChannel(InputChannel outInputChannel) {
...
String name = getName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);//1
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
mInputWindowHandle.inputChannel = inputChannels[0];
if (outInputChannel != null) {
mClientChannel.transferTo(outInputChannel);//2
mClientChannel.dispose();
mClientChannel = null;
}
...
mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);//3
}
InputChannel.java:
public static InputChannel[] openInputChannelPair(String name) {
...
return nativeOpenInputChannelPair(name);
}
android_view_InputChannel.cpp:
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env...) {
...
sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
```
jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(serverChannel));
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(clientChannel));
...
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
```
}
status_t InputChannel::openInputChannelPair(const String8& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
...
return result;
}
...
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
经过以上代码能够看出InputChannel运用的是sockets通讯,且WindowState的openInputChannel中注释1处:
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name),
回来的inputChannels是一个服务端和客户端的输入通道数组 其间:
- 下标0:标明服务端的InputChannel
- 下标1:标明客户端的InputChannel
在注释3处registerInputChannel传入的是server端InputChannel给IMS。 而注释2处将client端的InputChannel与app端传入的outInputChannel相关起来了。
这样服务端在InputChannel就能够写入input事情,然后在app端的InputChannel就能够接受到数据了。 输入事情通讯模型如下:
Surface办理:
WMS担任创立Surface以及对Surface的摆放作业,之后将Surface提交给SurfaceFlinger进行兼并。 在App层也创立了一个Surface目标,可是那个是空目标,用于WMS的填充。
Surface的创立:
Surface的创立在WMS中运用WindowStateAnimator代理创立,而WindowStateAnimator中又创立了一个WindowSurfaceController对Surface进行办理。
中心类:WindowSurfaceController
public WindowSurfaceController(SurfaceSession s, String name, int w, int h...){
mSurfaceControl = new SurfaceControl(s, name, w, h, format, flags, windowType, ownerUid);
}
public SurfaceControl(SurfaceSession session, String name, ...){
mNativeObject = nativeCreate(session, name, w, h, format, flags,...);
}
在其结构办法中创立了一个SurfaceControl,SurfaceControl终究进入native层,在native层创立了一个Surface,并回来native surface地址.实践在native层也是创立一个SurfaceControl
static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
jint windowType, jint ownerUid) {
```
sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
sp<SurfaceControl> surface = client->createSurface(
String8(name.c_str()), w, h, format, flags, parent, windowType, ownerUid);
...
return reinterpret_cast<jlong>(surface.get());
```
}
那app层是什么时分建议WMS的Surface创立任务的? 看ViewRootImpl的relayoutWindow办法:
private int relayoutWindow(WindowManager.LayoutParams params..){
int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params...mSurface);
}
调用mWindowSession的relayout办法,并传入最后mSurface目标,这是空Surface,在WMS中会被填充回来 终究调用到WMS中的relayoutWindow。
public int relayoutWindow(Session session, IWindow client, int seq..){
...
result = createSurfaceControl(outSurface, result, win, winAnimator);
}
private int createSurfaceControl(Surface outSurface, int result, WindowState win,
WindowStateAnimator winAnimator) {
```
WindowSurfaceController surfaceController;
try {
surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
if (surfaceController != null) {
surfaceController.getSurface(outSurface);//1
if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " OUT SURFACE " + outSurface + ": copied");
}
return result;
```
}
终究会调用WindowStateAnimator的createSurfaceLocked这个前面现已剖析过了。 回来的surfaceController目标在注释1处调用getSurface(outSurface),将native层的Surface填充到App传递过来的outSurface 进入getSurface看看:
WindowSurfaceController.java
void getSurface(Surface outSurface) {
outSurface.copyFrom(mSurfaceControl);
}
Surface.java
public void copyFrom(SurfaceControl other) {
if (other == null) {
throw new IllegalArgumentException("other must not be null");
}
```
long surfaceControlPtr = other.mNativeObject;
if (surfaceControlPtr == 0) {
throw new NullPointerException(
"null SurfaceControl native object. Are you using a released SurfaceControl?");
}
long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr);
synchronized (mLock) {
if (mNativeObject != 0) {
nativeRelease(mNativeObject);
}
setNativeObjectLocked(newNativeObject);
}
```
}
private void setNativeObjectLocked(long ptr) {
if (mNativeObject != ptr) {
mNativeObject = ptr;
}
}
getSurface办法将WMS中创立的WindowSurfaceController中SurfaceControl目标的mNativeObject目标传递给新的Surface,并运用这个目标去native层获取一个新的NativeObject 赋值给当时Surface的mNativeObject,这样App层的Surface就获取到了WMS在native中创立的SurfaceControl目标,能够在app层操作native层的Surface了。
那么为什么谷歌要绕这么大圈来创立Surface呢?直接在App层去创立不就能够了么?
个人见解谷歌是期望统一办理Surface而不是独自让某个运用持有,且Surface的摆放操作等都是得由WMS进行处理,所以就直接让WMS去创立,然后回来给App层去制作Surface操作。
Surface的摆放:
Surface在创立之后还需求进行屏幕方位的承认,那这个在哪里操作呢?
中心类:WindowSurfacePlacer
WMS在结构的时分就创立了WindowSurfacePlacer目标。这个目标首要用来给Surface进行方位的定位、
定位到WindowSurfacePlacer的performSurfacePlacement办法,这个办法能够说是WMS最中心的办法,其担任了所有窗口的摆放作业:如何显现?显现在屏幕什么方位?区域巨细等。 这些将在承认后,下发给SurfaceFlinger进行处理。
WMS中任何窗口状况产生改变都会触发该办法,整个办法进行容器树的遍历,承认窗口可见性等。
final void performSurfacePlacement(boolean force) {
```
int loopCount = 6;
do {
mTraversalScheduled = false;
performSurfacePlacementLoop();
loopCount--;
} while (mTraversalScheduled && loopCount > 0);
```
}
private void performSurfacePlacementLoop() {
...
mInLayout = true;
mService.mRoot.performSurfacePlacement(recoveringMemory);
mInLayout = false;
if (mService.mRoot.isLayoutNeeded()) {
if (++mLayoutRepeatCount < 6) {
requestTraversal();
} else {
Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
mLayoutRepeatCount = 0;
}
} else {
mLayoutRepeatCount = 0;
}
}
performSurfacePlacement终究会调用到mService.mRoot.performSurfacePlacement, mService.mRoot.performSurfacePlacement中终究会履行到对窗口容器树做以下遍历操作,中间代码跳转太多,就略过了。 首要做了下面三件事:
-
1.DisplayContent的mPerformLayout操作:核算所有DisplayFrame以及WindowFrame的巨细和方位
private final Consumer<WindowState> mPerformLayout = w -> { ... mService.mPolicy.layoutWindowLw(w, null); ... } mPolicy = PhoneManagerPolicy PhoneManagerPolicy.java public void layoutWindowLw(WindowState win, WindowState attached) { //这儿面都是对不同给的Window的方位进行承认 computeFrameLw(....);//这个办法会核算所有DisplayFrame以及WindowFrame的巨细和方位 }
-
2.DisplayContent的mApplySurfaceChangesTransaction操作:
private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> { ..前面一大推处理 winAnimator.setSurfaceBoundariesLocked(mTmpRecoveringMemory /* recoveringMemory */); } winAnimator = WindowStateAnimator WindowStateAnimator.java void setSurfaceBoundariesLocked(final boolean recoveringMemory) { calculateSurfaceBounds(w, attrs);//1 mSurfaceResized = mSurfaceController.setSizeInTransaction( mTmpSize.width(), mTmpSize.height(), recoveringMemory);//2 ... mSurfaceController.setPositionInTransaction((float) Math.floor(posX), (float) Math.floor(posY), recoveringMemory);//3 }
注释1处核算Surface的size巨细,然后在注释2处运用mSurfaceController设置到native层的SurfaceController目标中, 注释3处在核算好方位后,也运用mSurfaceController设置到native层的SurfaceController目标中。 这样就将Surface在屏幕中给的方位以及巨细都承认下来了、
-
3.WindowStateAnimator的commitFinishDrawingLocked();提交业务
boolean commitFinishDrawingLocked() { ``` mDrawState = READY_TO_SHOW; boolean result = false; final AppWindowToken atoken = mWin.mAppToken; if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) { result = mWin.performShowLocked(); } return result; ``` } boolean performShowLocked() { final int drawState = mWinAnimator.mDrawState; if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) && mAttrs.type != TYPE_APPLICATION_STARTING && mAppToken != null) { mAppToken.onFirstWindowDrawn(this, mWinAnimator); } ``` if (mWinAnimator.mDrawState != READY_TO_SHOW || !isReadyForDisplay()) { return false; } logPerformShow("Showing "); mService.enableScreenIfNeededLocked(); mWinAnimator.applyEnterAnimationLocked(); mWinAnimator.mDrawState = HAS_DRAWN; mService.scheduleAnimationLocked();//1 if (mHidden) { mHidden = false; final DisplayContent displayContent = getDisplayContent(); //2 for (int i = mChildren.size() - 1; i >= 0; --i) { final WindowState c = mChildren.get(i); if (c.mWinAnimator.mSurfaceController != null) { c.performShowLocked(); } } } ``` } ````
performShowLocked办法中有大量的对窗口状况的判别: 窗口的显现进程共有五个状况:
-
NO_SURFACE:
在创立WindowState后的默许状况,标明当时窗口还创没有履行relayout()办法创立Surface;
-
DRAW_PENDING:
履行relayout()办法后,创立完结Surface后的状况,标明等候制作;
-
COMMIT_DRAW_PENDING:
窗口Surface上完结制作后的状况,履行WindowStateAnimator#finishDrawingLocked()办法设置,标明现已完结制作,等候下次刷帧进行提交;
-
READY_TO_SHOW:
标明窗口现已制作完结而且完结提交,此刻假如该窗口的兄弟窗口悉数完结制作且满足显现要求,则直接进行HAS_DRAWN的改变完结显现,不然等候其他兄弟窗口完结制作后,再进行HAS_DRAWN改变;-
-
HAS_DRAWN:标明该窗口正式显现;
在注释2处又对窗口容器树进行了遍历:都指向performShowLocked办法 在注释1处调用了WMS的scheduleAnimationLocked办法,假如你还有形象,在前面剖析窗口动画的时分说过,scheduleAnimationLocked办法会将动画帧回调FrameCallback设置到Choreographer中去。 然后在VSYNC信号到来的时分,指向CallBack动画回调、最后履行animate
private void animate(long frameTimeNs) {
//..
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
...
dc.prepareWindowSurfaces();
}
void prepareWindowSurfaces() {
forAllWindows(mPrepareWindowSurfaces, false / traverseTopToBottom */);
}
private final Consumer<WindowState> mPrepareWindowSurfaces =
w -> w.mWinAnimator.prepareSurfaceLocked(true);
void prepareSurfaceLocked(final boolean recoveringMemory) {
boolean prepared = mSurfaceController.prepareToShowInTransaction(mShownAlpha,..);//1
```
mSurfaceController.setLayer(mAnimLayer);//2
showSurfaceRobustlyLocked()//3
```
}
private boolean showSurfaceRobustlyLocked() {
final Task task = mWin.getTask();
if (task != null && StackId.windowsAreScaleable(task.mStack.mStackId)) {
mSurfaceController.forceScaleableInTransaction(true);
}
```
boolean shown = mSurfaceController.showRobustlyInTransaction();
if (!shown)
return false;
if (mWin.mTurnOnScreen) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "Show surface turning screen on: " + mWin);
mWin.mTurnOnScreen = false;
mAnimator.mBulkUpdateParams |= SET_TURN_ON_SCREEN;
}
return true;
```
}
终究在showSurfaceRobustlyLocked中调mSurfaceController.showRobustlyInTransaction()办法进行Surface的提交给SurfaceFlinger进行组成并显现在屏幕上。
能够看到Surface的size,postion以及状况办理,提交履行等操作仍是一个比较繁琐的进程。
时序图如下:
总结
本篇文章笔者经过对WMS的职责进行划分,并分别对几个职责进行了较为具体的解说。
期望这篇文章对你有协助。假如你想了解更多关于Framework的常识请重视我、
笔者大众号:“小余的自习室”
参考文章:
Android体系中动画浅析
深化了解Android 卷III
“一文读懂”系列:Android屏幕刷新机制
Android R WindowManagerService模块(1) WMS全体架构及发动进程