1、Activity , Window和View的关系是什么?
跟踪Activity的源码就会发现:
Activity.attch() -> PolicyManager -> Policy -> PhoneWindow -> mLayoutInflater.inflate()&mContentParent.addView()
这只是一个简单的跟踪过程描述。通过跟踪源代码,就可以很清晰的看出他们三者的关系。
Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图)
LayoutInflater像剪刀,Xml配置像窗花图纸。
1)一个Activity构造的时候会初始化一个Window,准确的说是PhoneWindow。
2)这个PhoneWindow有一个“ViewRoot”,引号是说其实这个“ViewRoot”是一个View或者说ViewGroup,是最初始的根视图。
3)“ViewRoot”通过addView方法来一个个的添加View。比如TextView,Button等
4)这些View的事件监听,是由WindowManagerService来接受消息,并且回调Activity函数。比如onClickListener,onKeyDown等
2、Activity是android的显示视图么?
不是
3、LayoutInflater是做什么的 ? layoutInflater.inflater()做什么的?
一般来讲,我们用LayoutInflater做一件事:inflate。inflate这个方法总共有四种形式,目的都是把xml表述的layout转化为View。
This class is used to instantiate layout XML file into its corresponding View objects . It is never be used directly -- use
getLayoutInflater() or getSystemService(String)getLayoutInflater() or getSystemService(String) to retrieve a standard
LayoutInflater instance that is already hooked up to the current context and correctly configured for the device you are
running on
——————————————————————————————————————————————————————————————————
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方法的实现:
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mContentParent.addView(view, params);
final Callback cb = getCallback();
if (cb != null) {
cb.onContentChanged();
}
}
Window内部首先判断mContentParent是否为空,然后调用installDecor方法(安装装饰器),我们看看这个方法如何实现的
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setIsRootNamespace(true);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
if (mTitleView != null) {
if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
View titleContainer = findViewById(com.android.internal.R.id.title_container);
if (titleContainer != null) {
titleContainer.setVisibility(View.GONE);
} else {
mTitleView.setVisibility(View.GONE);
}
if (mContentParent instanceof FrameLayout) {
((FrameLayout)mContentParent).setForeground(null);
}
} else {
mTitleView.setText(mTitle);
}
}
}
}
在该方法中,首先创建一个DecorView,DecorView是一个扩张FrameLayout的类,是所有窗口的根View。我们在Activity中调用的setConctentView就是放到DecorView中了。这是我们类图的聚合关系如下:
Activity--->Window--->DecorView
这是我们得出这3个类之间最直接的一个关系。
我们详细分析一下,类对象是如何被创建的。
先不考虑Activity的创建(因为 Acitivity的实例由ActivityManager维护,是在另一个进程设计到IPC的通信,后面会讲到),而考虑Window和View的创建。
Activity被创建后,系统会调用它的attach方法来将Activity添加到ActivityThread当中。我们找到Activity的attach方法如下:
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
Object lastNonConfigurationInstance,
HashMap<String,Object> lastNonConfigurationChildInstances,
Configuration config) {
attachBaseContext(context);
mWindow= PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
mUiThread = Thread.currentThread();
mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mIdent = ident;
mApplication = application;
mIntent = intent;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstance = lastNonConfigurationInstance;
mLastNonConfigurationChildInstances = lastNonConfigurationChildInstances;
mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}
我们看红色的代码部分,就是创建Window对象的代码。感兴趣的同学可以跟踪去看看具体是如何创建的。其实很简单,其内部实现调用了Policy对象的makeNewWindow方法,其方法直接new了一个PhoneWindow对象如下:
public PhoneWindow makeNewWindow(Context context) {
return new 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() {
return new 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中。
PhoneWindow,ViewRoot,Activity之间的大致关系
在android里,我们都知道activity.但是一个activity跟一个Window是一个什么关系呢?
在activity.java中,我们可以看到两个变量,分别是:
private Window mWindow;
private WindowManager mWindowManager;
这这个变量是在哪里赋值的呢?可以看到attach函数,
mWindow = PolicyManager.makeNewWindow(this);
跟进去看一下,可以发现返回的时候一个PhoneWindow对象,PhoneWindow是Window类的派生类。
mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
仔细跟踪下这段代码中我们可以得到WindowManager对象,并且这个对象是在系统唯一的,这个对象同样被赋值给PhoneWindow的成员变量。
我们在新建一个activity的时候,往往喜欢用如下的代码来定义该activity的UI界面,那么这个具体的实现是怎么来实现的呢?
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
我们跟踪setContentView就知道了。可以发现activity其实把这个操作交给了自己的mWindow来完成,通过跟踪PhoneWindow中的setContentView函数可以得出以下知识:
1,每一个PhoneWiondow中都有一个叫DecorView的对象,该对象是该PhoneWiondow的框架view,可以找到这么一段代码:
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(FILL_PARENT, FILL_PARENT));
该layoutResource则是资源ID,通过往上翻代码就可以找得到。
2,一个PhoneWiondow中都有一个叫FrameLayout的对象mContentParent,该对象是从上述layoutResource中的布局xml中获得的,这也将是activity中所有用户新增加view都会被包含在这个对象当中。
总体可以这样大致说明:一个activity包含有一个PhoneWiondow对象,而所有的UI部件都是放在PhoneWiondow中。
ViewRoot这个类在android的UI结构中扮演的是一个中间者的角色,连接的是PhoneWindow跟WindowManagerService.
WindowManagerService中我们知道它读取android系统里所有事件,键盘事件,轨迹球事件等等,它怎么分发到各个activity的呢?就是通过这个ViewRoot。
在每个PhoneWindow创建的时候,系统都会向WindowManger中的一个保存View数组增加PhoneWindow的DecorView对象,WindowManger在保存好这个View对象的同时,也会新创建一个ViewRoot对象用来沟通WindowManagerService。
可以查看WindowManagerImpl跟ViewRoot中的代码,同时ViewRoot中我们可以看到一个W类型,该类型派生自IWindow.stub可以知道这个可以被用来作远程调用。
res = sWindowSession.add(mWindow, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets);
IwindowSession则是WindowManagerService中的远程调用接口,上述代码也表明了每创建了一个PhoneWindow,也就创建了一个ViewRoot,并将在WindowManagerService注册。
接下来简单的看下KeyEvent的传递。在WindowManagerService中
focus.mClient.dispatchKey(event);
这个段代码的mClient则就是ViewRoot中的W类型,通过远程调用,可以看看ViewRoot的处理:
boolean handled = mView != null
? mView.dispatchKeyEventPreIme(event) : true;
mView则是PhoneWindow的DecorView对象,接下来怎么具体传递可以慢慢去跟了