Window和view的關係

一、WIndow和windowManager
Window是一個抽象類,它的具體實現是PhoneWindow,創建一個window很簡單,只需要創建一個windowManager即可,window具體實現在windowManagerService中,windowManager和windowManagerService的交互是一個IPC的過程。
下面是用windowManager的例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
mFloatingButton = new Button(this);
      mFloatingButton.setText( "window");
      mLayoutParams = new WindowManager.LayoutParams(
          LayoutParams. WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,
          PixelFormat. TRANSPARENT);
      mLayoutParams. flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
          | LayoutParams. FLAG_NOT_FOCUSABLE
          | LayoutParams. FLAG_SHOW_WHEN_LOCKED;
      mLayoutParams. type = LayoutParams. TYPE_SYSTEM_ERROR;
      mLayoutParams. gravity = Gravity. LEFT | Gravity. TOP;
      mLayoutParams. x = 100;
      mLayoutParams. y = 300;
      mFloatingButton.setOnTouchListener( this);
      mWindowManager.addView( mFloatingButton, mLayoutParams); 

flags和type兩個屬性很重要,下面對一些屬性進行介紹,首先是flags:
FLAG_NOT_TOUCH_MODAL表示不需要獲取焦點,也不需要接收各種輸入,最終事件直接傳遞給下層具有焦點的window。
FLAG_NOT_FOCUSABLE:在此window外的區域單擊事件傳遞到底層window中。當前的區域則自己處理,這個一般都要設置,很重要。
FLAG_SHOW_WHEN_LOCKED :開啓可以讓window顯示在鎖屏界面上。
再來看下type這個參數:
window有三種類型:應用window,子window,系統window。應用類對應一個Activity,子Window不能單獨存在,需要附屬在父Window上,比如常用的Dialog。系統Window是需要聲明權限再創建的window,如toast等。
window有z-ordered屬性,層級越大,越在頂層。應用window層級1-99,子window1000-1999,系統2000-2999。這此層級對應着windowManager的type參數。系統層級常用的有兩個TYPE_SYSTEM_OVERLAY或者TYPE_SYSTEM_ERROR。比如想用TYPE_SYSTEM_ERROR,只需
mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR。還要添加權限<uses-permission andorid:name="android.permission.SYSTEM_ALERT_WINDOW"/>。
有了對window的基本認識之後,我們來看下它底層如何實現加載View的。
二、window的創建
其實Window的創建跟之前我寫的一篇博客LayoutInflater源碼分析有點相似。Window的創建是在Activity創建的attach方法中,通過PolicyManager的makeNewWindow方法。Activity中實現了Window的Callback接口,因此當window狀態改變時就會回調Activity方法。如onAttachedToWindow等。PolicyManager的真正實現類是Policy,看下它的代碼:

?
1
2
3
public Window makeNewWindow(Context context) {
    return new PhoneWindow(context);
  }

到此Window創建完成。
下面分析view是如何附屬到window上的。看Activity的setContentView方法。

?
1
2
3
4
public void setContentView(int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
  }

兩部分,設置內容和設置ActionBar。window的具體實現是PhoneWindow,看它的setContent。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void setContentView(int layoutResID) {
    // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
    // decor, when theme attributes and the like are crystalized. Do not check the feature
    // before this happens.
    if (mContentParent == null) {
      installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
      mContentParent.removeAllViews();
    }
  
    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
      final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
          getContext());
      transitionTo(newScene);
    } else {
      mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
      cb.onContentChanged();
    }
  

看到了吧,又是分析它。
這裏分三步執行:
1.如果沒有DecorView,在installDecor中的generateDecor()創建DecorView。之前就分析過,這次就不再分析它了。
2.將View添加到decorview中的mContentParent中。
3.回調Activity的onContentChanged接口。
經過以上操作,DecorView創建了,但還沒有正式添加到Window中。在ActivityResumeActivity中首先會調用Activity的onResume,再調用Activity的makeVisible,makeVisible中真正添加view ,代碼如下:

?
1
2
3
4
5
6
7
8
void makeVisible() {
   if (!mWindowAdded) {
     ViewManager wm = getWindowManager();
     wm.addView(mDecor, getWindow().getAttributes());
     mWindowAdded = true;
   }
   mDecor.setVisibility(View.VISIBLE);
 }

通過上面的addView方法將View添加到Window。
三、Window操作View內部機制
1.window的添加

一個window對應一個view和一個viewRootImpl,window和view通過ViewRootImpl來建立聯繫,它並不存在,實體是view。只能通過 windowManager來操作它。
windowManager的實現類是windowManagerImpl。它並沒有直接實現三大操作,而是委託給WindowManagerGlobal。addView的實現分爲以下幾步:
1).檢查參數是否合法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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");
    }
  
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
    if (parentWindow != null) {
      parentWindow.adjustLayoutParamsForSubWindow(wparams);
    } else {
      // If there's no parent and we're running on L or above (or in the
      // system context), assume we want hardware acceleration.
      final Context context = view.getContext();
      if (context != null
          && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
        wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
      }
    }

2).創建ViewRootImpl並將View添加到列表中。

?
1
2
3
4
5
6
7
root = new ViewRootImpl(view.getContext(), display);
  
      view.setLayoutParams(wparams);
  
      mViews.add(view);
      mRoots.add(root);
      mParams.add(wparams);

3).通過ViewRootImpl來更新界面並完成window的添加過程 。
root.setView(view, wparams, panelParentView);  
上面的root就是ViewRootImpl,setView中通過requestLayout()來完成異步刷新,看下requestLayout:

?
1
2
3
4
5
6
7
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
      checkThread();
      mLayoutRequested = true;
      scheduleTraversals();
    }
  }

接下來通過WindowSession來完成window添加過程,WindowSession是一個Binder對象,真正的實現類是 Session,window的添加是一次IPC調用。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
try {
          mOrigWindowType = mWindowAttributes.type;
          mAttachInfo.mRecomputeGlobalAttributes = true;
          collectViewAttributes();
          res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
              getHostVisibility(), mDisplay.getDisplayId(),
              mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
        } catch (RemoteException e) {
          mAdded = false;
          mView = null;
          mAttachInfo.mRootView = null;
          mInputChannel = null;
          mFallbackEventHandler.setView(null);
          unscheduleTraversals();
          setAccessibilityFocus(null, null);
          throw new RuntimeException("Adding window failed", e);
}

 在Session內部會通過WindowManagerService來實現Window的添加。

?
1
2
3
4
5
6
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
     int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
     InputChannel outInputChannel) {
   return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
       outContentInsets, outStableInsets, outInputChannel);
 }

在WindowManagerService內部會爲每一個應用保留一個單獨的session。
2.window的刪除
看下WindowManagerGlobal的removeView:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
    }
  }

首先調用findViewLocked來查找刪除view的索引,這個過程就是建立數組遍歷。然後再調用removeViewLocked來做進一步的刪除。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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());
      }
    }
    boolean deferred = root.die(immediate);
    if (view != null) {
      view.assignParent(null);
      if (deferred) {
        mDyingViews.add(view);
      }
    }
  }

真正刪除操作是viewRootImpl來完成的。windowManager提供了兩種刪除接口,removeViewImmediate,removeView。它們分別表示異步刪除和同步刪除。具體的刪除操作由ViewRootImpl的die來完成。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
boolean die(boolean immediate) {
    // Make sure we do execute immediately if we are in the middle of a traversal or the damage
    // done by dispatchDetachedFromWindow will cause havoc on return.
    if (immediate && !mIsInTraversal) {
      doDie();
      return false;
    }
  
    if (!mIsDrawing) {
      destroyHardwareRenderer();
    } else {
      Log.e(TAG, "Attempting to destroy the window while drawing!\n" +
          " window=" + this + ", title=" + mWindowAttributes.getTitle());
    }
    mHandler.sendEmptyMessage(MSG_DIE);
    return true;
  }

由上可知如果是removeViewImmediate,立即調用doDie,如果是removeView,用handler發送消息,ViewRootImpl中的Handler會處理消息並調用doDie。重點看下doDie:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void doDie() {
    checkThread();
    if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
    synchronized (this) {
      if (mRemoved) {
        return;
      }
      mRemoved = true;
      if (mAdded) {
        dispatchDetachedFromWindow();
      }
  
      if (mAdded && !mFirst) {
        destroyHardwareRenderer();
  
        if (mView != null) {
          int viewVisibility = mView.getVisibility();
          boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
          if (mWindowAttributesChanged || viewVisibilityChanged) {
            // If layout params have been changed, first give them
            // to the window manager to make sure it has the correct
            // animation info.
            try {
              if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
                  & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                mWindowSession.finishDrawing(mWindow);
              }
            } catch (RemoteException e) {
            }
          }
  
          mSurface.release();
        }
      }
  
      mAdded = false;
    }
    WindowManagerGlobal.getInstance().doRemoveView(this);
  }

主要做四件事:
1.垃圾回收相關工作,比如清數據,回調等。
2.通過Session的remove方法刪除Window,最終調用WindowManagerService的removeWindow

3.調用dispathDetachedFromWindow,在內部會調用onDetachedFromWindow()和onDetachedFromWindowInternal()。當view移除時會調用onDetachedFromWindow,它用於作一些資源回收。
4.通過doRemoveView刷新數據,刪除相關數據,如在mRoot,mDyingViews中刪除對象等。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void doRemoveView(ViewRootImpl root) {
    synchronized (mLock) {
      final int index = mRoots.indexOf(root);
      if (index >= 0) {
        mRoots.remove(index);
        mParams.remove(index);
        final View view = mViews.remove(index);
        mDyingViews.remove(view);
      }
    }
    if (HardwareRenderer.sTrimForeground && HardwareRenderer.isAvailable()) {
      doTrimForeground();
    }
  }

3.更新window
看下WindowManagerGlobal中的updateViewLayout。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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) {
      int index = findViewLocked(view, true);
      ViewRootImpl root = mRoots.get(index);
      mParams.remove(index);
      mParams.add(index, wparams);
      root.setLayoutParams(wparams, false);
    }
  }

通過viewRootImpl的setLayoutParams更新viewRootImpl的layoutParams,接着scheduleTraversals對view重新佈局,包括測量,佈局,重繪,此外它還會通過WindowSession來更新window。這個過程由WindowManagerService實現。這跟上面類似,就不再重複,到此Window底層源碼就分析完啦。






Android 中Activity,Window和View之間的關係

標籤: androidnulljavamatrix活動object
 3582人閱讀 評論(1) 收藏 舉報
 分類:
 

本文是我從以下地址拷貝來的:  http://lpqsun-126-com.iteye.com/blog/1409989

保存下來,仔細閱讀。


我想大多數人,對於這3個東西的概念能區分,但是具體區別在哪卻很難說出來。

我這裏根據我個人的理解來講講我個人對這3個概念的理解。當然這裏設計到通用的事件窗口模型等通用GUI設計,我這裏就不打算講了,純粹從概念上來進行區分。

Activity是Android應用程序的載體,允許用戶在其上創建一個用戶界面,並提供用戶處理事件的API,如onKeyEvent, onTouchEvent等。 並維護應用程序的生命週期(由於android應用程序的運行環境和其他操作系統不同,android的應用程序是運行在框架之內,所以他的應用程序不能噹噹從進程的級別去考慮,而更多是從概念上去考慮。android應用程序是由多個活動堆積而成,而各個活動又有其獨立的生命週期)。Activity本身是個龐大的載體,可以理解成是應用程序的載體,如果木有Activity,android應用將無法運行。也可以理解成android應用程序的入口。Acivity的實例對象由系統維護。系統服務ActivityManager負責維護Activity的實例對象,並根據運行狀態維護其狀態信息。

但在用戶級別,程序員可能根願意理解成爲一個界面的載體。但僅僅是個載體,它本身並不負責任何繪製。Activity的內部實現,實際上是聚了一個Window對象。Window是一個抽象類,它的具體是在android_src_home/framework/policies/base/phone/com/android/internal/policy/impl目錄下的PhoneWindow.java。

當我們調用Acitivity的 setContentView方法的時候實際上是調用的Window對象的setContentView方法,所以我們可以看出Activity中關於界面的繪製實際上全是交給Window對象來做的。繪製類圖的話,可以看出Activity聚合了一個Window對象。

下面是PhoneWindow中的setContentView方法的實現:

Java代碼  收藏代碼
  1. @Override  
  2.   
  3.     public void setContentView(View view, ViewGroup.LayoutParams params) {  
  4.   
  5.         if (mContentParent == null) {  
  6.   
  7.             installDecor();  
  8.   
  9.         } else {  
  10.   
  11.             mContentParent.removeAllViews();  
  12.   
  13.         }  
  14.   
  15.         mContentParent.addView(view, params);  
  16.   
  17.         final Callback cb = getCallback();  
  18.   
  19.         if (cb != null) {  
  20.   
  21.             cb.onContentChanged();  
  22.   
  23.         }  
  24.   
  25.     }  

 Window內部首先判斷mContentParent是否爲空,然後調用installDecor方法(安裝裝飾器),我們看看這個方法如何實現的 

Java代碼  收藏代碼
  1. private void installDecor() {  
  2.   
  3.         if (mDecor == null) {  
  4.   
  5.             mDecor = generateDecor();  
  6.   
  7.             mDecor.setIsRootNamespace(true);  
  8.   
  9.         }  
  10.   
  11.         if (mContentParent == null) {  
  12.   
  13.             mContentParent = generateLayout(mDecor);  
  14.   
  15.             mTitleView = (TextView)findViewById(com.android.internal.R.id.title);  
  16.   
  17.             if (mTitleView != null) {  
  18.   
  19.                 if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {  
  20.   
  21.                     View titleContainer = findViewById(com.android.internal.R.id.title_container);  
  22.   
  23.                     if (titleContainer != null) {  
  24.   
  25.                         titleContainer.setVisibility(View.GONE);  
  26.   
  27.                     } else {  
  28.   
  29.                         mTitleView.setVisibility(View.GONE);  
  30.   
  31.                     }  
  32.   
  33.                     if (mContentParent instanceof FrameLayout) {  
  34.   
  35.                         ((FrameLayout)mContentParent).setForeground(null);  
  36.   
  37.                     }  
  38.   
  39.                 } else {  
  40.   
  41.                     mTitleView.setText(mTitle);  
  42.   
  43.                 }  
  44.   
  45.             }  
  46.   
  47.         }  
  48.   
  49.     }  
 

在該方法中,首先創建一個DecorView,DecorView是一個擴張FrameLayout的類,是所有窗口的根View。我們在Activity中調用的setConctentView就是放到DecorView中了。這是我們類圖的聚合關係如下:

Activity--->Window--->DecorView

這是我們得出這3個類之間最直接的一個關係。

我們詳細分析一下,類對象是如何被創建的。

先不考慮Activity的創建(因爲 Acitivity的實例由ActivityManager維護,是在另一個進程設計到IPC的通信,後面會講到),而考慮Window和View的創建。

Activity被創建後,系統會調用它的attach方法來將Activity添加到ActivityThread當中。我們找到Activity的attach方法如下:

Java代碼  收藏代碼
  1. final void attach(Context context, ActivityThread aThread,  
  2.   
  3.             Instrumentation instr, IBinder token, int ident,  
  4.   
  5.             Application application, Intent intent, ActivityInfo info,  
  6.   
  7.             CharSequence title, Activity parent, String id,  
  8.   
  9.             Object lastNonConfigurationInstance,  
  10.   
  11.             HashMap<String,Object> lastNonConfigurationChildInstances,  
  12.   
  13.             Configuration config) {  
  14.   
  15.         attachBaseContext(context);  
  16.   
  17.        mWindow= PolicyManager.makeNewWindow(this);  
  18.   
  19.         mWindow.setCallback(this);  
  20.   
  21.         if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {  
  22.   
  23.             mWindow.setSoftInputMode(info.softInputMode);  
  24.   
  25.         }  
  26.   
  27.         mUiThread = Thread.currentThread();  
  28.   
  29.         mMainThread = aThread;  
  30.   
  31.         mInstrumentation = instr;  
  32.   
  33.         mToken = token;  
  34.   
  35.         mIdent = ident;  
  36.   
  37.         mApplication = application;  
  38.   
  39.         mIntent = intent;  
  40.   
  41.         mComponent = intent.getComponent();  
  42.   
  43.         mActivityInfo = info;  
  44.   
  45.         mTitle = title;  
  46.   
  47.         mParent = parent;  
  48.   
  49.         mEmbeddedID = id;  
  50.   
  51.         mLastNonConfigurationInstance = lastNonConfigurationInstance;  
  52.   
  53.         mLastNonConfigurationChildInstances = lastNonConfigurationChildInstances;  
  54.   
  55.         mWindow.setWindowManager(null, mToken, mComponent.flattenToString());  
  56.   
  57.         if (mParent != null) {  
  58.   
  59.             mWindow.setContainer(mParent.getWindow());  
  60.   
  61.         }  
  62.   
  63.         mWindowManager = mWindow.getWindowManager();  
  64.   
  65.         mCurrentConfig = config;  
  66.   
  67.     }  
 

我們看紅色的代碼部分,就是創建Window對象的代碼。感興趣的同學可以跟蹤去看看具體是如何創建的。其實很簡單,其內部實現調用了Policy對象的makeNewWindow方法,其方法直接new了一個PhoneWindow對象如下:

public PhoneWindow makeNewWindow(Context context) {

        returnnew PhoneWindow(context);

 }

這時我們已經可以把流程串起來,Activity創建後系統會調用其attach方法,將其添加到ActivityThread當中,在attach方法中創建了一個window對象。

下面分析View的創建。我們知道Window聚合了DocerView,當用戶調用setContentView的時候會把一顆View樹仍給DocerView.View樹是已經創建好的實例對象了,所以我們研究的是DocerView是個什麼東西,它是如何被創建的。

我們回頭看看Window實現裏邊的setContentView方法,我們看上面代碼的紅色部分setContentView-> installDecor-> generateDecor.

 

generateDecor直接new了一個DecorView對象:  

protected DecorView generateDecor() {

        returnnew DecorView(getContext(), -1);

 }

我們可以去看看DecorView的實現,它是PhoneWindow的一個內部類。實現很簡單,它默認會包含一個灰色的標題欄,然後在標題欄下邊會包含一個空白區域用來當用戶調用setContentView的時候放置用戶View,並傳遞事件,這裏不做詳細分析,感興趣同學可以自己研究研究。

當DecorView創建好之後再回到Window中的setContentView方法中來,見上面代碼藍色部分,調用

  mContentParent.addView(view, params);

來將用戶的View樹添加到DecorView中。

到這時爲止,我想我們已經很清晰的認識到它們3者之間的關係,並知道其創建流程。

現在總結一下:

Activity在onCreate之前調用attach方法,在attach方法中會創建window對象。window對象創建時並木有創建Decor對象對象。用戶在Activity中調用setContentView,然後調用window的setContentView,這時會檢查DecorView是否存在,如果不存在則創建DecorView對象,然後把用戶自己的View 添加到DecorView中。

上篇講解了3個對象之間的關係和創建的時機。這篇講解窗口是如何被繪製出來的。

首先,我們看一個概念。就是View的draw方法的doc:

Manually render this view (and all of its children) to the given Canvas.

意思是說把View繪製在畫布上。個人覺得這個概念很重要,View和Canvas 的關係,按常規的思維,肯定認爲View聚合了Canvas對象,然後在View的onDraw 方法中,在View中繪製圖形。實際上恰恰相反,Canvas 是由系統提供,view通過draw方法來把自身繪製在畫布上。如果這樣來理解的話,很多東西理解起來就很自然了。系統在window中提供一個Canvas對象,DocerView通過調用draw方法來將自己繪製到canvas上。draw方法實際上是一個遞歸方法,他會循環調用孩子View的draw方法來完成整棵樹的繪製。所以實際上一個界面的繪製所用的Cavans是同一個對象。Canvas內部聚合了Matrix對象來實現座標系的變換。

這裏將的是題外話,只是想讓大家理解一個東西。

下面回到系統如何來繪製一個窗口。

android 系統提供了WindowManager,WindowManager顧名思義窗口管理器。實際上它只是對WindowManager服務做了一個包裝。其內部實現通過ISessionWindow和IWindow接口來和WindowManager服務來通信,這裏設計到IPC的概念。IPC即進程間的通訊,ANDROID通過IBinder接口來實現,IBinder通過transact方法來實現進程間的交互,這是一個使用很不友好的接口,好在android還提供了aidl的更人性化的接口,它使得IPC通信就像調用普通的JAVA方法那樣便捷。不瞭解aidl的同學可以自行研究研究(其實我自己也是半桶水,瞭解概念,而用的不熟悉)

我來看Window是如何被添加到WindowManager的.

Activity有一個public的方法setVisible用來控制Activity的窗口是否顯示。

我們看其內部實現發現其調用了makeVisible方法,該方法就是讓Window顯示在屏幕當中的方法,實現如下:

Java代碼  收藏代碼
  1. void makeVisible() {  
  2.   
  3.        if (!mWindowAdded) {  
  4.   
  5.            ViewManager wm = getWindowManager();  
  6.   
  7.            wm.addView(mDecor, getWindow().getAttributes());  
  8.   
  9.            mWindowAdded = true;  
  10.   
  11.        }  
  12.   
  13.        mDecor.setVisibility(View.VISIBLE);  
  14.   
  15.    }  
 

這時我們立馬就明白了,Activity通過Context來獲取WindowManager對象,然後把Window對象的DocerView添加到了WindowManager 服務中,所以android的窗口的創建和顯示並不是在同一個進程中,而是把窗口的繪製和管理交給了專門的WindowManager服務,這也是android framework給我提供的基礎服務。

在上面綠色的代碼當中,我們看看mDeocr是在哪被創建的。

Java代碼  收藏代碼
  1. public void onWindowAttributesChanged(WindowManager.LayoutParams params) {  
  2.   
  3.       // Update window manager if: we have a view, that view is  
  4.   
  5.        // attached to its parent (which will be a RootView), and  
  6.   
  7.        // this activity is not embedded.  
  8.   
  9.         if (mParent == null) {  
  10.   
  11.             View decor = mDecor;  
  12.   
  13.             if (decor != null && decor.getParent() != null) {  
  14.   
  15.                 getWindowManager().updateViewLayout(decor, params);  
  16.   
  17.             }  
  18.   
  19.         }  
  20.   
  21.     }  
 

搜索代碼發現其創建的地方如上面紅色代碼。

這時我們已經知道,Activity創建好Window之後只要調用WindowManager 的addView方法來將Window的DocerView添加進去即可是使Window顯示出來。還方法Window其實是Activity中的概念,在WindowManager中是直接和View打交道的。

下面我們開始研究WindowManager對象,打開其源代碼,發現它是一個接口,且只是簡單的擴展了ViewManager接口.並增加了一個方法

getDefaultDisplay():Display.  內部還有一個繼承自ViewGroup.LayoutParam的內部類。

我們在framework/base/core/java/android/view/WindowManagerImpl.java 中找到其實現類。

我們找到其核心的實現方法addView 方法,如下:

Java代碼  收藏代碼
  1. private void addView(View view, ViewGroup.LayoutParams params, boolean nest)  
  2.   
  3.     {  
  4.   
  5.         if (Config.LOGV) Log.v("WindowManager""addView view=" + view);  
  6.   
  7.         if (!(params instanceof WindowManager.LayoutParams)) {  
  8.   
  9.             throw new IllegalArgumentException(  
  10.   
  11.                     "Params must be WindowManager.LayoutParams");  
  12.   
  13.         }  
  14.   
  15.         final WindowManager.LayoutParams wparams  
  16.   
  17.                 = (WindowManager.LayoutParams)params;  
  18.   
  19.           
  20.   
  21.         ViewRoot root;  
  22.   
  23.         View panelParentView = null;  
  24.   
  25.           
  26.   
  27.         synchronized (this) {  
  28.   
  29.             // Here's an odd/questionable case: if someone tries to add a  
  30.   
  31.             // view multiple times, then we simply bump up a nesting count  
  32.   
  33.             // and they need to remove the view the corresponding number of  
  34.   
  35.             // times to have it actually removed from the window manager.  
  36.   
  37.             // This is useful specifically for the notification manager,  
  38.   
  39.             // which can continually add/remove the same view as a  
  40.   
  41.             // notification gets updated.  
  42.   
  43.             int index = findViewLocked(view, false);  
  44.   
  45.             if (index >= 0) {  
  46.   
  47.                 if (!nest) {  
  48.   
  49.                     throw new IllegalStateException("View " + view  
  50.   
  51.                             + " has already been added to the window manager.");  
  52.   
  53.                 }  
  54.   
  55.                 root = mRoots[index];  
  56.   
  57.                 root.mAddNesting++;  
  58.   
  59.                 // Update layout parameters.  
  60.   
  61.                 view.setLayoutParams(wparams);  
  62.   
  63.                 root.setLayoutParams(wparams, true);  
  64.   
  65.                 return;  
  66.   
  67.             }  
  68.   
  69.               
  70.   
  71.             // If this is a panel window, then find the window it is being  
  72.   
  73.             // attached to for future reference.  
  74.   
  75.             if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&  
  76.   
  77.                     wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {  
  78.   
  79.                 final int count = mViews != null ? mViews.length : 0;  
  80.   
  81.                 for (int i=0; i<count; i++) {  
  82.   
  83.                     if (mRoots[i].mWindow.asBinder() == wparams.token) {  
  84.   
  85.                         panelParentView = mViews[i];  
  86.   
  87.                     }  
  88.   
  89.                 }  
  90.   
  91.             }  
  92.   
  93.               
  94.   
  95.            root =newViewRoot(view.getContext());  
  96.   
  97.             root.mAddNesting = 1;  
  98.   
  99.             view.setLayoutParams(wparams);  
  100.   
  101.               
  102.   
  103.             if (mViews == null) {  
  104.   
  105.                 index = 1;  
  106.   
  107.                 mViews = new View[1];  
  108.   
  109.                 mRoots = new ViewRoot[1];  
  110.   
  111.                 mParams = new WindowManager.LayoutParams[1];  
  112.   
  113.             } else {  
  114.   
  115.                 index = mViews.length + 1;  
  116.   
  117.                 Object[] old = mViews;  
  118.   
  119.                 mViews = new View[index];  
  120.   
  121.                 System.arraycopy(old, 0, mViews, 0, index-1);  
  122.   
  123.                 old = mRoots;  
  124.   
  125.                 mRoots = new ViewRoot[index];  
  126.   
  127.                 System.arraycopy(old, 0, mRoots, 0, index-1);  
  128.   
  129.                 old = mParams;  
  130.   
  131.                 mParams = new WindowManager.LayoutParams[index];  
  132.   
  133.                 System.arraycopy(old, 0, mParams, 0, index-1);  
  134.   
  135.             }  
  136.   
  137.             index--;  
  138.   
  139.             mViews[index] = view;  
  140.   
  141.             mRoots[index] = root;  
  142.   
  143.             mParams[index] = wparams;  
  144.   
  145.         }  
  146.   
  147.         // do this last because it fires off messages to start doing things  
  148.   
  149.        root.setView(view, wparams, panelParentView);  
  150.   
  151.     }  
 

我們看看我標記未紅色的兩行代碼  root =newViewRoot(view.getContext());和 root.setView(view, wparams, panelParentView);

創建了一個ViewRoot對象,然後把view添加到ViewRoot中。

ViewRoot對象是handler的一個實現,其聚合了ISessionWindow和IWindow對象來和WindowManager服務進行IPC通信。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章