Android窗口创建过程

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添加窗口。

到此已经完成了窗口创建的全部工作!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值