在Android的世界里茁计,我們可以通過WindowManager
將一個(gè)視圖添加到屏幕上镰吵。下面就是實(shí)現(xiàn)此需求的兩條關(guān)鍵語句:
WindowManager wm = (WindowManager) contex.getSystemService(Context.WINDOW_SERVICE);
...
wm.addView(view, layoutParam);
這篇文章將以這兩條語句作為切入點(diǎn),探究一下與WindowManager
相關(guān)的源碼(基于5.1.1系統(tǒng))翼雀。
WindowManager
是什么
根據(jù)WindowManager
的定義
public interface WindowManager extends ViewManager
可以看出WindowManager
是一個(gè)繼承自ViewManager
的接口膘融。所以WindowManager
繼承了ViewManager
中的兩個(gè)主要函數(shù):
public void addView(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
從函數(shù)名便可推斷這兩個(gè)函數(shù)的作用分別是往屏幕添加視圖以及移除之前添加的視圖。
既然WindowManager
只是一個(gè)接口划咐,那必然有類實(shí)現(xiàn)了這個(gè)接口拴念。讓我們回到這條語句
WindowManager wm = (WindowManager) contex.getSystemService(Context.WINDOW_SERVICE);
去探究一下通過contex.getSystemService(Context.WINDOW_SERVICE)
拿到的WindowManager
到底是什么。
因?yàn)榕cUI緊密相關(guān)的Context
是Activity
褐缠,所以假定這里的context
是一個(gè)Activity
政鼠。
首先看一下Activity
中getSystemService
的實(shí)現(xiàn):
@Override
5033 public Object getSystemService(@ServiceName @NonNull String name) {
5034 if (getBaseContext() == null) {
5035 throw new IllegalStateException(
5036 "System services not available to Activities before onCreate()");
5037 }
5038
5039 if (WINDOW_SERVICE.equals(name)) {
5040 return mWindowManager;
5041 } else if (SEARCH_SERVICE.equals(name)) {
5042 ensureSearchManager();
5043 return mSearchManager;
5044 }
5045 return super.getSystemService(name);
5046 }
通過5039行的if
語句可以看出,如果參數(shù)name
是WINDOW_SERVICE
則直接返回Activity
的成員變量mWindowManager
. 接下來需要找到mWindowManager
被初始化的地方队魏。它的初始化緊隨Activity
的初始化缔俄。而Activity
的初始化在ActivityThread.performLaunchActivity
中進(jìn)行。這里的ActivityThread
就是我們通常所說的主線程或者UI線程。摘取ActivityThread.performLaunchActivity
中的幾條關(guān)鍵語句:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
...
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor);
當(dāng)activity
被創(chuàng)建出來后其attach
方法即被調(diào)用俐载。
5922 final void attach(Context context, ActivityThread aThread,
5923 Instrumentation instr, IBinder token, int ident,
5924 Application application, Intent intent, ActivityInfo info,
5925 CharSequence title, Activity parent, String id,
5926 NonConfigurationInstances lastNonConfigurationInstances,
5927 Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
...
5932 mWindow = PolicyManager.makeNewWindow(this);
...
5966 mWindow.setWindowManager(
5967 (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
5968 mToken, mComponent.flattenToString(),
5969 (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
5970 if (mParent != null) {
5971 mWindow.setContainer(mParent.getWindow());
5972 }
5973 mWindowManager = mWindow.getWindowManager();
5974 mCurrentConfig = config;
5975 }
在第5932行蟹略,Activity
的成員變量mWindow
是一個(gè)Window
類對(duì)象。Window
是對(duì)Activity
或者Dialog
的視圖的一種上層抽象遏佣。Window
是一個(gè)抽象類挖炬,而PhoneWindow
繼承了Window
并且實(shí)現(xiàn)了其中的一些關(guān)鍵方法。PhoneWindow
中有一個(gè)類型為DecorView
的成員變量mDecor
状婶,表示的是Activity
對(duì)應(yīng)的視圖的最外層容器意敛。PolicyManager.makeNewWindow(this)
正好返回了一個(gè)PhoneWindow
對(duì)象。
接下來在5966行膛虫,Window.setWindowManager
被調(diào)用草姻。該函數(shù)需要四個(gè)參數(shù)。首先我們看第一個(gè)參數(shù)(WindowManager)context.getSystemService(Context.WINDOW_SERVICE)
. 在這里context.getSystemService(Context.WINDOW_SERVICE)
又一次被調(diào)用并且返回一個(gè)WindowManager
對(duì)象稍刀。不過這里的context
不是某個(gè)Activity
對(duì)象撩独,而是一個(gè)ContextImpl
對(duì)象。接下來看一下ContextImpl.getSystemService
的實(shí)現(xiàn)账月。
@Override
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
可以看到getSystemService
會(huì)根據(jù)參數(shù)name
從SYSTEM_SERVICE_MAP
中拿到對(duì)應(yīng)的ServiceFetcher
對(duì)象综膀,然后通過ServiceFetcher.getService
返回具體的對(duì)象。在ContextImpl
中有一個(gè)函數(shù)的作用是往SYSTEM_SERVICE_MAP
中放入(name, ServiceFetcher)
映射局齿。
private static void registerService(String serviceName, ServiceFetcher fetcher) {
if (!(fetcher instanceof StaticServiceFetcher)) {
fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
}
SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
}
另外在ContextImpl
中有一個(gè)static語句塊剧劝,里面通過多次調(diào)用registerService
方法將所有可能的(name, ServiceFetcher)
映射放入了SYSTEM_SERVICE_MAP
. 這里我們特別關(guān)注一下WINDOW_SERVICE
:
634 registerService(WINDOW_SERVICE, new ServiceFetcher() {
635 Display mDefaultDisplay;
636 public Object getService(ContextImpl ctx) {
637 Display display = ctx.mDisplay;
638 if (display == null) {
639 if (mDefaultDisplay == null) {
640 DisplayManager dm = (DisplayManager)ctx.getOuterContext().getSystemService(Context.DISPLAY_SERVICE);
642 mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
643 }
644 display = mDefaultDisplay;
645 }
646 return new WindowManagerImpl(display);
647 }});
可以看到getService
返回了一個(gè)WindowManagerImpl
對(duì)象。之前提到WindowManager
只是一個(gè)接口抓歼,而這里的WindowManagerImpl
正好實(shí)現(xiàn)了WindowManager
.
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Display mDisplay;
private final Window mParentWindow;
private IBinder mDefaultToken;
public WindowManagerImpl(Display display) {
this(display, null);
}
private WindowManagerImpl(Display display, Window parentWindow) {
mDisplay = display;
mParentWindow = parentWindow;
}
...
}
有一點(diǎn)需要注意:這里用到了WindowManagerImpl
的只需一個(gè)參數(shù)的構(gòu)造函數(shù)讥此。通過上面的類的定義可以看到,WindowManagerImpl
還有一個(gè)需要兩個(gè)參數(shù)的構(gòu)造函數(shù)谣妻,這個(gè)構(gòu)造函數(shù)的調(diào)用接下來就會(huì)看到暂论。還有我們可以看到WindowManagerImpl
中有一個(gè)類型為WindowManagerGlobal
的成員變量mGlobal
,使用了單例模式拌禾,它的作用將在下節(jié)說明。
現(xiàn)在回到Activity.attach
方法的5966行展哭,我們已經(jīng)知道第一個(gè)參數(shù)是一個(gè)WindowManagerImpl
對(duì)象湃窍。接著就看一下Window.setWindowManager
的具體實(shí)現(xiàn)。
539 public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
540 boolean hardwareAccelerated) {
541 mAppToken = appToken;
542 mAppName = appName;
543 mHardwareAccelerated = hardwareAccelerated
544 || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
545 if (wm == null) {
546 wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
547 }
548 mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
549 }
這里有兩個(gè)比較關(guān)鍵的點(diǎn):
第541行匪傍,將參數(shù)
appToken
賦給了Window
的成員變量mAppToken
.appToken
是一個(gè)Binder
對(duì)象您市,在ActivityManagerService
,WindowManagerService
等系統(tǒng)服務(wù)中通過它來標(biāo)識(shí)Activity
. 讓Window
的成員變量mAppToken
指向appToken
,就好比這個(gè)Window
得到了與之對(duì)應(yīng)的Activity
的身份通行證役衡。這樣一來茵休,WindowManagerService
就能知道這個(gè)Window
是屬于哪個(gè)Activity
了。-
第548行,調(diào)用
WindowManagerImpl.createLocalWindowManager
創(chuàng)建了一個(gè)新的WindowManagerImpl
對(duì)象榕莺,并賦給了成員變量mWindowManager
.WindowManagerImpl.createLocalWindowManager
的實(shí)現(xiàn)如下:public WindowManagerImpl createLocalWindowManager(Window parentWindow) { return new WindowManagerImpl(mDisplay, parentWindow); }
可以看到這里用到了
WindowManagerImpl
中需要兩個(gè)參數(shù)的構(gòu)造函數(shù)俐芯,第二個(gè)參數(shù)是對(duì)應(yīng)的Window
.
再次回到Activity.attach
方法。第5973行將Window
的成員變量mWindowManager
指向的WindowManagerImpl
對(duì)象賦給了Activity
的成員變量mWindowManager
. 至此钉鸯,我們就知道了通過Activity.getSystemService(Context.WINDOW_SERVICE);
拿到的是一個(gè)WindowManager
的實(shí)現(xiàn)類WindowManagerImpl
的對(duì)象吧史。對(duì)于非Activity
的Context
,調(diào)用它們的getSystemService
方法實(shí)際上會(huì)調(diào)用ContextImpl.getSystemService
. 這個(gè)方法返回的也是一個(gè)WindowManagerImpl
的對(duì)象唠雕。只不過這個(gè)WindowManagerImpl
的對(duì)象的成員變量mParentWindow
為null
贸营,也就是沒有關(guān)聯(lián)任何Window
對(duì)象。
WindowManager.addView
做了什么
接下來探究一下WindowManager.addView
到底做了什么岩睁。
通過上面的分析钞脂,我們已經(jīng)知道實(shí)現(xiàn)WindowManager
這個(gè)接口的是WindowManagerImpl
類,因此直接看一下WindowManagerImpl.addView
的實(shí)現(xiàn):
@Override
public void addView(@NonNull View view,
@NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
函數(shù)就只有兩句話捕儒,我們看關(guān)鍵的第二句冰啃。這里的mGlobal
就是之前提到過的WindowManagerGlobal
對(duì)象。它是一個(gè)單例肋层,也就意味著在一個(gè)應(yīng)用進(jìn)程里亿笤,雖然每一個(gè)Activity
會(huì)對(duì)應(yīng)不同的WindowManagerImpl
對(duì)象,但是它們的視圖是由唯一的一個(gè)WindowManagerGlobal
對(duì)象統(tǒng)一管理栋猖。
在看WindowManagerGlobal.addView
的實(shí)現(xiàn)之前净薛,有必要先說明一下WindowManagerGlobal
中有三個(gè)比較重要的ArrayList
:
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
這三個(gè)ArrayList
分別保存了View
, ViewRootImpl
和WindowManager.LayoutParams
.
接下來就具體分析一下WindowManagerGlobal.addView
.
204 public void addView(View view, ViewGroup.LayoutParams params,
205 Display display, Window parentWindow) {
206 if (view == null) {
207 throw new IllegalArgumentException("view must not be null");
208 }
209 if (display == null) {
210 throw new IllegalArgumentException("display must not be null");
211 }
212 if (!(params instanceof WindowManager.LayoutParams)) {
213 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
214 }
215
216 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
217 if (parentWindow != null) {
218 parentWindow.adjustLayoutParamsForSubWindow(wparams);
219 } else {
220 // If there's no parent and we're running on L or above (or in the
221 // system context), assume we want hardware acceleration.
222 final Context context = view.getContext();
223 if (context != null
224 && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
225 wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
226 }
227 }
228
229 ViewRootImpl root;
230 View panelParentView = null;
231
232 synchronized (mLock) {
233 // Start watching for system property changes.
234 if (mSystemPropertyUpdater == null) {
235 mSystemPropertyUpdater = new Runnable() {
236 @Override public void More ...run() {
237 synchronized (mLock) {
238 for (int i = mRoots.size() - 1; i >= 0; --i) {
239 mRoots.get(i).loadSystemProperties();
240 }
241 }
242 }
243 };
244 SystemProperties.addChangeCallback(mSystemPropertyUpdater);
245 }
246
247 int index = findViewLocked(view, false);
248 if (index >= 0) {
249 if (mDyingViews.contains(view)) {
250 // Don't wait for MSG_DIE to make it's way through root's queue.
251 mRoots.get(index).doDie();
252 } else {
253 throw new IllegalStateException("View " + view
254 + " has already been added to the window manager.");
255 }
256 // The previous removeView() had not completed executing. Now it has.
257 }
258
259 // If this is a panel window, then find the window it is being
260 // attached to for future reference.
261 if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
262 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
263 final int count = mViews.size();
264 for (int i = 0; i < count; i++) {
265 if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
266 panelParentView = mViews.get(i);
267 }
268 }
269 }
270
271 root = new ViewRootImpl(view.getContext(), display);
272
273 view.setLayoutParams(wparams);
274
275 mViews.add(view);
276 mRoots.add(root);
277 mParams.add(wparams);
278 }
279
280 // do this last because it fires off messages to start doing things
281 try {
282 root.setView(view, wparams, panelParentView);
283 } catch (RuntimeException e) {
284 // BadTokenException or InvalidDisplayException, clean up.
285 synchronized (mLock) {
286 final int index = findViewLocked(view, false);
287 if (index >= 0) {
288 removeViewLocked(index, true);
289 }
290 }
291 throw e;
292 }
293 }
206-214行是對(duì)參數(shù)的正確性檢查。
然后217行有一個(gè)if
語句:如果parentWindow
不為null
則調(diào)用其adjustLayoutParamsForSubWindow
方法蒲拉。這里的parentWindow
實(shí)際上指向的是WindowManagerImpl
的成員變量mParentWindow
. 如果我們?nèi)匀患俣ó?dāng)前的Context
是一個(gè)Activity
, 那么parentWindow
就非空肃拜。因此Window.adjustLayoutParamsForSubWindow
被調(diào)用:
551 void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
552 CharSequence curTitle = wp.getTitle();
553 if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
554 wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
...
581 } else {
582 if (wp.token == null) {
583 wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
584 }
585 if ((curTitle == null || curTitle.length() == 0)
586 && mAppName != null) {
587 wp.setTitle(mAppName);
588 }
589 }
...
596 }
這里最關(guān)鍵的是第583行,將Window
里保存的Activity
的標(biāo)識(shí)mAppToken
賦給了傳進(jìn)來的WindowManager.LayoutParams
的token
. 由于這個(gè)WindowManager.LayoutParams
最后會(huì)傳給WindowManagerService
, 因此WindowManagerService
可以通過這個(gè)token
知道是哪個(gè)Activity
要添加視圖雌团。
回到WindowManagerGlobal.addView
, 248-257行會(huì)對(duì)要添加的view
進(jìn)行重復(fù)添加的檢查燃领。如果發(fā)現(xiàn)是重復(fù)添加則拋出異常。
如果當(dāng)前要添加的view
從屬于某個(gè)之前已經(jīng)添加的view
锦援,261-269行就會(huì)去找出那個(gè)已經(jīng)添加的view
.
接著在271行猛蔽,一個(gè)ViewRootImpl
對(duì)象被創(chuàng)建出來鸵贬。ViewRootImpl
在應(yīng)用進(jìn)程這邊管理視圖的過程中擔(dān)任了重要的角色小泉。像Activity
對(duì)應(yīng)的視圖的measure, layout, draw等過程都是從ViewRootImpl
開始。另外ViewRootImpl
還負(fù)責(zé)應(yīng)用進(jìn)程和WindowManagerService
進(jìn)程之間的通信臀防。
275-277行將view
, root
和wparams
分別加入各自對(duì)應(yīng)的ArrayList
. View
, ViewRootImpl
和WindowManager.LayoutParams
這三者是通過ArrayList
的下標(biāo)一一對(duì)應(yīng)的略板。
最后282行調(diào)用了ViewRootImpl.setView
, 在這個(gè)方法中ViewRootImpl
會(huì)去通知WindowManagerService
將新的視圖添加到屏幕上毁枯。
總結(jié)
這篇文章所講的內(nèi)容可以濃縮成下面這張圖。
一個(gè)Activity
會(huì)有一個(gè)成員變量指向一個(gè)WindowManager
的具體實(shí)現(xiàn)類WindowManagerImpl
對(duì)象叮称。該WindowManagerImpl
對(duì)象會(huì)保存該Activity
對(duì)應(yīng)的一個(gè)PhoneWindow
對(duì)象的引用种玛。一個(gè)PhoneWindow
又會(huì)引用一個(gè)DecorView
對(duì)象藐鹤。在一個(gè)應(yīng)用進(jìn)程中,會(huì)有一個(gè)WindowManagerGlobal
的單例管理應(yīng)用中所有Activity對(duì)應(yīng)的視圖赂韵。WindowManagerGlobal
中有三個(gè)關(guān)鍵的ArrayList
娱节,分別保存了View
, ViewRootImpl
和WindowManager.LayoutParams
.
上文所講的這些內(nèi)容都是發(fā)生在應(yīng)用進(jìn)程里的事情。在系統(tǒng)里真正負(fù)責(zé)視圖管理的是WindowManagerService
進(jìn)程右锨。其中括堤,ViewRootImpl
擔(dān)任了溝通應(yīng)用進(jìn)程和WindowManagerService
進(jìn)程的角色。