目光所及,皆有Window!Window,顧名思義,視窗,它是應用與使用者互動的一個視窗,我們所見到視圖,都對應着一個Window。比如螢幕上方的狀态欄、下方的導航欄、按音量鍵調出來音量控制欄、充電時的充電界面、螢幕中間的應用顯示區域(Activity)、Dialog、Toast、PopWindow、菜單等,都依附于對應的Window。可以認為Window是View的實際直接管理者,是以了解Window相關的知識,對了解Android的視圖機制有很大的幫助。本文将介紹Window相關的基礎知識,以及從源碼的角度分析WindowManager是如何将View呈現在界面的。
前言
轉載請聲明,轉自【https://www.cnblogs.com/andy-songwei/p/9965714.html】,多謝!
目光所及,皆有Window!Window,顧名思義,視窗,它是應用與使用者互動的一個視窗,我們所見到視圖,都對應着一個Window。比如螢幕上方的狀态欄、下方的導航欄、按音量鍵調出來音量控制欄、充電時的充電界面、螢幕中間的應用顯示區域(Activity)、Dialog、Toast、PopWindow、菜單等,都依附于對應的Window。可以認為Window是View的實際直接管理者,是以了解Window相關的知識,對了解Android的視圖機制有很大的幫助。
本文将介紹Window相關的基礎知識,以及基于Android8.0源碼分析WindowManager是如何将View呈現在界面的。本文主要包含如下内容:
一、一個懸浮按鈕的demo
本demo實作了在螢幕上顯示一個懸浮的Button,并可以跟随手指的移動而移動。代碼如下:
1 public void drawFloatButton() {
2 requestWindowPermission();
3 final WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
4 final Button button = new Button(this);
5 button.setText("button");
6 final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
7 WindowManager.LayoutParams.WRAP_CONTENT,
8 WindowManager.LayoutParams.WRAP_CONTENT,
9 0,
10 0,
11 PixelFormat.TRANSPARENT);
12 params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
13 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
14 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
15 params.gravity = Gravity.LEFT | Gravity.TOP;
16 params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
17 params.x = 500;
18 params.y = 500;
19 windowManager.addView(button, params);
20 button.setOnTouchListener(new View.OnTouchListener() {
21 @Override
22 public boolean onTouch(View v, MotionEvent event) {
23 int rawX = (int) event.getRawX();
24 int rawY = (int) event.getRawY();
25 switch (event.getAction()) {
26 case MotionEvent.ACTION_MOVE:
27 params.x = rawX;
28 params.y = rawY;
29 windowManager.updateViewLayout(button, params);
30 break;
31 default:
32 break;
33 }
34 return false;
35 }
36 });
37 }
如果是在Android6.0及以上,需要處理權限問題,在AndroidManifest.xml中聲明權限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
并在代碼中動态申請
1 private void requestWindowPermission() {
2 //android 6.0或者之後的版本需要發一個intent讓使用者授權
3 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
4 if (!Settings.canDrawOverlays(getApplicationContext())) {
5 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
6 Uri.parse("package:" + getPackageName()));
7 startActivityForResult(intent, 100);
8 }
9 }
10 }
由于該權限是一個敏感權限,是以啟動時系統還會彈出一個界面讓使用者手動開啟該權限:
示範效果如下所示:
該Button是個懸浮按鈕,它并不是通過在xml布局檔案中加入,然後通過Activity的setContentView方法将其顯示在界面中的,而是通過第19行的WindowManager.addView方法實作的。gif中可以看到,該Button可以顯示到Title區域,也可以在app退出後(不殺死程序)獨立顯示在桌面上。手指滑動過程中,通過第29行WindowManager.updateViewLayout更新Button的LayoutParams的坐标參數,不斷更新該Button的位置。後續我們會對這兩個方法做詳細分析。
二、Window相關特性
通過上述的demo,我們可以看到設定了LayoutParams的flags、type變量值,它們都是用來定義Window的特性的。
1、flag
flag變量用于設定window的屬性,控制其顯示特性,比如設定為不擷取焦點、不接受觸屏事件、顯示在鎖屏之上等。在WindowManager.LayoutParams類中定義了很多的flag常量,來豐富Window的功能及屬性。
2、type
type變量用于表示Window的類型。系統規定了Window有三種類型,按取值由小到大依次為:
(1)應用Window:對應着一個Activity,要建立應用視窗就必須在Activity中完成。層級範圍:1~99
(2)子Window:不能獨立存在,需要依附于特定的父Window。比如Dialog,PopWindow,菜單等。層級範圍:1000~1999
(3)系統Window:擁有系統權限才能建立的Window。比如Toast、系統狀态欄、導航欄、手機低電量提示、輸入法Window、搜尋條、來電顯示等。系統Window是獨立于應用程式的,理論上講應用程式沒有權限建立系統Window,隻有系統程序才有。層級範圍:2000~2999
type的值越大,在Window體系中顯示的層級就越高。為了了解這一點,我們了解一下視窗的Z-Order管理。
手機上采用的是層疊式的布局,它是一個三維空間,将手機水準方向作為X軸,豎直方向作為Y軸,垂直于螢幕由裡向外的方向為Z軸,所有視窗就按照type值的順序排列在Z軸上,type值越大Z值也就越大。如下圖所示,是以系統Window往往會在上層顯示:
三、WindowManager關系網
1、WindowManager在系統架構中的位置
這裡咱們先通過系統架構圖來直覺看看WindowManager在系統架構中的位置:
WindowManager在系統架構中的位置
上圖中紅色邊框的“Window Manager”和“Surface Manager”都和Window直接相關,“Surface Manager”不在本文的讨論範圍内,這裡隻關注“Window Manager”,從上圖可以看出,它位于Framework層。
2、WindowManager執行個體的擷取
在前面的demo中的第三行,通過如下的方式來擷取WindowManager執行個體:
WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
這種方式在擷取系統服務的時候非常常見,是以這裡順便看看其代碼實作:
1 //=============Activity.java============
2 private WindowManager mWindowManager;
3 private Window mWindow;
4 @Override
5 public Object getSystemService(@ServiceName @NonNull String name) {
6 if (getBaseContext() == null) {
7 throw new IllegalStateException(
8 "System services not available to Activities before onCreate()");
9 }
10 if (WINDOW_SERVICE.equals(name)) {
11 return mWindowManager;
12 } else if (SEARCH_SERVICE.equals(name)) {
13 ensureSearchManager();
14 return mSearchManager;
15 }
16 return super.getSystemService(name);
17 }
18
19 final void attach(...){
20 mWindowManager = mWindow.getWindowManager();
21 }
22
23 //==========Context.java==========
24 public static final String WINDOW_SERVICE = "window";
25
26 //===========Window.java==========
27 private WindowManager mWindowManager;
28
29 public WindowManager getWindowManager() {
30 return mWindowManager;
31 }
32
33 public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
34 boolean hardwareAccelerated) {
35 ......
36 mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
37 }
38
39 public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
40 return new WindowManagerImpl(mContext, parentWindow);
41 }
了解上面的邏輯,需要先知道一個繼承關系:Activity extends ContextThemeWrapper extends ContextWrapper extends Context。通過上述代碼可以明确了,這裡獲得的WindowManager的執行個體,實際上是WindowManagerImpl執行個體。這種面向接口程式設計(在接口中定義方法,在實作類中實作)的方式很普遍,這裡就不贅述了。
3、WindowManager與ViewManager、WindowManager、WindowManagerImpl、WindowManagerGlobal之間的關系
在分析源碼前一定要捋清楚ViewManager、WindowManager、WindowManagerImpl、WindowManagerGlobal之間的關系。下面先把關鍵代碼擺出來:
1 public interface ViewManager
2 {
3 public void addView(View view, ViewGroup.LayoutParams params);
4 public void updateViewLayout(View view, ViewGroup.LayoutParams params);
5 public void removeView(View view);
6 }
7
8 public interface WindowManager extends ViewManager{
9 ......
10 }
11
12 public final class WindowManagerImpl implements WindowManager {
13 private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
14 @override
15 public void addView(view,params){
16 mGlobal.addView(...);
17 }
18 public void updateViewLayout(view,params){
19 mGlobal.updateViewLayout(...);
20 }
21 public void remove(view){
22 mGlobal.remove(...);
23 }
24 }
25
26 public final class WindowManagerGlobal {
27 public void addView(...){
28 ......
29 }
30 public void updateViewLayout(...) {
31 ......
32 }
33 public void removeView(...) {
34 ......
35 }
36 }
看到上述這段代碼,相信你一定已經清楚了它們之間的關系了。ViewManager是個接口,其中定義了三個方法,在Window體系中添加、更新、移除View的過程看起來比較複雜,但其實都是圍繞着這三個方法展開的。WindowManager是個接口,繼承了ViewManager,擴充了功能。WindowManagerImpl是個實作類,其中包含了WindowManagerGlobal執行個體。WindowManagerGlobal則真正完成了addView、updateViewLayout、removeView過程。是以,在demo中我們在使用WindowManager的執行個體調用addView,updateViewLayout方法時,實際上都是WindowManagerGlobal來完成的。這種方式稱為橋接模式,這在系統源碼種很常見,Context機制中也是這種方式,橋接模式這裡就不展開講了。
第13行中通過WindowManagerGlobal.getInstance()來擷取的執行個體,這裡看看其實作:
1 //=========WindowManagerGlobal.java========
2 private static WindowManagerGlobal sDefaultWindowManager;
3 public static WindowManagerGlobal getInstance() {
4 synchronized (WindowManagerGlobal.class) {
5 if (sDefaultWindowManager == null) {
6 sDefaultWindowManager = new WindowManagerGlobal();
7 }
8 return sDefaultWindowManager;
9 }
10 }
可見,它是通過單例模式的方式對外提供的執行個體。如果對單例模式比較了解的話,就能看出這種實作方式是有問題的(對比DCL方式),但不明白系統源碼為什麼要這樣實作,可能是系統中調用該執行個體方法的場景比較簡單吧,咱們自己在設計單例模式的時候可不能這樣做。
在WindowMangerGlobal.java中維護着四個非常重要的list,這四個list在addView、updateViewLayout、removeView的過程中都會頻頻出現,理清楚這四個list和這三個方法,了解WindowManager工作機制時會清晰很多,它們在下面的源碼中會詳細講到。
1 //=========WindowManagerGlobal.java=========
2 //所有Window對應的View
3 private final ArrayList<View> mViews = new ArrayList<View>();
4 //所有Window對應的ViewRootImpl
5 private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
6 //所有Window對應的LayoutParams
7 private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>;
8 //正在被删除的View,或者已經執行了removeView方法但還沒有完成删除操作的View
9 private final ArraySet<View> mDyingViews = new ArraySet<View>;
四、addView機制
1 //=========WindowManagerGlobal=========
2 public void addView(View view, ViewGroup.LayoutParams params,
3 Display display, Window parentWindow) {
4 ......
5 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
6 ......
7 ViewRootImpl root;
8 View panelParentView = null;
9 synchronized (mLock) {
10 ......
11 //view不能重複添加,如果要添加需要先removeView,否則抛異常
12 int index = findViewLocked(view, false);
13 if (index >= 0) {
14 if (mDyingViews.contains(view)) {
15 // Don't wait for MSG_DIE to make it's way through root's queue.
16 mRoots.get(index).doDie();
17 } else {
18 throw new IllegalStateException("View " + view
19 + " has already been added to the window manager.");
20 }
21 // The previous removeView() had not completed executing. Now it has.
22 }
23 //子window
24 // If this is a panel window, then find the window it is being
25 // attached to for future reference.
26 if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
27 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
28 final int count = mViews.size();
29 for (int i = 0; i < count; i++) {
30 if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
31 panelParentView = mViews.get(i);
32 }
33 }
34 }
35 root = new ViewRootImpl(view.getContext(), display);
36 view.setLayoutParams(wparams);
37 mViews.add(view);
38 mRoots.add(root);8
39 mParams.add(wparams);
40 // do this last because it fires off messages to start doing things
41 try {
42 root.setView(view, wparams, panelParentView);
43 } catch (RuntimeException e) {
44 // BadTokenException or InvalidDisplayException, clean up.
45 if (index >= 0) {
46 removeViewLocked(index, true);
47 }
48 throw e;
49 }
50 }
51 }
上述代碼中除了關鍵方法外,注意留意mViews、mRoots、mParams集合的操作。
1 //=========ViewRootImpl.java==========
2 /**
3 * We have one child
4 */
5 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
6 synchronized (this) {
7 if (mView == null) {
8 mView = view;
9 ......
10 mAdded = true;
11 int res; /* = WindowManagerImpl.ADD_OKAY; */
12 // Schedule the first layout -before- adding to the window
13 // manager, to make sure we do the relayout before receiving
14 // any other events from the system.
15 requestLayout();
16 ......
17 try {
18 ......
19 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
20 getHostVisibility(), mDisplay.getDisplayId(),
21 mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
22 mAttachInfo.mOutsets, mInputChannel);
23 } catch (RemoteException e) {
24 mAdded = false;
25 mView = null;
26 ......
27 throw new RuntimeException("Adding window failed", e);
28 } finally {
29 ......
30 }
31 ......
32 }
33 }
34 }
第15行requestLayout()方法會執行View的繪制流程,在我之前的博文【朝花夕拾】Android自定義View篇之(一)View繪制流程中第三節有相關介紹。這裡簡單截取了一段UML圖,讀者明白其作用就可以了,想深入研究的可以去這篇文章看看。
第19行的mWindowSession.addToDisplay(...)方法,addView的實際邏輯處理就在這裡面。我們繼續分析mWindowSession和addToDisplay(...)的邏輯:
1 //========ViewRootImpl.java=========
2 public final class ViewRootImpl{
3 final IWindowSession mWindowSession;
4 public ViewRootImpl(...){
5 ......
6 mWindowSession = WindowManagerGlobal.getWindowSession();
7 ......
8 }
9 }
10
11 //=======WindowManagerGlobal.java==========
12 public static IWindowSession getWindowSession() {
13 synchronized (WindowManagerGlobal.class) {
14 if (sWindowSession == null) {
15 try {
16 ......
17 IWindowManager windowManager = getWindowManagerService();
18 sWindowSession = windowManager.openSession(
19 new IWindowSessionCallback.Stub() {
20 @Override
21 public void onAnimatorScaleChanged(float scale) {
22 ValueAnimator.setDurationScale(scale);
23 }
24 },
25 ......
26 } catch (RemoteException e) {
27 throw e.rethrowFromSystemServer();
28 }
29 }
30 return sWindowSession;
31 }
32 }
33
34 public static IWindowManager getWindowManagerService() {
35 synchronized (WindowManagerGlobal.class) {
36 if (sWindowManagerService == null) {
37 sWindowManagerService = IWindowManager.Stub.asInterface(
38 ServiceManager.getService("window"));
39 ......
40 }
41 return sWindowManagerService;
42 }
43 }
44
45 //============WindowManagerService.java=======
46 @Override
47 public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
48 IInputContext inputContext) {
49 if (client == null) throw new IllegalArgumentException("null client");
50 if (inputContext == null) throw new IllegalArgumentException("null inputContext");
51 Session session = new Session(this, callback, client, inputContext);
52 return session;
53 }
54
55 public int addWindow(Session session, IWindow client, int seq,
56 WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
57 Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
58 InputChannel outInputChannel) {
59 //在wms中真正實作
60 ......
61 }
62
63 //===========Session.java=========
64 final WindowManagerService mService;
65 public Session(WindowManagerService service, ......) {
66 mService = service;
67 ......
68 }
69
70 @Override
71 public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
72 int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
73 Rect outOutsets, InputChannel outInputChannel) {
74 return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
75 outContentInsets, outStableInsets, outOutsets, outInputChannel);
76 }
這裡面的代碼邏輯很容易了解,最後是把addView的工作交給了WMS的addWindow方法,是以真正添加view的邏輯是在WMS中完成的。這裡面邏輯比較複雜繁瑣,就不繼續深入了,當目前為止就已經清楚整個流程了。
五、updateViewLayout更新機制
1 //============WindowManagerGlobal.java==========
2 public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
3 ......
4 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
5 view.setLayoutParams(wparams);
6 synchronized (mLock) {
7 int index = findViewLocked(view, true);
8 ViewRootImpl root = mRoots.get(index);
9 mParams.remove(index);
10 mParams.add(index, wparams);
11 root.setLayoutParams(wparams, false);
12 }
13 }
這裡面對mParams進行了操作,将舊有的LayoutParams進行了替換。第11行執行了更新邏輯:
1 //=============ViewRootImpl.java==========
2 void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
3 ...... //對新的LayoutParams參數做一些操作
4 scheduleTraversals(); //調用繪制流程
5 }
從上述過程可以發現更新過程相對比較簡單,更新view的過程簡單來說就是,将新的LayoutParams替換掉舊的,并啟用繪制流程。
六、removeView移除機制
1 //============WindowManagerImpl.java==========
2 @Override
3 public void removeView(View view) {
4 mGlobal.removeView(view, false);
5 }
6 @Override
7 public void removeViewImmediate(View view) {
8 mGlobal.removeView(view, true);
9 }
WindowManagerImpl類中提供了兩個方法用于移除view,從方法名稱可以推測其差别在于Immediate,也就是是否立即移除的意思。
1 //==========WindowManagerGlobal.java=========
2 public void removeView(View view, boolean immediate) {
3 ......
4 synchronized (mLock) {
5 int index = findViewLocked(view, true);
6 View curView = mRoots.get(index).getView();
7 removeViewLocked(index, immediate);
8 ......
9 }
10 }
11
12 private void removeViewLocked(int index, boolean immediate) {
13 ViewRootImpl root = mRoots.get(index);
14 View view = root.getView();
15 ......
16 boolean deferred = root.die(immediate);
17 if (view != null) {
18 view.assignParent(null);
19 if (deferred) {
20 mDyingViews.add(view);
21 }
22 }
23 }
24
25 //========ViewRootImpl.java=========
26 private final static int MSG_DIE = 3;
27 final ViewRootHandler mHandler = new ViewRootHandler();
28 /**
29 * @param immediate True, do now if not in traversal. False, put on queue and do later.
30 * @return True, request has been queued. False, request has been completed.
31 */
32 boolean die(boolean immediate) {
33 // Make sure we do execute immediately if we are in the middle of a traversal or the damage
34 // done by dispatchDetachedFromWindow will cause havoc on return.
35 if (immediate && !mIsInTraversal) {
36 doDie();
37 return false;
38 }
39 ......
40 mHandler.sendEmptyMessage(MSG_DIE);
41 return true;
42 }
43
44 final class ViewRootHandler extends Handler {
45 ......
46 @Override
47 public void handleMessage(Message msg) {
48 switch (msg.what) {
49 case MSG_DIE:
50 doDie();
51 break;
52 ......
53 }
54 }
55 }
如上代碼驗證了之前的猜想,removeView(View)和removeViewImmediate(View)的差別确實就在于是否立即移除。如果調用removeView,會通過handler來調用doDie(),而我們知道handler對應了一個MessageQueue的,需要排隊等待執行的,這樣就實作了延後執行。而如果調用removeViewImmediate,如果目前沒有執行view的周遊,那就直接調用doDie()了。
1 //==========ViewRootImpl.java========
2 void doDie() {
3 ......
4 synchronized (this) {
5 if (mRemoved) {
6 return;
7 }
8 mRemoved = true;
9 if (mAdded) {
10 dispatchDetachedFromWindow();
11 }
12 ......
13 mAdded = false;
14 }
15 WindowManagerGlobal.getInstance().doRemoveView(this);
16 }
17 //移除的主要邏輯都在該方法内完成
18 void dispatchDetachedFromWindow() {
19 mView.dispatchDetachedFromWindow();
20 ......
21 try {
22 mWindowSession.remove(mWindow);
23 } catch (RemoteException e) {
24 }
25 ......
26 unscheduleTraversals();//停止繪制View
27 }
28
29 //======View.java======
30 void dispatchDetachedFromWindow() {
31 ......
32 onDetachedFromWindow();
33 ......
34 }
35
36 @CallSuper
37 protected void onDetachedFromWindow() {
38 //在view從window移除後會回調該方法,可以在其中做一些資源回收的操作,如終止動畫、停止線程等。
39 }
40
41 //========WindowManagerGlobal.java=======
42 //該方法主要用于重新整理資料
43 void doRemoveView(ViewRootImpl root) {
44 synchronized (mLock) {
45 final int index = mRoots.indexOf(root);
46 if (index >= 0) {
47 mRoots.remove(index);
48 mParams.remove(index);
49 final View view = mViews.remove(index);
50 mDyingViews.remove(view);
51 }
52 }
53 ......
54 }
55
56 //==========Session.java=======
57 @Override
58 public void remove(IWindow window) {
59 mService.removeWindow(this, window);
60 }
61
62 //==========WindowManagerService.java=======
63 void removeWindow(Session session, IWindow client) {
64 synchronized(mWindowMap) {
65 WindowState win = windowForClientLocked(session, client, false);
66 if (win == null) {
67 return;
68 }
69 win.removeIfPossible();
70 }
71 }
上述doDie()過程比較容易了解,第22行,參考addView中的邏輯分析可知,這裡也是IPC方式,流程最終進入到了WMS中的removeWindow方法,同樣到這裡咱們不繼續往下深入了。上述流程中,mRoots、mParams、mViews、mDyingViews四個集合也做了相應的操作。
上面講到的三個主要方法中,可以看到它們都對mRoots、mParams、mViews、mDyingViews進行了重新整理。
七、WMS簡介
在前面的addView和removeView機制中,我們會發現具體的處理邏輯都交給了WMS(即WindowManagerService的簡寫)中,那WMS是什麼呢?
WMS是一個非常重要的系統服務。它支撐着視圖相關的各項業務,這非常符合軟體設計的單一職責原則,其業務和ActivityManagerService(簡稱AMS)一起幾乎占據了framework業務的半壁江山,可見其重要性。關于WMS的内容實在太多了,這裡隻簡單介紹其大緻功能以及啟動流程。
1、WMS功能介紹
WMS的大概功能如下圖所示:
這裡先簡單描述一下各項功能:
視窗管理:WMS是視窗管理者,結合WindowManager實作視窗的啟動、添加、删除,以及管理視窗的大小、層級等。
視窗動畫:在視窗切換時,使用視窗動畫可以使這個過程看起來更炫更生動,這個視窗動畫就是由WMS的動畫子系統來負責的,動畫子系統的管理者便是WindowAnimator。
輸入系統的中轉站: 觸摸裝置螢幕上的視窗時會産生觸摸事件,InputManagerService(IMS)會對觸摸事件進行處理,找到最合适的視窗來回報事件。而WMS是這些視窗的管理者,那自然而然就成為了輸入系統的中轉站了。
Surface管理:視窗并不具備繪制功能,是以每個視窗都需要一個Surface來供自己繪制,WMS就是這個Surface的管理者。
該部分參考:Android解析WindowManagerService(一)WMS的誕生
2、WMS的啟動流程
在我之前的文章:【系統之音】Android系統啟動篇 中有介紹過系統的啟動過程,其中提到過WMS的啟動時機,這裡從源碼入手再稍微詳細闡述一下:
1 //===========SystemServer.java==========
2 /**
3 * The main entry point from zygote.
4 */
5 public static void main(String[] args) {
6 new SystemServer().run();
7 }
8
9 private void run() {
10 ......
11 startBootstrapServices();
12 startCoreServices();
13 startOtherServices();
14 ......
15 }
16
17 private void startOtherServices() {
18 ......
19 WindowManagerService wm = null;
20 ......
21 wm = WindowManagerService.main(context, inputManager,
22 mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
23 !mFirstBoot, mOnlyCore, new PhoneWindowManager());
24 ServiceManager.addService(Context.WINDOW_SERVICE, wm);
25 ......
26 }
在第21行中得到了WMS的執行個體:
1 //=========WindowManagerService.java=======
2 private static WindowManagerService sInstance;
3 ......
4 public static WindowManagerService main(final Context context, final InputManagerService im,
5 final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
6 WindowManagerPolicy policy) {
7 DisplayThread.getHandler().runWithScissors(() ->
8 sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
9 onlyCore, policy), 0);
10 return sInstance;
11 }
第7行調用了Handler的runWithScissors方法,該方法的作用是執行一個同步的task,即執行完第8行後才會執行第10行,是以main方法傳回的是一個WMS執行個體。
第24行就是一個将WMS加入到系統服務的過程:
1 //============ServiceManager.java=========
2 /**
3 * Place a new @a service called @a name into the service
4 * manager.
5 *
6 * @param name the name of the new service
7 * @param service the service object
8 */
9 public static void addService(String name, IBinder service) {
10 try {
11 getIServiceManager().addService(name, service, false);
12 } catch (RemoteException e) {
13 Log.e(TAG, "error in addService", e);
14 }
15 }
16
17 private static IServiceManager sServiceManager;
18 private static IServiceManager getIServiceManager() {
19 if (sServiceManager != null) {
20 return sServiceManager;
21 }
22 // Find the service manager
23 sServiceManager = ServiceManagerNative
24 .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
25 return sServiceManager;
26 }
這裡調用的addService就是一個IPC過程,如果有閱讀過AIDL實作Binder時編譯生成的代碼,看到ServiceManagerNative類後就會非常眼熟,它就是實作Binder的中間類。至于提供addService功能實作的Server是誰,筆者目前還未找到,是以先研究到這裡,後續研究透徹了再補上。
到這裡我們就清楚了,在SystemServer程序啟動過程中,WMS啟動,并通過ServiceManager中的addServie方法添加到系統中,供其它程序調用。
結語
關于Window、WindowManager、WMS等的内容非常多,這裡隻寫了一些筆者在學習過程中做的一些研究。 由于筆者水準有限,如果有些地方描述不妥或者不準确的地方,請讀者不吝賜教。
本文參考資料有:
【朝花夕拾】Android自定義View篇之(一)View繪制流程
Android解析WindowManagerService(一)WMS的誕生
【系統之音】Android系統啟動篇
【系統之音】Android系統啟動篇
《Android開發藝術探索》