前兩天寫Activity啓動流程時挖個坑,ActivityThread調用Activity.resume後,緊接着調用WindowManager.addView()用來正在的顯示View,以前講的很草率,如今感受有必要寫一下WindowManager的調用流程。android
本文基於android8.1.0_r15分支分析緩存
WindowManager簡單介紹bash
首先WindowManager是一個接口繼承自ViewManager,它做用是用於窗口的管理,咱們平時的使用比較多的是addView方法和LayoutParams參數,addView定義在基類ViewManager中,源碼:session
ViewManagerapp
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
複製代碼
一共三個方法比較簡單,注意看參數類型,咱們平時用WindowManager時候傳的是WindowManager.LayoutParams,這裏定義的是ViewGroup.LayoutParams params,我想確定前者繼承後者吧,啥都不說,看看WindowManager.LayoutParams定義;ide
經常使用的type值ui
WindowManager畢竟只是一個接口,真正的實如今哪裏?回想在ActivityThread.handleResumeActivity()中,調用了activity.getWindowManager(),咱們定位到Activity:this
Activity#getWindowManagerspa
public WindowManager getWindowManager() {
return mWindowManager;
}
複製代碼
在Activity#getSystemService()中一樣能夠獲得代理
@Override
public Object getSystemService(@ServiceName @NonNull String name) {
if (getBaseContext() == null) {
throw new IllegalStateException(
"System services not available to Activities before onCreate()");
}
//WINDOW_SERVICE比較特殊,不調用super的
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
} else if (SEARCH_SERVICE.equals(name)) {
ensureSearchManager();
return mSearchManager;
}
return super.getSystemService(name);
}
複製代碼
咱們注意到在調用getSystemService時,WINDOW_SERVICE比較特殊,沒有調用super,直接返回mWindowManager,爲何呢,咱們留到後文說;
mWindowManager何時賦值的呢,咱們在Activity.attach中找到了代碼
Activity.attach
//設置系統的WindowManager
mWindow.setWindowManager(
//獲取系統的WindowManager
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
//拿Window對象的
mWindowManager = mWindow.getWindowManager();
複製代碼
也是直接獲取系統的,可是通過mWindow轉手了,這是爲何呢,Window關於setWindowManager()定義
Window#setWindowManager
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);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
複製代碼
正在的實現是WindowManagerImpl,咱們查看WindowManagerImpl.addView方法:
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
調用mGlobal
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
if (mDefaultToken != null && mParentWindow == null) {
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (wparams.token == null) {
wparams.token = mDefaultToken;
}
}
}
複製代碼
方法體首先執行applyDefaultToken,對wparams賦token值,而後直接調用mGlobal對應的方法,mGlobal是WindowManagerGlobal類型,它也繼承自WindowManager麼?接下來分析WindowManagerGlobal
WindowManagerGlobal
WindowManagerGlobal並非繼承WindowManager,可是有着同樣的方法名,這不是代理模式麼,它一樣有addView、updateViewLayout、removeView方法,咱們從addView方法分析
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");
}
//轉成WindowManager.LayoutParams對象
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
//顯示Activity的話window確定不爲空
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
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);
}
//從mViews集合查找有沒有添加過
int index = findViewLocked(view, false);
//有則調用對應的ViewRootImpl.doDie
if (index >= 0) {
if (mDyingViews.contains(view)) {
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對象
root = new ViewRootImpl(view.getContext(), display);
//參數最終給了decor
view.setLayoutParams(wparams);
mViews.add(view);//把Decor存起來
mRoots.add(root);//把ViewRootImpl純起來
mParams.add(wparams);//把參數存起來
// do this last because it fires off messages to start doing things
try {
//設置View
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
複製代碼
簡單分析addView做用:
繼續分析updateViewLayout和removeView方法:
WindowManagerGlobal#updateViewLayout#removeView
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
view.setLayoutParams(wparams);
synchronized (mLock) {
//去除以前的view對應的下標
int index = findViewLocked(view, true);
//緩存的root
ViewRootImpl root = mRoots.get(index);
//mParams集合從新添加
mParams.remove(index);
mParams.add(index, wparams);
//最終調用viewRootImpl的setLayoutParams
root.setLayoutParams(wparams, false);
}
}
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
//集合裏的全都刪除掉
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
//關掉輸入法
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
//調用ViewRootImpl.die
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
複製代碼
更新和刪除的代碼比較簡單,註釋說的很清楚了,只要就是管理集合和調用ViewRootImpl的對應代碼
調用順序總結:
意義總結:
到這裏尚未真正的看到IPC調用,由於一層一層代理,到了RootViewImpl這一層,真假美猴王應該能分曉了吧。
RootViewImpl調用IPC
上文分析RootViewImpl在WindowManagerGlobal.addView中構造,而且有三個方法setView()、setLayoutParams()、doDie()被調用,可是看了RootViewImpl代碼頭髮有些發麻,代碼量太大了。
因爲鄙人水平有些,這篇博客只拿出它和WMS的IPC調用過程分析,這代碼只能一片一片剖析。見諒啊!開始吧。
RootViewImpl構造方法
咱們發現RootViewImpl構造方法建立了兩個window相關的對象,分別是
mWindowSession = WindowManagerGlobal.getWindowSession();
mWindow = new W(this);
複製代碼
跟進WindowManagerGlobal看個究竟
WindowManagerGlobal#getWindowSession#getWindowManagerService
//獲取WindowSession
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
//通知WMS開啓一個新的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;
}
}
//獲取WindowManagerService
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;
}
}
複製代碼
再看W這個類,它繼承自IWindow.Stub,若是看過Activity啓動流程的應該知道,這個W和ApplicationThread同樣的道理啊,咱們接着看ViewRootImpl關鍵是的三個方法有沒有mWindowSession和mWindow的調用
ViewRootImpl#setView
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
複製代碼
ViewRootImpl#doDie
mWindowSession.finishDrawing(mWindow);
複製代碼
IWindowSession.aild
interface IWindowSession {
int add(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets,
out InputChannel outInputChannel);
int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outContentInsets,
out Rect outStableInsets, out Rect outOutsets, out InputChannel outInputChannel);
int addWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets);
int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outContentInsets,
out Rect outStableInsets);
void remove(IWindow window);
...
}
複製代碼
IWindow.aidl
oneway interface IWindow {
void executeCommand(String command, String parameters, in ParcelFileDescriptor descriptor);
void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets,
in Rect visibleInsets, in Rect stableInsets, in Rect outsets, boolean reportDraw,
in MergedConfiguration newMergedConfiguration, in Rect backDropFrame,
boolean forceLayout, boolean alwaysConsumeNavBar, int displayId);
void moved(int newX, int newY);
void dispatchAppVisibility(boolean visible);
void dispatchGetNewSurface();
void windowFocusChanged(boolean hasFocus, boolean inTouchMode);
void closeSystemDialogs(String reason);
...
}
複製代碼
Session#addToDisplay
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
//調用WindowManagerService的addWindow
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
複製代碼
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) {
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
boolean reportNewConfig = false;
WindowState parentWindow = null;
long origId;
final int callingUid = Binder.getCallingUid();
final int type = attrs.type;
synchronized(mWindowMap) {
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}
...
}
}
複製代碼
既然涉及到binder雙向通訊,app端確定得有handle來處理,果真在ViewRootImpl中定義了ViewRootHandler
這個類。
ViewRootHandler
final class ViewRootHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
break;
case MSG_INVALIDATE_RECT:
final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
info.target.invalidate(info.left, info.top, info.right, info.bottom);
info.recycle();
break;
case MSG_PROCESS_INPUT_EVENTS:
mProcessInputEventsScheduled = false;
doProcessInputEvents();
break;
case MSG_DISPATCH_APP_VISIBILITY:
handleAppVisibility(msg.arg1 != 0);
break;
case MSG_DISPATCH_GET_NEW_SURFACE:
handleGetNewSurface();
break;
case MSG_RESIZED: {
// Recycled in the fall through...
SomeArgs args = (SomeArgs) msg.obj;
if (mWinFrame.equals(args.arg1)
&& mPendingOverscanInsets.equals(args.arg5)
&& mPendingContentInsets.equals(args.arg2)
&& mPendingStableInsets.equals(args.arg6)
&& mPendingVisibleInsets.equals(args.arg3)
&& mPendingOutsets.equals(args.arg7)
&& mPendingBackDropFrame.equals(args.arg8)
&& args.arg4 == null
&& args.argi1 == 0
&& mDisplay.getDisplayId() == args.argi3) {
break;
}
}
...
}
}
複製代碼
畫一張圖標識記憶一下吧。