所有需要显示在屏幕上的内容,都是通过WindowManager来操作的,这就是我们通常所说的WMS(WindowManagerService),本篇我们一起来分析一下WindowManagerService、Surface、SurfaceFlinger、WindowManager之间是如何建立关系并交互的。我们通过Dialog来分析它们之间如何建立联系并交互。
首先当我们想要获取WindowManager的时候,发现它是一个抽象类,所以我们需要找到它的实现。
我们可以通过Context的getSystemService方法获取到对应的服务实例,而WindowManager就是在ContextImpl中注册的服务之一。
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx);
}});
原来WIndowManager的实例是WindowManagerImpl。
那么Dialog是如何获取到WindowManager的?
首先我们先分析下Dialog的构造函数:
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
可以发现,在构造函数中获取到WindowManager对象,并通过Window的setWindowManager方法将Window和WindowManager联系起来。
public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) {
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
最终通过new WindowManagerImpl(mContext, parentWindow)将WindowManager和Window联系起来,此时可以发现这个构建的WindowManager方法中多出一个parentWindow,表示和具体的WIndow相关联。
现在我们跳转到WindowManagerImpl中:
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Context mContext;
private final Window mParentWindow;
private WindowManagerImpl(Context context, Window parentWindow) {
mContext = context;
mParentWindow = parentWindow;
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
return new WindowManagerImpl(displayContext, mParentWindow);
}
@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);
}
}
可以发现WindowManagerImpl并没有做什么操作,添加View、移除View、更新View的操作最终都交给了WindowManagerGlobal类,并调用addView方法。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// 构建ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
// 设置布局
view.setLayoutParams(wparams);
// 将View添加到View列表中
mViews.add(view);
// 将root添加到root列表中
mRoots.add(root);
mParams.add(wparams);
}
// do this last because it fires off messages to start doing things
try {
// 调用ViewRootImpl的addView方法将View显示到界面上
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
throw e;
}
}
通过分析WindowManagerGlobal,可以发现主要操作是:
- 构建ViewRootImpl
- 将布局设置给View
- 将View、ViewRootImpl存储起来
- 通过setView将view显示到界面上
到此时,我们分析到了framework层,而WMS是在native层,那么framework层是如何与native层建立关系和交互的呢?
这个时候我们就需要看ViewRootImpl类了,这个类继承自Handler,是native与java层View系统通讯的桥梁,比如performTraversals函数就是收到绘制View的消息之后,通过调用measure、layout、draw来绘制整个视图。
首先我们先看构造函数:
public ViewRootImpl(Context context, Display display) {
mContext = context;
// 获取Window Session,与WindowManagerService之间建立联系
mWindowSession = WindowManagerGlobal.getWindowSession();
// 保存当前线程,更新UI只能在创建了ViewRootImpl的线程
// 所以通常情况下,我们只能在UI线程更新UI,但并不是不能在子线程中更新UI
mThread = Thread.currentThread();
}
我们继续点进WindowManagerGlobal.getWindowSession()方法:
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
// 获取WindowManagerService
IWindowManager windowManager = getWindowManagerService();
// 与WindowManagerService建立联系
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;
}
}
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
}
return sWindowManagerService;
}
}
最终通过ServiceManager.getService("window")获取WMS,并将WMS转换为IWindowManager。
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
通过getService方法可以发现,返回的是一个IBinder对象,这个也就能从侧面说明Framework和WMS之间是通过Binder机制进行通信的,获取到WMS之后再通过IWindowManager.Stub.asInterface方法将WMS的IBinder转换成WindowManager对象。最终通过openSession与WMS建立通信对话,之后就可以通过这个session,java层和native层之间就可以通信了。
这个时候View并不会显示在界面上,WMS只负责管理View的z-order,也就是管理哪个view应该显示在最上层。这个时候WindowManagerGlobal之后就调用了root.setView(view, wparams, panelParentView)方法,这个方法就是发起显示请求:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
// 请求布局
requestLayout();
// 向WMS发起请求
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
}
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
performTraversals();
}
}
private void performTraversals() {
// 获取Surface,用于图形绘制
// performMeasure
// performLayout
// performDraw
}
调用performTraversals方法,这个方法就是我们绘制界面的方法,里面会调用我们所熟悉的onMeasure、onLayout、onDraw方法。
private void performDraw() {
try {
// 调用绘制函数
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
private void draw(boolean fullRedrawNeeded) {
// 获取surface
Surface surface = mSurface;
if (!surface.isValid()) {
return;
}
// 绘制需要更新
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
// 使用硬件加速
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
// 使用硬件渲染绘制
mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
} else {
// 使用cpu绘制
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
}
if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
}
在draw函数中获取需要绘制的区域,再判断是否使用硬件加速。
通常情况下都是使用cpu绘制,也就是直接调用drawSoftware:
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;
// 获取canvas对象,用于framework层绘制
canvas = mSurface.lockCanvas(dirty);
} catch (Surface.OutOfResourcesException e) {
return false;
}
try {
// 开始绘制,从decorView开始绘制
mView.draw(canvas);
} finally {
// 释放canvas锁,并通知SurfaceFlinger更新
// private static native void nHwuiDraw(long renderer); 调用native方法
surface.unlockCanvasAndPost(canvas);
}
return true;
}
从上面的函数可以看出绘制的过程:
- 判断使用gpu还是cpu渲染
- 获取绘制的surface对象
- 通过surface获取并锁住canvas对象
- 从DecorView开始发起整个view树的绘制
- 解锁canvas对象,并通知surfaceFlinger对象更新视图,调用native方法。
到此为止,Activity、Dialog等组件的View就显示到用户的界面上了。