文章目录
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)渲染到手机屏幕上。