在WindowManagerService.addWindow每次的client(IWindow)是不同的,而client是什么呢?这个要从这个client在应用的创建开始了
(1)应用端client的初始化和传递
frameworks/base/core/java/android/view/ViewRootImpl.java
//在ViewRootImpl的构造器中创建了client在应用端的实现mWindow
public ViewRootImpl(Context context, Display display) {
/*......*/
mWindow = new W(this);
/*......*/
}
//在ViewRootImpl.setView中调用到了session传给WindowManagerService去处理
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
/*......*/
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
/*......*/
}
//client在应用中的是一个AIDL的服务端实现,那就说明WindowManagerService端的话就是一个客户端的实现了.
static class W extends IWindow.Stub {
private final WeakReference mViewAncestor;
private final IWindowSession mWindowSession;
W(ViewRootImpl viewAncestor) {
mViewAncestor = new WeakReference(viewAncestor);
mWindowSession = viewAncestor.mWindowSession;
}
/*......*/
}
client在应用端的创建可以看到,应用端的client就是一个IWindow.Stub的实现,依附与ViewRootImpl,通过session.addToDisplay传递给服务端要在服务端生成一个AIDL的客户端,也就是说在WindowManagerService,它就是一个client.
(2)服务端的client(IWindow)
frameworks/base/base/services/core/java/com/android/server/wm/WindowManagerService.java
//在addWindow中吧这个client添加到mWindowMap中
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
/*......*/
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
/*......*/
mWindowMap.put(client.asBinder(), win);
/*......*/
}
/*************************************************/下面是client的几个作用我用万能分割符分开下
mWindowMap是WindowHashMap类型的变量,WindowHashMap继承了HashMap,它限制了HashMap的key值的类型为IBinder,value值的类型为WindowState。也就是说在WindowManagerService中保存Window信息的存储器了.而且也可以发现的是client就是WindowManagerService区分Window的key,只要知道了client那么对应的Window也就能获取到了.
/*************************************************/
另外在上面的代码中WindowState初始化时也用到了client,这里就真正用到client的AIDL的作用了,通过WindowState获取client,然后回调应用端的"class W extends IWindow.Stub".
frameworks/base/base/services/core/java/com/android/server/wm/WindowState.java
final IWindow mClient;
private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
MergedConfiguration mergedConfiguration, boolean reportOrientation, int displayId,
DisplayCutout displayCutout)
throws RemoteException {
final boolean forceRelayout = isDragResizeChanged() || reportOrientation;
mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets,
reportDraw, mergedConfiguration, getBackdropFrame(frame), forceRelayout,
mPolicy.isNavBarForcedShownLw(this), displayId,
new DisplayCutout.ParcelableWrapper(displayCutout));
mDragResizingChangeReported = true;
}
这里很有意思的是IWindow.aidl是oneway模式,这是由于在WindowManagerService中我们拿到的是一个client的客户端,client操作是会通过Binder调用到应用端的"class W extends IWindow.Stub",但是说到底WindowManagerService是一个管理者,管理者可以通过client这个喇叭通知应用端要做什么,但不能等着应用端做完之后给我们回复了我们再去通知下一个,否则的话工作的效率就太低了.
frameworks/base/core/java/android/view/IWindow.aidl
/**
* API back to a client window that the Window Manager uses to inform it of
* interesting things happening.
*
* {@hide}
*/
oneway interface IWindow {
}
还有一处有意思的是当服务端的client是一个"class W extends IWindow.Stub"时,说明这个时候添加的Window是由system_server创建的,因为是同一个进程,所以client传递过程没有经过Binder化的跨进程处理.例如在<
02-27 11:45:04.600 2590 2691 V lishuo : *** ADD session :Session{e5dc9ec 2590:1000}
02-27 11:45:04.600 2590 2691 V lishuo : *** ADD client.asBinder :android.view.ViewRootImpl$W@f957350
02-27 11:45:04.600 2590 2691 V lishuo : *** ADD windowState :Window{ba4a949 u0 Splash Screen com.android.settings}
由于服务端调用client时没有办法通过oneway模式,所以WindowManagerService就要对一些耗时的操作做了些处理.
frameworks/base/base/services/core/java/com/android/server/wm/WindowState.java
final IWindow mClient;
void reportResized() {
/*......*/
if (mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
&& mClient instanceof IWindow.Stub) {
// To prevent deadlock simulate one-way call if win.mClient is a local object.
// 为了防止死锁,如果win.mclient是本地对象,则模拟单向调用。
mService.mH.post(new Runnable() {
@Override
public void run() {
try {
dispatchResized(frame, overscanInsets, contentInsets, visibleInsets,
stableInsets, outsets, reportDraw, mergedConfiguration,
reportOrientation, displayId, displayCutout);
} catch (RemoteException e) {
// Not a remote call, RemoteException won't be raised.
}
}
});
} else {
dispatchResized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
outsets, reportDraw, mergedConfiguration, reportOrientation, displayId,
displayCutout);
}
/*......*/
}
/*************************************************/
client在WindowManagerService中的另外一个作用也在WindowManagerService.addWindow:
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
/*......*/
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
if (token == null) {
if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG_WM, "Attempted to add application window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_VOICE_INTERACTION) {
Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_WALLPAPER) {
Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_DREAM) {
Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_QS_DIALOG) {
Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_TOAST) {
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
parentWindow)) {
Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow);
}else{
/*......*/
}
此处就用来作为binder来生成WindowToken了,WindowToken就是用来同步AM和WM的操作了(AppWindowToken)等.这个地方可以参考下老罗的<
总结
client(IWindow)主要有以下几点:
(1)每个Window有不同的client,所以client被WindowManagerService用作了识别Window的key.
(2)client(IWindow)就同WindowSession相反了,WindowSession是从应用指向了服务,而client是从服务指向了应用,处理一些从WindowManagerService发到应用的操作.
(3)对于一些特殊的Window,也被用来生成WindowToken,例如一些自定义的悬浮窗等.