我想大多数人,对于这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方法的实现:
@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中。
上篇讲解了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 =
new
ViewRoot(view.getContext());和
root.setView(view, wparams, panelParentView);
创建了一个ViewRoot对象,然后把view添加到ViewRoot中。
ViewRoot对象是handler的一个实现,其聚合了ISessionWindow和IWindow对象来和WindowManager服务进行IPC通信。
分享到:
相关推荐
然后通过startActivity(String id, Intent intent)这个方法获取Window获取当前Window对象,再然后调用getDecorView()方法获取当前activity对应的view,这样就可以使用addView(View child)方法添加到activity中。
在本文中,我们就将详细分析Android应用程序窗口对象的创建过程。为了方便接下来描述类型为PhoneWindow的应用程序窗口的创建过程,我们将这两个图拿过来,如以下的图1和图2所示:图1Activity和Win
Window表示一个窗口的概念,Window是...Android中,所有的视图都是通过Window来呈现,不管是Activity、Dialog、还是Toast,它们的视图实际上都是附加在Window上,因此Window是实际View的直接管理者,单击事件由Window传
Android 中 android.view.WindowLeaked的解决办法 按字面了解,Window Leaked大概就是说一个窗体泄漏了,也就是我们常说的内存泄漏,为什么窗体会泄漏呢? 产生原因: 我们知道Android的每一个Activity都有个...
Android 中的每个窗口 View 都有一个对应的 Window,例如 Activity、Dialog,在他们初始化的时候就会为其创建对应的PhoneWindow 并赋值到其内部的一个引用 window 的层级 WindowLayoutParams.setType 设置
Activity设置全屏和无标题栏,要用到andorid.view.Window和Android.view.WindowManager。 Window.FEATURE_NO_TITLE表示无标题栏。 WindowManager.LayoutParams.FLAG_FULLSCREEN表示全屏。 具体用法如下: 1、设置...
Android系统采用一种称为Surface的UI架构为应用程序提供用户...这个PPT讲Android应用程序的Surface机制,阐述Activity、Window和View的关系,以及应用程序、WindowManagerService和SurfaceFlinger协作完成UI渲染的过程
本书是一本Android进阶类书籍,采用理论、源码和实践相结合的方式来阐述高水准的Android应用开发要点。本书从三个方面来组织内容。第一,介绍Android开发者不容易掌握的一些知识点;第二,结合Android源代码和应用层...
4.2.2 Measure Spec和Layout Params的对应关系 / 178 4.3 View的工作流程 / 183 4.3.1 measure过程 / 183 4.3.2 layout过程 / 193 4.3.3 draw过程 / 197 4.4 自定义View / 199 4.4.1 自定义View的分类 ...
弹框在Android项目中经常出现,常见的实现方法有三种:Dialog 弹框,Window弹框,Activity伪弹框。本文就说一说三种弹框的实现及在项目中的运用。 二丶演示图 图一为常见的三种弹框(文末上链接),图二为项目中...
注意:PopupWindow组件的使用问题,PopupWindow是一个阻塞对话框,如果你直接在Activity创建的方法中显示它,则会报错:android.view.WindowManager$BadTokenException:Unable to add window -- token null is not ...
177 4.2.1 Measure Spec / 177 4.2.2 Measure Spec和Layout Params的对应关系 / 178 4.3 View的工作流程 / 183 4.3.1 measure过程 / 183 4.3.2 layout过程 / 193 4.3.3 draw过程 / 197 4.4 自定义View / 199 ...
的 Android SDK 提供了在 Android 平台上使用 JaVa 语言进行 Android 应用开发必须的工具和 API 接口。 特性 • 应用程序框架 支持组件的重用与替换 • Dalvik Dalvik Dalvik Dalvik 虚拟机 专为移动设备优化 • ...
在开发中我们经常需要把我们的应用设置为全屏,有两种方法... import android.view.Window; import android.view.WindowManager; public class OpenGl_Lesson1 extends Activity { public void onCreate(Bundle sa
而Activity接着会传递到 PhoneWindow上,PhoneWindow会传递给RootView,而RootView其实就是DecorView了,接下来便是从 DecorView到View上的分发过程了,具体就可以分成ViewGroup和View的分发两种情况了;
Android以Java为编程语言,使接口到功能,都有层出不穷的变化,其中Activity等同于J2ME的MIDlet,一个 Activity 类(class)负责创建视窗(window),一个活动中的Activity就是在 foreground(前景)模式,背景运行...
本文实例讲述了Android开发中获取View视图宽与高的常用方法。分享给大家供大家参考,具体如下: 一、根据WindowManager管理器获得 1)这两种方法在屏幕未显示的时候,还是处于0的状态,即要在setContentView调用之后...
import android.view.Window; import android.widget.ImageView; public class MyMultitouchDemo extends Activity { private static final int SCALEBASIC = 3 ;// 调整的比率 private int imageX = 0 ; // 计算...