在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<ViewRootImpl> mViewAncestor;
private final IWindowSession mWindowSession;
W(ViewRootImpl viewAncestor) {
mViewAncestor = new WeakReference<ViewRootImpl>(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化的跨進程處理.例如在<<Android P WindowManager (二) window添加時主要參數的分析(1) WindowSession。>>中點擊設置應用啓動時,會先添加一個Window{ba4a949 u0 Splash Screen com.android.settings},而這個Window就是由system_server創建的.
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)等.這個地方可以參考下老羅的<<Android窗口管理服務WindowManagerService對窗口的組織方式分析>>,這個是講的很詳細的,後面我也儘量簡單的寫寫.
總結
client(IWindow)主要有以下幾點:
(1)每個Window有不同的client,所以client被WindowManagerService用作了識別Window的key.
(2)client(IWindow)就同WindowSession相反了,WindowSession是從應用指向了服務,而client是從服務指向了應用,處理一些從WindowManagerService發到應用的操作.
(3)對於一些特殊的Window,也被用來生成WindowToken,例如一些自定義的懸浮窗等.