浅谈framework之WindowsManagerService

mac2025-04-09  13

文章目录

1. 前言2. 简述WMS3. 流程图4. SDK Client5. Framework Client5.1 requestLayout5.2 addToDisplay 6. Framework Server7. SurfaceFlinger的链接

1. 前言

接下来的一段时间,主要着重点会放在WMS的学习研究上,不断完善这篇文章,然后再将AMS那篇文章完善,就先告一段落了。这里提一下,下文WindowsManagerService都简称为WMS。

2. 简述WMS

WMS最主要的作用就是管理窗口,提到窗口,这里就提一下android系统中的几大窗口:

应用窗口,就是被人熟知的activity,一个activity对应着一个应用窗口子窗口,子窗口依附于父窗口,即不能独立存在,需依赖一个应用,如应用输入法窗口,应用提示框窗口等系统窗口,系统窗口不属于任何一个应用,只属于系统本身的,如关机对话框窗口,状态栏窗口等

下文也主要是讲解一个窗口的添加过程。

3. 流程图

网上找到一张图,先来感受一下: 下面也会基于这个流程图的过程来讲解WMS。

4. SDK Client

下面这段代码就是添加一个悬浮View的过程来说明窗口的添加:

private void addTextViewWindow(Context context){ TextView mview=new TextView(context); WindowManager mWindowManager = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE); WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams(); wmParams.type = WindowManager.LayoutParams.TYPE_TOAST; wmParams.format = PixelFormat.RGBA_8888; wmParams.width = 800; wmParams.height = 800; mWindowManager.addView(mview, wmParams); }

5. Framework Client

WindowManager的addView方法开始绘制View。WindowManager只是个接口,它的实现类是WindowManagerImpl类,接着看addView方法:

===========/frameworks/base/core/java/android/view/WindowManagerImpl.java ===========

public void addView(View view, ViewGroup.LayoutParams params) { mGlobal.addView(view, params, mDisplay, mParentWindow); }

WindowManagerImpl调用mGlobal的addView方法,mGloabal是WindowManagerGlobal的实例对象,我们继续看下mGlobal的addView方法:

===========/frameworks/base/core/java/android/view/WindowManagerGlobal.java ===========

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ...... root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); ...... try { root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { synchronized (mLock) { final int index = findViewLocked(view, false); if (index >= 0) { removeViewLocked(index, true); } } throw e; } }

创建ViewRootImpl实例对象root,再设置View的layoutParams,将view加入mViews列表中,将root加入mRoots列表中,接着调用ViewRootImpl的setView方法:

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

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; // Schedule the first layout -before- adding to the window // manager, to make sure we do the relayout before receiving // any other events from the system. requestLayout(); //3 try { res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); //4 } catch (RemoteException e) { } } } }

先看注释3,这里是先为relayout占一个位置,其实是依靠Handler先发送一个Message,排在所有WMS发送过来的消息之前,先布局绘制一次,之后才会处理WMS传来的各种事件,比如触摸事件等,毕竟要首先将各个View的布局、位置处理好,才能准确的处理WMS传来的事件。

再看注释4,这里才是真正添加窗口的地方,虽然关键点1执行在前,但是用的是Handler发消息的方式来处理,其Runable一定是在关键点2之后执行。

下面将分别对这两个关键函数进行分析。

5.1 requestLayout

注释3的函数实现如下:

public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }

上面代码checkThread是检查当前线程是否是主线程,不是的话会报错退出,然后调用scheduleTraversals函数继续处理:

void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); mChoreographer.postCallback( //5 Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); scheduleConsumeBatchedInput(); } } final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

上面经过一系列的函数调用,最后在注释5处加入消息队列,TraversalRunnable 线程被启动,然后就调用到doTraversal函数中:

void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals"); try { performTraversals(); //6 } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } }

上边代码注释6处performTraversals函数是视图绘制流程的开端:

private void performTraversals() { ...... performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); ...... performLayout(lp, mWidth, mHeight); ...... performDraw(); ...... }

可以看到上面函数中的有调用到performMeasure,performLayout,performLayout等核心方法,分别对应上measure(测量),layout(布局),draw(绘制)流程。

5.2 addToDisplay

我们再来分析注释4处的代码:

res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mInputChannel);

需要知道的是mWindowSession其实是一个aidl对象:

final IWindowSession mWindowSession;

这里其实调用了远程服务端的addToDisplay的函数。

找到aidl服务端:

final class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { ...... }

其实就是Session类,继续往下分析,找到aidl服务端的addToDisplay函数的实现:

public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, InputChannel outInputChannel) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outInputChannel); }

需要了解的是,这个mService其实就是WindowManagerService对象,所以 mService.addWindow其实就是调用到了WMS的addWindow方法,也就到了上面流程图的Framework Server层。从函数名字上可以看出这个方法的作用就是添加窗口。这就过渡到Framework Server层。

6. Framework Server

我们直接跟踪addWindow的实现吧,这个是WMS中的一个核心函数,完整代码如下所示:

public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, InputChannel outInputChannel) { int[] appOp = new int[1]; int res = mPolicy.checkAddPermission(attrs, appOp); if (res != WindowManagerGlobal.ADD_OKAY) { return res; } boolean reportNewConfig = false; WindowState attachedWindow = null; WindowState win = null; long origId; final int type = attrs.type; synchronized(mWindowMap) { if (!mDisplayReady) { throw new IllegalStateException("Display has not been initialialized"); } final DisplayContent displayContent = getDisplayContentLocked(displayId); if (displayContent == null) { Slog.w(TAG, "Attempted to add window to a display that does not exist: " + displayId + ". Aborting."); return WindowManagerGlobal.ADD_INVALID_DISPLAY; } if (!displayContent.hasAccess(session.mUid)) { Slog.w(TAG, "Attempted to add window to a display for which the application " + "does not have access: " + displayId + ". Aborting."); return WindowManagerGlobal.ADD_INVALID_DISPLAY; } if (mWindowMap.containsKey(client.asBinder())) { Slog.w(TAG, "Window " + client + " is already added"); return WindowManagerGlobal.ADD_DUPLICATE_ADD; } if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { attachedWindow = windowForClientLocked(null, attrs.token, false); if (attachedWindow == null) { Slog.w(TAG, "Attempted to add window with token that is not a window: " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; } if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) { Slog.w(TAG, "Attempted to add window with token that is a sub-window: " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; } } if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) { Slog.w(TAG, "Attempted to add private presentation window to a non-private display. Aborting."); return WindowManagerGlobal.ADD_PERMISSION_DENIED; } boolean addToken = false; WindowToken token = mTokenMap.get(attrs.token); if (token == null) { if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { Slog.w(TAG, "Attempted to add application window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (type == TYPE_INPUT_METHOD) { Slog.w(TAG, "Attempted to add input method window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (type == TYPE_WALLPAPER) { Slog.w(TAG, "Attempted to add wallpaper window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (type == TYPE_DREAM) { Slog.w(TAG, "Attempted to add Dream window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } token = new WindowToken(this, attrs.token, -1, false); addToken = true; } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { AppWindowToken atoken = token.appWindowToken; if (atoken == null) { Slog.w(TAG, "Attempted to add window with non-application token " + token + ". Aborting."); return WindowManagerGlobal.ADD_NOT_APP_TOKEN; } else if (atoken.removed) { Slog.w(TAG, "Attempted to add window with exiting application token " + token + ". Aborting."); return WindowManagerGlobal.ADD_APP_EXITING; } if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) { // No need for this guy! if (localLOGV) Slog.v( TAG, "**** NO NEED TO START: " + attrs.getTitle()); return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED; } } else if (type == TYPE_INPUT_METHOD) { if (token.windowType != TYPE_INPUT_METHOD) { Slog.w(TAG, "Attempted to add input method window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } else if (type == TYPE_WALLPAPER) { if (token.windowType != TYPE_WALLPAPER) { Slog.w(TAG, "Attempted to add wallpaper window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } else if (type == TYPE_DREAM) { if (token.windowType != TYPE_DREAM) { Slog.w(TAG, "Attempted to add Dream window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } win = new WindowState(this, session, client, token, attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent); if (win.mDeathRecipient == null) { // Client has apparently died, so there is no reason to // continue. Slog.w(TAG, "Adding window client " + client.asBinder() + " that is dead, aborting."); return WindowManagerGlobal.ADD_APP_EXITING; } mPolicy.adjustWindowParamsLw(win.mAttrs); win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs)); res = mPolicy.prepareAddWindowLw(win, attrs); if (res != WindowManagerGlobal.ADD_OKAY) { return res; } if (outInputChannel != null && (attrs.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { String name = win.makeInputChannelName(); InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); win.setInputChannel(inputChannels[0]); inputChannels[1].transferTo(outInputChannel); mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle); } // From now on, no exceptions or errors allowed! res = WindowManagerGlobal.ADD_OKAY; origId = Binder.clearCallingIdentity(); if (addToken) { mTokenMap.put(attrs.token, token); } win.attach(); //7 mWindowMap.put(client.asBinder(), win); if (win.mAppOp != AppOpsManager.OP_NONE) { if (mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(), win.getOwningPackage()) != AppOpsManager.MODE_ALLOWED) { win.setAppOpVisibilityLw(false); } } if (type == TYPE_APPLICATION_STARTING && token.appWindowToken != null) { token.appWindowToken.startingWindow = win; if (DEBUG_STARTING_WINDOW) Slog.v (TAG, "addWindow: " + token.appWindowToken + " startingWindow=" + win); Message m = mH.obtainMessage(H.REMOVE_STARTING_TIMEOUT, token.appWindowToken); mH.sendMessageDelayed(m, STARTING_WINDOW_TIMEOUT_DURATION); } boolean imMayMove = true; if (type == TYPE_INPUT_METHOD) { win.mGivenInsetsPending = true; mInputMethodWindow = win; addInputMethodWindowToListLocked(win); imMayMove = false; } else if (type == TYPE_INPUT_METHOD_DIALOG) { mInputMethodDialogs.add(win); addWindowToListInOrderLocked(win, true); moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true)); imMayMove = false; } else { addWindowToListInOrderLocked(win, true); if (type == TYPE_WALLPAPER) { mLastWallpaperTimeoutTime = 0; displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) { displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } else if (mWallpaperTarget != null && mWallpaperTarget.mLayer >= win.mBaseLayer) { // If there is currently a wallpaper being shown, and // the base layer of the new window is below the current // layer of the target window, then adjust the wallpaper. // This is to avoid a new window being placed between the // wallpaper and its target. displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } } win.mWinAnimator.mEnterAnimationPending = true; if (displayContent.isDefaultDisplay) { mPolicy.getContentInsetHintLw(attrs, outContentInsets); } else { outContentInsets.setEmpty(); } if (mInTouchMode) { res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE; } if (win.mAppToken == null || !win.mAppToken.clientHidden) { res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE; } mInputMonitor.setUpdateInputWindowsNeededLw(); boolean focusChanged = false; if (win.canReceiveKeys()) { focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS, false /*updateInputWindows*/); if (focusChanged) { imMayMove = false; } } if (imMayMove) { moveInputMethodWindowsIfNeededLocked(false); } assignLayersLocked(displayContent.getWindowList()); // Don't do layout here, the window must call // relayout to be displayed, so we'll do it there. if (focusChanged) { finishUpdateFocusedWindowAfterAssignLayersLocked(false /*updateInputWindows*/); } mInputMonitor.updateInputWindowsLw(false /*force*/); if (localLOGV) Slog.v( TAG, "New client " + client.asBinder() + ": window=" + win); if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) { reportNewConfig = true; } } if (reportNewConfig) { sendNewConfiguration(); } Binder.restoreCallingIdentity(origId); return res; }

上面代码会判断传的参数,看添加的是什么类型的窗口,然后最重要的函数是注释7处attach函数实现的代码,也就是链接SurfaceFlinger的过程。

7. SurfaceFlinger的链接

void attach() { if (WindowManagerService.localLOGV) Slog.v( TAG, "Attaching " + this + " token=" + mToken + ", list=" + mToken.windows); mSession.windowAddedLocked(); }

继续往下:

void windowAddedLocked() { if (mSurfaceSession == null) { if (WindowManagerService.localLOGV) Slog.v( WindowManagerService.TAG, "First window added to " + this + ", creating SurfaceSession"); mSurfaceSession = new SurfaceSession(); //8 if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i( WindowManagerService.TAG, " NEW SURFACE SESSION " + mSurfaceSession); mService.mSessions.add(this); } mNumWindow++; }

注释8处代码新建了一个SurfaceSession对象mSurfaceSession ,然后SurfaceSession的构造方法为:

/** Create a new connection with the surface flinger. */ public SurfaceSession() { mNativeClient = nativeCreate(); }

可以根据注释可以看出,是在这个构造方法中进行链接surface flinger的操作。

我们继续往下跟,找到nativeCreate函数的实现:

private static native int nativeCreate();

可以看到这是调用了native函数,实际上nativeCreate的实现代码在: /frameworks/base/core/jni/android_view_SurfaceSession.cpp 由:

static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ { "nativeCreate", "()I", (void*)nativeCreate }, { "nativeDestroy", "(I)V", (void*)nativeDestroy }, { "nativeKill", "(I)V", (void*)nativeKill } }; int register_android_view_SurfaceSession(JNIEnv* env) { int res = jniRegisterNativeMethods(env, "android/view/SurfaceSession", gMethods, NELEM(gMethods)); LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); jclass clazz = env->FindClass("android/view/SurfaceSession"); gSurfaceSessionClassInfo.mNativeClient = env->GetFieldID(clazz, "mNativeClient", "I"); return 0; }

这个是jni的动态注册的方式注册的nativeCreate函数,然后在本地代码的实现是本地类中的nativeCreate 函数:

static jint nativeCreate(JNIEnv* env, jclass clazz) { SurfaceComposerClient* client = new SurfaceComposerClient(); client->incStrong((void*)nativeCreate); return reinterpret_cast<jint>(client); }

Session与APP进程是一一对应的,它会进一步为当前进程建立SurfaceSession会话,可以这么理解:Session是APP同WMS通信的通道,SurfaceSession是WMS为APP向SurfaceFlinger申请的通信通道,同样 SurfaceSession与APP也是一一对应的,既然是同SurfaceFlinger通信的信使,那么SurfaceSession就应该握着SurfaceFlinger的代理,其实就是SurfaceComposerClient里的ISurfaceComposerClient mClient对象,它是SurfaceFlinger为每个APP封装一个代理,也就是 **进程 <-> Session <-> SurfaceSession <-> SurfaceComposerClient <-> ISurfaceComposerClient(BpSurfaceComposerClient) **五者是一条线。

至于ISurfaceComposerClient(BpSurfaceComposerClient) 究竟是怎么样一步步创建的,其实它是利用ComposerService这样一个单对象为每个APP在WMS端申请一个ISurfaceComposerClient对象,在WMS端表现为BpSurfaceComposerClient,在SurfaceFlinger端表现为BnSurfaceComposerClient,具体代码如下:

SurfaceComposerClient::SurfaceComposerClient() : mStatus(NO_INIT), mComposer(Composer::getInstance()) { } // 单例的,所以只有第一次的时候采用 void SurfaceComposerClient::onFirstRef() { sp<ISurfaceComposer> sm(ComposerService::getComposerService()); if (sm != 0) { sp<ISurfaceComposerClient> conn = sm->createConnection(); if (conn != 0) { mClient = conn; mStatus = NO_ERROR; } } } sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() { sp<ISurfaceComposerClient> bclient; sp<Client> client(new Client(this)); status_t err = client->initCheck(); if (err == NO_ERROR) { bclient = client; } return bclient; }

SurfaceFlinger可以说是Android UI渲染体系的核心,在Android系统启动时会启动SurfaceFlinger服务,它的主要作用就是被Android应用程序调用,把绘制(测量,布局,绘制)后的窗口(Surface)渲染到手机屏幕上。

最新回复(0)