轉自:http://hi.baidu.com/xiaofanqing/blog/item/9c4ef9116e5889dca6ef3f02.html
上篇講解了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顯示在屏幕當中的方法,實現如下:
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
這時我們立馬就明白了,Activity通過Context來獲取WindowManager對象,然后把Window對象的DocerView添加到了WindowManager 服務中,所以android的窗口的創建和顯示並不是在同一個進程中,而是把窗口的繪制和管理交給了專門的WindowManager服務,這也是android framework給我提供的基礎服務。
在上面綠色的代碼當中,我們看看mDeocr是在哪被創建的。
public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
// Update window manager if: we have a view, that view is
// attached to its parent (which will be a RootView), and
// this activity is not embedded.
if (mParent == null) {
View decor = mDecor;
if (decor != null && decor.getParent() != null) {
getWindowManager().updateViewLayout(decor, params);
}
}
}
搜索代碼發現其創建的地方如上面紅色代碼。
這時我們已經知道,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 方法,如下:
private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
{
if (Config.LOGV) Log.v("WindowManager", "addView view=" + view);
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException(
"Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams
= (WindowManager.LayoutParams)params;
ViewRoot root;
View panelParentView = null;
synchronized (this) {
// Here's an odd/questionable case: if someone tries to add a
// view multiple times, then we simply bump up a nesting count
// and they need to remove the view the corresponding number of
// times to have it actually removed from the window manager.
// This is useful specifically for the notification manager,
// which can continually add/remove the same view as a
// notification gets updated.
int index = findViewLocked(view, false);
if (index >= 0) {
if (!nest) {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
root = mRoots[index];
root.mAddNesting++;
// Update layout parameters.
view.setLayoutParams(wparams);
root.setLayoutParams(wparams, true);
return;
}
// 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 != null ? mViews.length : 0;
for (int i=0; i<count; i++) {
if (mRoots[i].mWindow.asBinder() == wparams.token) {
panelParentView = mViews[i];
}
}
}
root =newViewRoot(view.getContext());
root.mAddNesting = 1;
view.setLayoutParams(wparams);
if (mViews == null) {
index = 1;
mViews = new View[1];
mRoots = new ViewRoot[1];
mParams = new WindowManager.LayoutParams[1];
} else {
index = mViews.length + 1;
Object[] old = mViews;
mViews = new View[index];
System.arraycopy(old, 0, mViews, 0, index-1);
old = mRoots;
mRoots = new ViewRoot[index];
System.arraycopy(old, 0, mRoots, 0, index-1);
old = mParams;
mParams = new WindowManager.LayoutParams[index];
System.arraycopy(old, 0, mParams, 0, index-1);
}
index--;
mViews[index] = view;
mRoots[index] = root;
mParams[index] = wparams;
}
// do this last because it fires off messages to start doing things
root.setView(view, wparams, panelParentView);
}
我們看看我標記未紅色的兩行代碼 root =newViewRoot(view.getContext());和 root.setView(view, wparams, panelParentView);
創建了一個ViewRoot對象,然后把view添加到ViewRoot中。
ViewRoot對象是handler的一個實現,其聚合了ISessionWindow和IWindow對象來和WindowManager服務進行IPC通信。
今天就寫到這里了, 本來想畫類圖和交互圖的,哎呀只是mac電腦不給力,就沒畫了。
希望有朋友能多多交流,文筆不好,像記流水賬。看的同學 需結合源代碼來看^_^
原創文章,轉載注明謝謝。