您的位置:首页 > 移动开发 > Android开发

Android 中Activity,Window和View之间的关系(二)

2012-05-17 09:29 489 查看
转自: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电脑不给力,就没画了。

希望有朋友能多多交流,文笔不好,像记流水账。看的同学 需结合源代码来看^_^

原创文章,转载注明谢谢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: