所有需要显示到屏幕上的内容(包括Activity等)都是通过WindowManager来操作的,看来WindowManager是一个非常重要的子系统,这就是我们常说的WMS(WindowManager Service)。我们只关心WindowManager和WindowManagerService(后续简称WMS)、Surface、SurfaceFlinger等建立关联以及交互的一个基本过程。
WindowManager是接口, 真正的实现是类WindowManagerImpl。
与WindowManager联系上的第一步就是通过Context的getSystemService方法,在上一章的单例模式中,各种系统服务会注册到SystemServiceRegistry 的一个map容器中,然后通过该服务的字串键来获取。
#ContextImpl
// The system service cache for the system services that are cached per-ContextImpl.
final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();
#SystemServiceRegistry
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
//构造WindowManagerImp对象
return new WindowManagerImpl(ctx);
}});
/**
* Creates an array which is used to cache per-Context service instances.
*/
#SystemServiceRegistry
public static Object[] createServiceCache() {
return new Object[sServiceCacheSize];
}
上述代码中可以发现,我们看到了WindowManager在Java层的具体实现,也就是WIndowManagerImpl。那么Dialog是如何获取到WindowManager对象的呢?我们从上述代码中可以知道,Windowmanager是注册到SystemServiceRegistry的,而ContextImp中存在一个ServiceCache(是一个Object类型的数组)。而getSystemService也是Context定义的接口,因此,需要先从Dialog的构造函数进行分析,因为Context对象就是从Dialog的构造函数传入的。
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
//设置包装Context相关的代码
if (createContextThemeWrapper) {
if (themeResId == ResourceId.ID_NULL) {
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
themeResId = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, themeResId);
} else {
mContext = context;
}
//1.获取WindowManager
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext);
mWindow = w;
//2.设置Window 的回调。
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setOnWindowSwipeDismissedCallback(() -> {
if (mCancelable) {
cancel();
}
});
//3.设置Window的WindowManager
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
Window是抽象类
通过上述代码可以发现,最终还是通过Window对象的setWindowManager函数将Window对象与WindowManager建立了联系,该函数在Window类中,我们看看这个函数的实现。
/**
* Set the window manager for use by this Window to, for example,
* display panels. This is <em>not</em> used for displaying the
* Window itself -- that must be done by the client.
*
* @param wm The window manager for adding new windows.
*/
#window
public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
setWindowManager(wm, appToken, appName, false);
}
/**
* Set the window manager for use by this Window to, for example,
* display panels. This is <em>not</em> used for displaying the
* Window itself -- that must be done by the client.
*
* @param wm The window manager for adding new windows.
*/
#window
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
//参数赋值等代码
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
//注意这里,调用了createLocalWindowManager函数
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
//这里的一句代码很重要,调用的是WindowManagerImpl中的createLocalWindowManager 方法
#WinodwManagerImpl
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
//重新构建的WindowManagerImpl与具体的WIndow关联。
return new WindowManagerImpl(mContext, parentWindow);
}
这个方法很简单,只是单纯地构建了一个WindowManagerImpl对象。与ContextImpl(SystemServiceRegistry)注册的WindowManagerImpl不同的是,这里多了一个parentWindow参数,也就是说,此时构造的WindowMangerImpl对象与具体的Window相关联,而在ContextImpl(SystemServiceRegistry)中注册的并没有此参数(只有一个ContextImpl)。此时,java层上Window对象就已经和WindowManger建立了第一步联系。
#WindowManagerImpl
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Context mContext;
private final Window mParentWindow;
private IBinder mDefaultToken;
public WindowManagerImpl(Context context) {
this(context, null);
}
private WindowManagerImpl(Context context, Window parentWindow) {
mContext = context;
mParentWindow = parentWindow;
}
... ...
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
... ....
}
显然,WindowManagerImp也不是具体“干活”的对象,添加View、移除View、更新View的布局等具体的工作都交给了WindowManagerGlobal这个类,在这一切就绪之后,会调用WindowManager的addView方法请求系统将该View显示到屏幕,经过上述代码可知,实际上调用的是WindowManagerGlobal中的addView方法。
#WindowManagerGlobal
//将View添加的到WindowManager中,也就是在手机窗口中显示该View。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent, then hardware acceleration for this view is
// set from the application's hardware acceleration setting.
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
//检查参数有效性
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
//构建ViewRootImpl
//是作为native层与java层View系统通信的桥梁
root = new ViewRootImpl(view.getContext(), display);
// 给View设置布局参数
view.setLayoutParams(wparams);
//将View添加到View列表中
mViews.add(view);
//将ViewRootImpl对象root添加到mRoots对象中
mRoots.add(root);
mParams.add(wparams);
/// M: Add log for tracking mViews.
Log.d("WindowClient", "Add to mViews: " + view + ", this = " + this);
// do this last because it fires off messages to start doing things
try {
//调用ViewRootImpl的setView方法将View显示到手机窗口中
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
上面的程序主要分为以下4个步骤:
1.构建ViewRootImpl;
2.将布局参数设置给View;
3.存储这些ViewRootImpl、View、LayoutParams到列表中。
4.通过ViewRootImpl的setView将View显示到窗口上。
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks
final class ViewRootHandler extends Handler
ViewRootImpl并不是一个View,(它继承自Handler类(现不是)),是作为native层与java层View系统通信的桥梁,比如我们熟知的private void performTraversals()函数就是收到系统绘制View的消息之后,通过调用视图的各个节点的measure、layout、draw方法来绘制整棵视图树。
既然ViewRootImpl是Framework层与Native层的通信桥梁,ViewRootImpl很有可能是与Native层建立联系的关键点,不过我们这里只讨论与WMS的关联。
从WindowManagerGlobal的addView函数中可以看到,第一个重要步骤就是构建了ViewRootImpl对象root,我们看看它的构造函数。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
... ...
root = new ViewRootImpl(view.getContext(), display);
... ...
}
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
... ....
}
public ViewRootImpl(Context context, Display display) {
mContext = context;
//1.获取Window Session,也就是和WindowManagerService建立连接
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mBasePackageName = context.getBasePackageName();
//保存当前线程,更新UI的线程只能是创建ViewRoorImpl时的线程
//我们在应用的开发中,如果在子线程中更新UI会抛出异常,但并不是因为只有UI线程才能更新UI
//而是因为ViewRootImpl是在UI线程中创建的
mThread = Thread.currentThread();
... ...
}
我们看到ViewRootImpl的代码1处,有一个WindowManagerGlobal.getWindowSession()方法,通过函数名以及WindowManagerGlobal的作用来看,这很有可能是与Native层建立通信的地方。
#WindowManagerGlobal
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
//1.获取WindowManagerService
IWindowManager windowManager = getWindowManagerService();
2.与WindowManagerService建立一个Session
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
#WindowManagerGlobal
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
try {
if (sWindowManagerService != null) {
ValueAnimator.setDurationScale(
sWindowManagerService.getCurrentAnimatorScale());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowManagerService;
}
}
在getWindowSession函数中,Framework层首先通过getWindowManagerService()函数获取到IWindowManager对象,该函数中通过ServiceManager.getService函数获取WMS,并且将WMS转换为IWindowManger类型,我们先看看ServiceManager.getService函数代码。
/**
* Returns a reference to a service with the given name.
*
* @param name the name of the service to get
* @return a reference to the service, or <code>null</code> if the service doesn't exist
*/
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return Binder.allowBlocking(getIServiceManager().getService(name));
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
从程序中可以看到ServiceManager.getService返回的IBinder对象,也就是说Android Framework与WMS之间也是通过Binder机制进行通信,到了这一步我们已经与WMS建立了初步联系。
获取WMS之后,又调用了IWindowManager.Stub类的asInterface函数,将获取到的WMS的IBinder对象转换成WindowManager对象,最后通过openSession函数与WMS建立一个通信会话,相当于Framework层与Native层建立了一个长期的“办事处”,双方有什么需求都是通过这个Session来交换信息。
但是,此时Dialog或者Activity的View并不能显示在手机屏幕上,WMS只是负责管理手机屏幕上View的z-order,也就是说WMS管理当前状态下哪个View应该显示在最上层显示。仔细想一想,你发现其实WMS管理的并不是Window,而是View,只不过它管理的是属于某一个Window下的View。
与WMS建立Session之后就到了调用ViewRootImpl的setView方法了,该方法会向WMS发起显示Dialog或者Activity中DecorView的请求。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
//1.请求布局
requestLayout();
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
//2.向WMS发起请求
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
... ...
}
}
}
setView很复杂,但是我们主要关注两步:
1.requestLayout
2.向WMS发起显示当前Window的请求
我们先看看requestLayout函数。
#ViewRootImpl
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
if (ViewDebugManager.DEBUG_REQUESTLAYOUT) {
Log.d(mTag, "requestLayout: mView = " + mView + ", this = " + this,
new Throwable("requestLayout"));
}
checkThread();
mLayoutRequested = true;
scheduleTraversals();//发送 DO_TRAVERSAL
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
if (ViewDebugManager.DEBUG_SCHEDULETRAVERSALS) {
Log.v(mTag, "scheduleTraversals: mTraversalBarrier = " + mTraversalBarrier
+ ",this = " + this, new Throwable("scheduleTraversals"));
}
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
就是往handler发送一个DO_TRAVERSAL消息,这个消息会触发整个视图树的绘制操作,也就是最终会执行performTraversals函数,这是一个极为复杂的但又非常重要的函数。
private void performTraversals() {
... ...
//1.获取Surface对象,用于图形绘制
//2.丈量整个视图树的各个View的大小,performMeasure函数
//3.布局整个视图树,performLayout函数
//4.绘制整棵视图树,performDraw函数
}
在第四步中,Framework会获取到图形绘制表面Surface对象,然后获取它的可绘制区域,也就是我们的Canvas对象,然后Framework再这个Canvas对象上绘制。
private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
} else if (mView == null) {
return;
}
final boolean fullRedrawNeeded = mFullRedrawNeeded;
mFullRedrawNeeded = false;
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
try {
//调用绘制函数
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
// For whatever reason we didn't create a HardwareRenderer, end any
// hardware animations that are now dangling
if (mAttachInfo.mPendingAnimatingRenderNodes != null) {
final int count = mAttachInfo.mPendingAnimatingRenderNodes.size();
for (int i = 0; i < count; i++) {
mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators();
}
mAttachInfo.mPendingAnimatingRenderNodes.clear();
}
if (mReportNextDraw) {
mReportNextDraw = false;
// if we're using multi-thread renderer, wait for the window frame draws
if (mWindowDrawCountDown != null) {
try {
mWindowDrawCountDown.await();
} catch (InterruptedException e) {
Log.e(mTag, "Window redraw count down interruped!");
}
mWindowDrawCountDown = null;
}
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.fence();
mAttachInfo.mThreadedRenderer.setStopped(mStopped);
}
if (DEBUG_DRAW) {
Log.v(mTag, "FINISHED DRAWING: " + mWindowAttributes.getTitle()
+ ", this = " + this);
}
if (mSurfaceHolder != null && mSurface.isValid()) {
SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::postDrawFinished);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
} else {
pendingDrawFinished();
}
}
}
#ViewRootImpl
private void draw(boolean fullRedrawNeeded) {
//1.获取绘制表面
Surface surface = mSurface;
if (!surface.isValid()) {
return;
}
... ...
//代码省略
//2.绘图表面需要更新
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
//使用GPU绘制,也就是硬件加速
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
... ...
//代码省略
//使用硬件渲染器绘制
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
... ...
//代码省略
//使用CPU绘制图形
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
}
if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
}
在draw函数中会获取到需要绘制的区域,以及判断是否使用GPU进行绘制。通常情况下使用的是CPU绘制,也就是调用的是drawSoftware函数来绘制。
/**
* @return true if drawing was successful, false if an error occurred
*/
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
// Draw with software renderer.
final Canvas canvas;
try {
final int left = dirty.left;
final int top = dirty.top;
final int right = dirty.right;
final int bottom = dirty.bottom;
//1.获取指定区域的Canvas对象,用于framework层绘图
canvas = mSurface.lockCanvas(dirty);
// ... ...
// The dirty rectangle can be modified by Surface.lockCanvas()
//noinspection ConstantConditions
if (left != dirty.left || top != dirty.top || right != dirty.right
|| bottom != dirty.bottom) {
attachInfo.mIgnoreDirtyState = true;
}
// TODO: Do this in native
canvas.setDensity(mDensity);
}catch(){
}
try {
... ...
}
try {
... ...
//从DecorView开始绘制,也就是整个Window的根视图,这会引起整棵树的重绘。
mView.draw(canvas);
... ...
} finally {
if (!attachInfo.mSetIgnoreDirtyState) {
// Only clear the flag if it was not set during the mView.draw() call
attachInfo.mIgnoreDirtyState = false;
}
}
} finally {
try {
//释放Canvas锁,然后通知SurfaceFlinger更新这块区域
surface.unlockCanvasAndPost(canvas);
} catch (IllegalArgumentException e) {
}
}
mPerfFrameInfoManager.setSoftwareDraw();
return true;
}
上述的视图树绘制代码主要分为以下几个步骤:
1.判断使用CPU绘制还是使用GPU绘制
2.获取绘制表面Surface对象
3.通过Surface对象获取并锁住Canvas绘图对象
5.Surface对象解锁Canvas,并且通知SurfaceFlinger更新视图
内容绘制完毕请求WMS显示该窗口上的内容。
参考《Android源码设计模式》