Android应用程序窗口(Activity)窗口对象(Window)创建指南
在前文中,我们分析了android应用程序窗口的运行上下文环境的创建过程。由此可知,每一个activity组件都有一个关联的contextimpl对象,同时,它还关联有一个window对象,用来描述一个具体的应用程序窗口。由此又可知,activity只不过是一个高度抽象的ui组件,它的具体ui实现其实是由其它的一系列对象来实现的。在本文中,我们就将详细分析android应用程序窗口对象的创建过程。
从前面android应用程序窗口(activity)实现框架简要介绍和学习计划一文可以知道,在phone平台上,与activity组件所关联的窗口对象的实际类型为phonewindow,后者是从window类继承下来的。activity、window和phonwwindow三个类的关系可以参考android应用程序窗口(activity)实现框架简要介绍和学习计划一文中的图3和图5。为了方便接下来描述类型为phonewindow的应用程序窗口的创建过程,我们将这两个图拿过来,如以下的图1和图2所示:
图1 activity和window的类关系图
图2 window和phonewindow的类关系图
上述两个图中所涉及到的类的描述可以参考android应用程序窗口(activity)实现框架简要介绍和学习计划一文,本文主要从android应用程序窗口的创建过程来理解activity、window和phonwwindow三个类的关系。
从android应用程序窗口(activity)的运行上下文环境(context)的创建过程分析一文又可以知道,与activity组件所关联的一个phonewindow对象是从activity类的成员函数attach中创建的,如图3所示:
图3 android应用程序窗口的创建过程
这个过程可以分为9个步骤,接下来我们就详细分析每一个步骤。
step 1. activity.attach
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
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
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
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,如下所示:
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
......
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
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
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
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
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中,如下所示:
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的。
在接下来的一篇文章中,我们将继续分析应用程序窗口内部的视图对象的创建过程,敬请关注!