1:创建应用窗口首先需要创建一个Activity对象。当AmS决定启动某个Activity时,会通知客户端进程,每个客户端进程对应一个ActivityThread类,Activity的启动任务由ActivityThread来完成。启动Activity首先需要创建一个Activity对象,并使用ClassLoader从程序文件中装载指定的Activity对应的class文件。
在ActivityThread.java 函数里面private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) 如下代码段便是创建Activity对象。
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
2:构造好Activity对象后,调用activity的attach方法为构造好的activity对象设置内部变量,这些变量是以后进行Activity调度所必需的。
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor);
其中有几个重要的变量:
appContext:该对象作为Activity的BaseContext,前面说过Activity本质是个Context,有时又继承于ContextWrapper,该类需要一个真正的Context对象,就是appContext,该对象是使用new ContextXmpl()方法创建的。
this:这就是指当前ActivityThread对象,Activity对象内部可能会需要主程序的引用。
r.token:r是一个ActivityRecord对象。其内部变量token的含义是AmS中的一个HistoryRecord对象。
r.parent:一个Activity可以有一个父Activity,因此可以把一个Activity嵌入到另一个Activity内部执行,这常使用ActivityGroup类。
3:在attach方法内部除了变量赋值外,还一件事情是为Activity创建Window对象。mWindow = new PhoneWindow(this); mWindow.setCallback(this);并设置Window的CallBack接口为当前的Activity对象,这是为什么用户消息能够传递到Activity的原因。
4:接着给Window对象中的mWindowManager变量赋值。该变量的类型是WindowManager类,WindowManager仅仅是一个接口,真正实现该接口的有两个类。一个是Window.localWindowManager子类,另一个是WindowManagerImpl类。localWindowManager仅仅是个壳,有点像ContextWrapper,本身虽然也提供了WindowManager接口的全部功能,然后真正实现这些功能的却是壳里面的WindowManager对象,这就是WindowManagerImpl。
每个Activity内部也有一个mWindowManager对象,其直和Window类中的同名变量相同。
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
5:配置好Activity和Window对象后,接下来需要给该窗口中添加真正的显示元素View或者ViewGroup。这从第一步讲到的performLaunchActivity函数里面的callActivityOnCreate函数开始的。并辗转来到Activity的onCreate。在Activity中添加界面调用setContentView方法,其内部实际又是调用到了其所对应的Window对象的setContentView方法。
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initActionBar();
}
6:下面我们看Window中如何把一个layout.xml文件作为Window界面的。步骤5中getWindow()获得的是mWindow对象,在步骤3中我们看到mWindow对象赋值为PhoneWindow类型的对象,所以步骤5中实际是调用的PhoneWindow类里面的SetContentView
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
首先调用installDecor方法为Window类安装一个窗口修饰,所谓的窗口修饰就是界面上常见的标题栏,程序中指定的layout.xml界面将被包含在窗口修饰中,称为窗口内容。窗口修饰也是一个ViewGroup,窗口修饰及其内部的窗口内容加起来就是我们所说的窗口,或者叫做Window界面。
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
这段代码主要做三件事情:
a:使用generateDecor()创建一个mDecorView对象,并赋值给mDecor变量。该变量并不完全等同于窗口修饰,窗口修饰是mDecor内部的唯一一个子视图。
b:根据用户指定的参数选择不同的窗口修饰,并把该窗口修饰作为mDecor的子窗口,这是在generateDecor中调用mDecor.addView()完成。
c:给mContentParent变量赋值,其值是通过调用 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);来赋值的,而ID_ANDROID_CONTENT是id = content的FrameLayout。
不同的窗口修饰区别不大,但是共同的特点是其内部必须包含一个id=content的FrameLayout,因为内容窗口正是被包含在该FrameLayout中。
安装完窗口修饰后,就可以把layout.xml文件添加到窗口修饰中。通过上面代码中的inflate函数来完成,该方法的第二个参数是mContentParement上面说过,该值是通过 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);来赋值的,而ID_ANDROID_CONTENT是id = content的FrameLayout。也就是说窗口内容是添加在该FrameLayout中。最后调用cb.onContentChanged();方法通知应用程序内容发生了改变。而cb正是Activity自身。因为在步骤3中的attach函数中说到,Activity实现了Window.CallBack接口,并将自身作为Window对象的CallBack接口实现。
上述b中指的根据用户指定的参数有两个地方 :
a:在Activity的onCreate()方法中调用得到当前Window,然后调用requestFeature方法指定。generateLayout方法中使用getLocalFeature()获取feature值,并根据这些值选择不同的窗口修饰
b:另一个是在AndroidMainifest.xml中Activity元素内部使用android:theme="xxx"指定。generateLayout()方法使用getWindowStyle()方法获取这些值。
7:给Window类设置完其视图后,剩下的就是把创建的这个窗口告诉给WmS,以便WmS能够把窗口显示到屏幕上。首先,Activity准备好后会通知AmS,AmS经过各种条件的判断最终调用Activity的makeVisible()方法。该方法及后续的各种调用将完成真正的把窗口添加进WmS。
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
8:makeVisible()方法中,先获得Activity内部的WindowManager对象,实际上是Window.localWindowManager对象,然后调用该对象的addView()方法,该方法第一个参数是一个DecorView对象,也是用户能看到的一个Activity对应的全部界面内容。第二个参数是在构造Window对象时默认构造的WindowManager.LayoutParams对象。该代码在Window类中的初始化代码中:
private final WindowManager.LayoutParams mWindowAttributes =
new WindowManager.LayoutParams();
WindowManager.LayoutParams的构造函数如下:
public LayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = TYPE_APPLICATION;
format = PixelFormat.OPAQUE;
}
以上构造函数说明,在默认情况下,窗口的类型是一个TYPE_APPLICATION类型,即应用程序窗口类型。
为什么上面不直接使用WindowManagerImpl类而使用localWindowManager类呢?原因是后者会检查WindowManager.LayoutParams的值,并给其内部token变量赋值,以便能够正确添加。而WindowManagerImpl类添加窗口时不会检查params的值。
9:最后会调用到WindowManagerImpl的addView,一个应用程序内部无论有多少个Activity,但只有一个WindowManagerImpl对象。在WindowManagerImpl类中维护三个数组,用于保存应用程序中所拥有的窗口的状态。分别是:
a:View[]mViews。这里每一个View对象都将成为WmS所认为的一个窗口
b:ViewRoot[]mRoots。所有的ViewRoot对象,mViews中每个View对象都对应的ViewRoot对象。
c:WindowManager.LayoutParams[]mParams。当把mViews中的View对象当做一个窗口添加进WmS中,WmS要求每一个被添加的窗口都要对应一个LayoutParams对象,mParams正是保存了每一个窗口对应的参数对象。
addView()的执行流程:
a:检查所添加的窗口是否已经添加过,不允许重复添加
b:如果所添加的窗口为子窗口类型,找到其父窗口,并保存在内部临时变量panelParentView中,该变量将作为后面调用ViewRoot的setView()参数。
c:创建一个新的ViewRoot,前面说过,每一个窗口都对应一个ViewRoot对象。
d:调用ViewRoot的setView()方法,完成最后的、真正意义上的添加工作。
10:完成上面说的新建一个ViewRoot对象后,需要把新建的ViewRoot对象添加到mRoots对象中,其添加的逻辑是,新建三个长度都加1的数组,然后把原来数组mViews、mRoots、mParams的内容复制到新建数组,并把新创建的View、ViewRoot及WindowManager.LayoutParams对象保存到三个数组的最后。
11:调用ViewRoot对象的SetView(View view,WindowManager.LayoutParams atts,View panelParentView)方法,该方法将完成最后的窗口添加工作,三个参数意义如下:
view:是WindowManagerImpl中mViews数组的一个元素,也就是新建窗口的界面。
attrs:即为添加窗口的参数,该参数描述该窗口的呈现风格、大小、位置等,尤其是其内部变量token,指明了该窗口和相关Activity的关系。
panelParentView:该对象也是WindowManagerImpl种mViews数组的一个元素,仅当该窗口有父窗口时,该值才有意义。
setView的执行流程如下:
a:给ViewRoot的重要变量赋值。包括mView、mWindowAttributes及mAttachInfo。对于mAttachInfo变量,其成员mRootView赋值为参数View。如果添加的窗口是子窗口,那么同时给mAttachInfo的成员mPanelParentWindowToken赋值,其值为父窗口的token。
b:调用requestLayout()发出界面重绘请求
c:调用sWindowSession.add(),通知WmS添加窗口。
到此已经完成了窗口创建的全部工作!