Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析
在前文中,我们分析了Android应用程序窗口的运行上下文环境的创建过程。由此可知,每一个Activity组件都有一个关联的ContextImpl对象,同时,它还关联有一个Window对象,用来描述一个具体的应用程序窗口。由此又可知,Activity只不过是一个高度抽象的UI组件,它的具体UI实现其实是由其它的一系列对象来实现的。在本文中,我们就将详细分析Android应用程序窗口对象的创建过程。
从前面Android应用程序窗口(Activity)实现框架简要介绍和学习计划一文可以知道,在PHONE平台上,与Activity组件所关联的窗口对象的实际类型为PhoneWindow,后者是从Window类继承下来的。Activity、Window和PhoneWindow三个类的关系可以参考Android应用程序窗口(Activity)实现框架简要介绍和学习计划一文中的图3和图5。为了方便接下来描述类型为PhoneWindow的应用程序窗口的创建过程,我们将这两个图拿过来,如以下的图1和图2所示:
图1 Activity和Window的类关系图
图2 Window和PhoneWindow的类关系图
上述两个图中所涉及到的类的描述可以参考Android应用程序窗口(Activity)实现框架简要介绍和学习计划一文,本文主要从Android应用程序窗口的创建过程来理解Activity、Window和PhoneWindow三个类的关系。
从Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析一文又可以知道,与Activity组件所关联的一个PhoneWindow对象是从Activity类的成员函数attach中创建的,如图3所示:
图3 Android应用程序窗口的创建过程
这个过程可以分为9个步骤,接下来我们就详细分析每一个步骤。
Step 1. Activity.attach
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
|
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks {
......
private Window mWindow;
......
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) {
......
mWindow = PolicyManager.makeNewWindow( this );
mWindow.setCallback( this );
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
......
mWindow.setWindowManager( null , mToken, mComponent.flattenToString());
......
}
......
}
|
这个函数定义在文件frameworks/base/core/java/android/app/Activity.java中。
在前面Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析一文中,我们已经分析过这个函数的实现了,这里我们只关注与应用程序窗口创建相关的代码。
函数首先调用PolicyManager类的静态成员函数makeNewWindow来创建一个类型为PhoneWindow的应用程序窗口,并且保存在Activity类的成员变量mWindow中。有了这个类型为PhoneWindow的应用程序窗口,函数接下来还会调用它的成员函数setCallback、setSoftInputMode和setWindowManager来设置窗口回调接口、软键盘输入区域的显示模式和本地窗口管理器。
PhoneWindow类的成员函数setCallback、setSoftInputMode和setWindowManager都是从父类Window继承下来的,因此,接下来我们就继续分析PolicyManager类的静态成员函数makeNewWindow,以及Window类的成员函数setCallback、setSoftInputMode和setWindowManager的实现。
Step 2. PolicyManager.makeNewWindow
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
|
public final class PolicyManager {
private static final String POLICY_IMPL_CLASS_NAME =
"com.android.internal.policy.impl.Policy" ;
private static final IPolicy sPolicy;
static {
// Pull in the actual implementation of the policy at run-time
try {
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
sPolicy = (IPolicy)policyClass.newInstance();
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be loaded" , ex);
} catch (InstantiationException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be instantiated" , ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be instantiated" , ex);
}
}
......
// The static methods to spawn new policy-specific objects
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
......
}
|
这个函数定义在文件frameworks/base/core/java/com/android/internal/policy/PolicyManager.java中。
PolicyManager是一个窗口管理策略类,它在第一次被使用的时候,就会创建一个Policy类实例,并且保存在静态成员变量sPolicy中,以后PolicyManager类的窗口管理策略就是通过这个Policy类实例来实现的,例如,PolicyManager类的静态成员函数makeNewWindow就是通过调用这个Policy类实例的成员函数makeNewWindow来创建一个具体的应用程序窗口的。
接下来,我们就继续分析Policy类的成员函数makeNewWindow的实现。
Step 3. Policy.makeNewWindow
1
2
3
4
5
6
7
8
|
public class Policy implements IPolicy {
......
public PhoneWindow makeNewWindow(Context context) {
return new PhoneWindow(context);
}
......
}
|
这个函数定义在文件frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java中。
Policy类的成员函数makeNewWindow的实现很简单,它只是创建了一个PhoneWindow对象,然后返回给调用者。
接下来,我们就继续分析PhoneWindow类的构造函数的实现,以便可以了解一个类型为PhoneWindow的应用程序窗口的创建过程。
Step 4. new PhoneWindow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public class PhoneWindow extends Window implements MenuBuilder.Callback {
......
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
private ViewGroup mContentParent;
......
private LayoutInflater mLayoutInflater;
......
public PhoneWindow(Context context) {
super (context);
mLayoutInflater = LayoutInflater.from(context);
}
......
}
|
这个函数定义在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。
PhoneWindow类的构造函数很简单,它首先调用父类Window的构造函数来执行一些初始化操作,接着再调用LayoutInflater的静态成员函数from创建一个LayoutInflater实例,并且保存在成员变量mLayoutInflater中。这样,PhoneWindow类以后就可以通过成员变量mLayoutInflater来创建应用程序窗口的视图,这个视图使用类型为DecorView的成员变量mDecor来描述。PhoneWindow类还有另外一个类型为ViewGroup的成员变量mContentParent,用来描述一个视图容器,这个容器存放的就是成员变量mDecor所描述的视图的内容,不过这个容器也有可能指向的是mDecor本身。在后面的文章中,我们再详细分析类型为PhoneWindow的应用程序窗口的视图的创建过程。
Window的构造函数定义在文件frameworks/base/core/java/android/view/Window.java中,它的实现很简单,只是初始化了其成员变量mContext,如下所示:
1
2
3
4
5
6
7
8
9
10
|
public abstract class Window {
......
private final Context mContext;
......
public Window(Context context) {
mContext = context;
}
......
}
|
从前面的调用过程可以知道,参数context描述的是正在启动的Activity组件,将它保存在Window类的成员变量mContext之后,Window类就可以通过它来访问与Activity组件相关的资源了。
这一步执行完成之后,回到前面的Step 1中,即Activity类的成员函数attach中,接下来就会继续调用前面所创建的PhoneWindow对象从父类Window继承下来的成员函数setCallback来设置窗口回调接口,因此,接下来我们就继续分析Window类的成员函数setCallback的实现。
Step 5. Window.setCallback
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public abstract class Window {
......
private Callback mCallback;
......
/**
* Set the Callback interface for this window, used to intercept key
* events and other dynamic operations in the window.
*
* @param callback The desired Callback interface.
*/
public void setCallback(Callback callback) {
mCallback = callback;
}
......
}
|
这个函数定义在文件frameworks/base/core/java/android/view/Window.java中。
正在启动的Activity组件会将它所实现的一个Callback接口设置到与它所关联的一个PhoneWindow对象的父类Window的成员变量mCallback中去,这样当这个PhoneWindow对象接收到系统给它分发的IO输入事件,例如,键盘和触摸屏事件,转发给与它所关联的Activity组件处理,这一点可以参考前面Android应用程序键盘(Keyboard)消息处理机制分析一文。
这一步执行完成之后,回到前面的Step 1中,即Activity类的成员函数attach中,接下来就会继续调用前面所创建的PhoneWindow对象从父类Window继承下来的成员函数setSoftInputMode来设置应用程序窗口的软键盘输入区域的显示模式,因此,接下来我们就继续分析Window类的成员函数setSoftInputMode的实现。
Step 6. Window.setSoftInputMode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public abstract class Window {
......
private boolean mHasSoftInputMode = false ;
......
public void setSoftInputMode( int mode) {
final WindowManager.LayoutParams attrs = getAttributes();
if (mode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
attrs.softInputMode = mode;
mHasSoftInputMode = true ;
} else {
mHasSoftInputMode = false ;
}
if (mCallback != null ) {
mCallback.onWindowAttributesChanged(attrs);
}
}
......
}
|
这个函数定义在文件frameworks/base/core/java/android/view/Window.java中。
参数mode有SOFT_INPUT_STATE_UNSPECIFIED、SOFT_INPUT_STATE_UNCHANGED、SOFT_INPUT_STATE_HIDDEN、SOFT_INPUT_STATE_ALWAYS_HIDDEN、SOFT_INPUT_STATE_VISIBLE和SOFT_INPUT_STATE_ALWAYS_VISIBLE一共六个取值,用来描述窗口的软键盘输入区域的显示模式,它们的含义如下所示:
1. SOFT_INPUT_STATE_UNSPECIFIED:没有指定软键盘输入区域的显示状态。
2. SOFT_INPUT_STATE_UNCHANGED:不要改变软键盘输入区域的显示状态。
3. SOFT_INPUT_STATE_HIDDEN:在合适的时候隐藏软键盘输入区域,例如,当用户导航到当前窗口时。
4. SOFT_INPUT_STATE_ALWAYS_HIDDEN:当窗口获得焦点时,总是隐藏软键盘输入区域。
5. SOFT_INPUT_STATE_VISIBLE:在合适的时候显示软键盘输入区域,例如,当用户导航到当前窗口时。
6. SOFT_INPUT_STATE_ALWAYS_VISIBLE:当窗口获得焦点时,总是显示软键盘输入区域。
当参数mode的值不等于SOFT_INPUT_STATE_UNSPECIFIED时,就表示当前窗口被指定软键盘输入区域的显示模式,这时候Window类的成员函数setSoftInputMode就会将成员变量mHasSoftInputMode的值设置为true,并且将这个显示模式保存在用来描述窗口布局属性的一个WindowManager.LayoutParams对象的成员变量softInputMode中,否则的话,就会将成员变量mHasSoftInputMode的值设置为false。
设置完成窗口的软键盘输入区域的显示模式之后,如果Window类的成员变量mCallback指向了一个窗口回调接口,那么Window类的成员函数setSoftInputMode还会调用它的成员函数onWindowAttributesChanged来通知与窗口所关联的Activity组件,它的窗口布局属性发生了变化。
这一步执行完成之后,回到前面的Step 1中,即Activity类的成员函数attach中,接下来就会继续调用前面所创建的PhoneWindow对象从父类Window继承下来的成员函数setWindowManager来设置应用程序窗口的本地窗口管理器,因此,接下来我们就继续分析Window类的成员函数setWindowManager的实现。
Step 7. Window.setWindowManager
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public abstract class Window {
......
private WindowManager mWindowManager;
private IBinder mAppToken;
private String mAppName;
......
public void setWindowManager(WindowManager wm,
IBinder appToken, String appName) {
mAppToken = appToken;
mAppName = appName;
if (wm == null ) {
wm = WindowManagerImpl.getDefault();
}
mWindowManager = new LocalWindowManager(wm);
}
......
}
|
这个函数定义在文件frameworks/base/core/java/android/view/Window.java中。
参数appToken用来描述当前正在处理的窗口是与哪一个Activity组件关联的,它是一个Binder代理对象,引用了在ActivityManagerService这一侧所创建的一个类型为ActivityRecord的Binder本地对象。从前面Android应用程序的Activity启动过程简要介绍和学习计划一系列文章可以知道,每一个启动起来了的Activity组件在ActivityManagerService这一侧,都有一个对应的ActivityRecord对象,用来描述该Activity组件的运行状态。这个Binder代理对象会被保存在Window类的成员变量mAppToken中,这样当前正在处理的窗口就可以知道与它所关联的Activity组件是什么。
参数appName用来描述当前正在处理的窗口所关联的Activity组件的名称,这个名称会被保存在Window类的成员变量mAppName中。
参数wm用来描述一个窗口管理器。从前面的调用过程可以知道, 这里传进来的参数wm的值等于null,因此,函数首先会调用WindowManagerImpl类的静态成员函数getDefault来获得一个默认的窗口管理器。有了这个窗口管理器之后,函数接着再使用它来创建一个本地窗口管理器,即一个LocalWindowManager对象,用来维护当前正在处理的应用程序窗口。
接下来,我们首先分析WindowManagerImpl类的静态成员函数getDefault的实现,接着再分析本地窗口管理器的创建过程,即LocalWindowManager类的构造函数的实现。
Step 8. WindowManagerImpl.getDefault
1
2
3
4
5
6
7
8
9
10
|
public class WindowManagerImpl implements WindowManager {
......
public static WindowManagerImpl getDefault()
{
return mWindowManager;
}
......
private static WindowManagerImpl mWindowManager = new WindowManagerImpl();
}
|
这个函数定义在文件frameworks/base/core/java/android/view/WindowManagerImpl.java中。
WindowManagerImpl类的静态成员函数getDefault的实现很简单,它只是将静态成员变量mWindowManager所指向的一个WindowManagerImpl对象返回给调用者,这个WindowManagerImpl对象实现了WindowManager接口,因此,它就可以用来管理应用程序窗口。
这一步执行完成之后,回到前面的Step 7中,即Window类的成员函数setWindowManager中,接下来就会使用前面所获得一个WindowManagerImpl对象来创建一个本地窗口管理器,即一个LocalWindowManager对象。
Step 9. new LocalWindowManager
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public abstract class Window {
......
private final Context mContext;
......
private class LocalWindowManager implements WindowManager {
LocalWindowManager(WindowManager wm) {
mWindowManager = wm;
mDefaultDisplay = mContext.getResources().getDefaultDisplay(
mWindowManager.getDefaultDisplay());
}
......
private final WindowManager mWindowManager;
private final Display mDefaultDisplay;
}
......
}
|
这个函数定义在文件frameworks/base/core/java/android/view/Window.java中。
LocalWindowManager类的构造函数首先将参数wm所描述的一个WindowManagerImpl对象保存它的成员变量mWindowManager中,这样以后就将窗口管理工作交给它来处理。
LocalWindowManager类的构造函数接着又通过成员变量mWindowManager所描述的一个WindowManagerImpl对象的成员函数getDefaultDisplay来获得一个Display对象,用来描述系统屏幕属性。
由于前面所获得的Display对象描述的是全局的屏幕属性,而当前正在处理的窗口可能配置了一些可自定义的屏幕属性,因此,LocalWindowManager类的构造函数需要进一步地调整前面所获得的Display对象所描述的屏幕属性,以便可以适合当前正在处理的窗口使用。LocalWindowManager类的构造函数首先通过外部类Window的成员变量mContext的成员函数getResources来获得一个Resources对象,接着再调用这个Resources对象的成员函数getDefaultDisplay来调整前面所获得的Display对象所描述的屏幕属性。最终调整完成的Display对象就保存在LocalWindowManager类的成员变量mDefaultDisplay中。
从前面的Step 4可以知道,类Window的成员变量mContext描述的是与当前窗口所关联的一个Activity组件。Activity类的成员函数getResources是从父类ContextWrapper继续下来的,它实现在文件frameworks/base/core/java/android/content/ContextWrapper.java中,如下所示:
1
2
3
4
5
6
7
8
9
10
|
public class ContextWrapper extends Context {
Context mBase;
......
@Override
public Resources getResources()
{
return mBase.getResources();
}
......
}
|
从前面Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析一文可以知道,ContextWrapper类的成员变量mBase指向的是一个ContextImpl对象,用来描述一个Activity组件的运行上下文环境。通过调用这个ContextImpl对象的成员函数getResources,就可以获得与一个Resources对象,而通过这个Resources对象,就可以访问一个Activity组件的资源信息,从而可以获得它所配置的屏幕属性。
至此,我们就分析完成一个Activity组件所关联的应用程序窗口对象的创建过程了。从分析的过程可以知道:
1. 一个Activity组件所关联的应用程序窗口对象的类型为PhoneWindow。
2. 这个类型为PhoneWindow的应用程序窗口是通过一个类型为LocalWindowManager的本地窗口管理器来维护的。
3. 这个类型为LocalWindowManager的本地窗口管理器又是通过一个类型为WindowManagerImpl的窗口管理器来维护应用程序窗口的。
4. 这个类型为PhoneWindow的应用程序窗口内部有一个类型为DecorView的视图对象,这个视图对象才是真正用来描述一个Activity组件的UI的。
在接下来的一篇文章中,我们将继续分析应用程序窗口内部的视图对象的创建过程,敬请关注!
Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析相关推荐
- Android应用程序资源管理器(Asset Manager)的创建过程分析
在前面一篇文章中,我们分析了Android应用程序资源的编译和打包过程,最终得到的应用程序资源就与应用程序代码一起打包在一个APK文件中.Android应用程序在运行的过程中,是通过一个称为Asset ...
- Android基本程序单元Activity总结
1.什么是Activity? 在Android当中,Activity提供可视化的用户界面,一个Android应用通常由多个Activity组成.多个Activity组成了Activity栈(Stack ...
- 解开Android应用程序组件Activity的singleTask之谜(3)
回到前面的startActivityUncheckedLocked函数中,这里的变量top就为null了,于是执行下面的else语句: if (top != null) { ...... } else ...
- Android应用程序注冊广播接收器(registerReceiver)的过程分析
前面我们介绍了Android系统的广播机制,从本质来说,它是一种消息订阅/公布机制,因此,使用这样的消息驱动模型的第一步便是订阅消息:而对Android应用程序来说,订阅消息事实上就是注冊广播接收器, ...
- Android中如何使用Intent在Activity之间传递对象[使用Serializable或者Parcelable]
Android中如何使用Intent在Activity之间传递对象[使用Serializable或者Parcelable] 在Android中的不同Activity之间传递对象,我们可以考虑采用Bun ...
- Android应用程序内部启动Activity过程(startActivity)的源代码分析
上文介绍了Android应用程序的启动过程,即应用程序默认Activity的启动过程,一般来说,这种默认Activity是在新的进程和任务中启动的:本文将继续分析在应用程序内部启动非默认Activit ...
- Android4.0窗口机制token分析以及activitiy, dialog, toast 窗口创建过程分析
From 51CTO 一 前言 在谈到这个话题的时候,脑海里面千头万绪,因为它涉及到了方方面面的知识- 比如Activity管理,窗口添加,Token权限验证等等- 既然这么复杂,那么我们就复杂的问 ...
- Android应用程序与SurfaceFlinger服务的关系概述和学习计划
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/7846923 SurfaceFlinger服务负 ...
- Android应用程序请求SurfaceFlinger服务创建Surface的过程分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/7884628 前面我们已经学习过Android应 ...
最新文章
- fzyzojP3372 -- [校内训练20171124]博弈问题
- 反射机制(java)
- (十五)算法设计思想之“回溯算法”
- ASP.NET MVC HandleError异常过滤器过滤器用法
- 2019南昌网络赛G. tsy‘s number(反演 + 积性函数O(n)预处理)
- 最大熵对应的概率分布
- 空间服务器有问题是系统问题吗,服务器空间租用两个主要问题要留意
- Redis基础1(定义及基础)
- simulink模块使用记录2-EnabledSubsystem/merge
- Flutter通过BasicMessageChannel实现Flutter 与Android iOS 的双向通信
- 4-3 面向复用的设计模式
- 关于微信小程序使用腾讯地理位置服务,线上版本无法生效的问题
- account.php,account.php
- qt 当前窗口句柄_QT获取窗口句柄
- MyEclipse 2014 之 安装JDK 7 与JRE 7 (二)
- 计算机专业表情包图片,各个专业表情包盘点 | 你的专业也有自己专属表情包吗?...
- 小米 12S Utra 莱卡水印生成在线工具
- Knime 发送邮件
- 那些散落在人间的天使
- 医学影像SCI发表攻略
热门文章
- nodejs如何实现ajax,nodejs使用静态服务器处理ajax
- [BUUCTF-pwn]——bjdctf_2020_babystack
- 应用服务器与数据库之间是长连接,要接收多个 tcp 长连接不断发送的数据并存储,哪些数据库或数据存储方案比较合适?...
- java不同网址提示过期_使用history.back()出现警告: 网页已过期的解决办法
- struts2.2.1.1 类型转换问题 (传递Date类型参数)
- Lecture4_14_2.多维随机变量及其概率分布
- Bug in Code CodeForces - 420C (计数,图论)
- JQuery------各种版本下载
- 在linux上一行代码不用写实现自动采集+hadoop分词
- logback.xml配置详解