Activity/Window/View

来源:互联网 发布:手机淘宝购物付款流程 编辑:程序博客网 时间:2024/06/06 23:57

先看UML类图:



(1)PolicyManager是一个策略工厂;

(2)Policy是各种一个的生产者,具体是由PolicyManager进行生产操作;

(3)PhoneWindow是Android中的最基本的窗口系统,每个Activity 均会创建一个PhoneWindow对象,是Activity和整个View系统交互的接口。

(4)DecorView是当前Activity所有View的祖先,它并不会向用户呈现任何东西,它主要有如下几个功能:

A. Dispatch ViewRoot分发来的key、touch、trackball等外部事件;

B. DecorView有一个直接的子View,我们称之为System Layout,这个View是从系统的Layout.xml中解析出的,它包含当前UI的风格,如是否带title、是否带process bar等。可以称这些属性为Window decorations。

C. 作为PhoneWindow与ViewRoot之间的桥梁,ViewRoot通过DecorView设置窗口属性。


1、Activity的构成

当我们写Activity时会调用setContentView()方法,来加载布局,setContentView()方法源码如下:

public void setContentView(@LayoutRes int layoutResID) {        getWindow().setContentView(layoutResID);        initWindowDecorActionBar();} public Window getWindow() {        return mWindow;}
那么这个mWindow是在什么地方创建的呢?

1.1 Window的创建

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,            NonConfigurationInstances lastNonConfigurationInstances,            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {        attachBaseContext(context);        mFragments.attachHost(null /*parent*/);        mWindow = new PhoneWindow(this);        ...}

原来mWindow指的就是PhoneWindow,PhoneWindow是继承抽象类Window的,这样就知道getWindow()得到的是一个PhoneWindow,我们来看看PhoneWindow.java的setContentView()方法(PhoneWindow.java)

1.2 PhoneWindow#setContentView

public void setContentView(View view, ViewGroup.LayoutParams params) {        // 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();  //1        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {            mContentParent.removeAllViews();        }        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {            view.setLayoutParams(params);            final Scene newScene = new Scene(mContentParent, view);            transitionTo(newScene);        } else {             mLayoutInflater.inflate(layoutResID, mContentParent);// 2        }        final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {            cb.onContentChanged();        }    }

其中,在installDecor()方法中创建了DecorView,产生mDecor和mContentParent对象,并将我们界面添加到id为content的一个FrameLayout(ViewGroup)中。

下面就详细分析。

PhoneWindow#installDecor

    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) {            //根据窗口的风格修饰,选择对应的修饰布局文件,并且将id为content的FrameLayout赋值给mContentParent            mContentParent = generateLayout(mDecor);            //......            //初始化一堆属性值        }    }
installDecor方法实质就是产生mDecor和mContentParent对象。

DecorView就是Activity中的根View。接着查看DecorView的源码,发现DecorView是PhoneWindow类的内部类,并且继承FrameLayout。接着PhoneWindow#generateDecor

  protected DecorView generateDecor() {        return new DecorView(getContext(), -1);    }    protected ViewGroup generateLayout(DecorView decor) {        // Apply data from current theme.        TypedArray a = getWindowStyle();        //......        //依据主题style设置一堆值进行设置        // Inflate the window decor.        int layoutResource;        int features = getLocalFeatures();        //......        //根据设定好的features值选择不同的窗口修饰布局文件,得到layoutResource值        //把选中的窗口修饰布局文件添加到DecorView对象里,并且指定contentParent值        View in = mLayoutInflater.inflate(layoutResource, null);        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));        mContentRoot = (ViewGroup) in;        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);        if (contentParent == null) {            throw new RuntimeException("Window couldn't find content container view");        }        //......        //继续一堆属性设置,完事返回contentParent        return contentParent;    }

        可以看见上面方法主要作用就是:根据窗口的风格修饰类型为该窗口选择不同的窗口根布局文件。mDecor做为根视图将该窗口根布局添加进去,然后获取id为content的FrameLayout返回给mContentParent对象。

        所以installDecor方法实质就是产生mDecor和mContentParent对象。DecorView是顶级View,内部有titlebar和contentParent两个子元素,contentParent的id是content,而我们设置的main.xml布局则是contentParent里面的一个子元素。

        在DecorView创建完毕后,让我们回到PhoneWindow#setContentView方法,直接看②号代码: mLayoutInflater.inflate(layoutResID, mContentParent);这里加载了我们设置的main.xml布局文件,并且设置mContentParent为main.xml的父布局,至于它怎么加载的,这里就不展开来说了。

        到目前为止,通过setContentView方法,创建了DecorView和加载了我们提供的布局,但是这时,我们的View还是不可见的,因为我们仅仅是加载了布局,并没有对View进行任何的测量、布局、绘制工作。

1.3 总结

     (1)setContentView()三部曲

a、创建一个DecorView的对象mDecor,该mDecor对象将作为整个应用窗口的根视图;

b、依据Feature等style theme创建不同的窗口修饰布局文件,并且通过findViewById获取Activity布局文件该存放的地方(窗口修饰布局文件中id为content的FrameLayout);

c、将Activity的布局文件添加至id为content的FrameLayout内。

       函数栈关系:

PhoneWindow#setContentVIew

        PhoneWindow#installDecor

                PhoneWindow#generateDecor

     (2)Activity、 Window、View的关系
       类似于MVC模式,Activity为控制器,Window为模型,View为真正的视图。
a,一个Activity 构造的时候一定会构造一个Window(PhoneWindow),并且只有一个
b,这个Window有一个ViewRoot(View / ViewGroup)
c,ViewRoot通过addView方法来添加View,如TextView,Button
d,这些View的事件监听,是由WindowManagerService来接收信息,并且回调Activity函数,比如onClickListener,onKeyDown等。 

        一个Activity包含一个window对象,这个对象是由PhoneWindow来实现的,PhoneWindow将DecorView做为整个应用窗口的根View,而这个DecorView又将屏幕划分为两个区域一个是TitleView一个是ContentView,而我们平常做应用所写的布局正是展示在ContentView中的。



2、Activity的显示

        每一个Activity组件都有一个关联的Window对象,用来描述一个应用程序窗口。每一个应用程序窗口内部又包含有一个View对象,用来描述应用程序窗口的视图。

       上文分析了创建DecorView的过程,现在则要把DecorView添加到Window对象中。这个过程与Activity的创建过程相关:

        首先,在ActivityThread#handleLaunchActivity中启动Activity,在这里面会调用到Activity#onCreate方法,从而完成上面所述的DecorView创建动作,当onCreate()方法执行完毕,在handleLaunchActivity方法会继续调用到ActivityThread#handleResumeActivity方法。

2.1 ActivityThread#handleResumeActivity

final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {     //...    ActivityClientRecord r = performResumeActivity(token, clearHide); // 这里会调用到onResume()方法    if (r != null) {        final Activity a = r.activity;        //...        if (r.window == null && !a.mFinished && willBeVisible) {            r.window = r.activity.getWindow(); // 获得window对象            View decor = r.window.getDecorView(); // 获得DecorView对象            decor.setVisibility(View.INVISIBLE);            ViewManager wm = a.getWindowManager(); // 获得windowManager对象            WindowManager.LayoutParams l = r.window.getAttributes();            a.mDecor = decor;            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;            l.softInputMode |= forwardBit;            if (a.mVisibleFromClient) {                a.mWindowAdded = true;                wm.addView(decor, l); // 调用addView方法            }            //...        }    }}
        在该方法内部,获取该activity所关联的window对象,DecorView对象,以及windowManager对象,而WindowManager是抽象类,它的实现类是WindowManagerImpl,所以后面调用的是WindowManagerImpl#addView方法,我们看看源码:
public final class WindowManagerImpl implements WindowManager {        private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();    ...    @Override    public void addView(View view, ViewGroup.LayoutParams params) {        mGlobal.addView(view, params, mDisplay, mParentWindow);    }}
接着看WindowManagerGlobal#addView方法:
public void addView(View view, ViewGroup.LayoutParams params,            Display display, Window parentWindow) {        ...        ViewRootImpl root;        View panelParentView = null;        synchronized (mLock) {            ...            root = new ViewRootImpl(view.getContext(), display); // 1            view.setLayoutParams(wparams);            mViews.add(view);            mRoots.add(root);            mParams.add(wparams);        }        // do this last because it fires off messages to start doing things        try {            root.setView(view, wparams, panelParentView); // 2        } catch (RuntimeException e) {            // BadTokenException or InvalidDisplayException, clean up.            synchronized (mLock) {                final int index = findViewLocked(view, false);                if (index >= 0) {                    removeViewLocked(index, true);                }            }            throw e;        }    }

        先看①号代码处,实例化了ViewRootImpl类,接着,在②号代码处,调用ViewRootImpl#setView方法,并把DecorView作为参数传递进去,在这个方法内部,会通过跨进程的方式向WMS(WindowManagerService)发起一个调用,从而将DecorView最终添加到Window上,在这个过程中,ViewRootImpl、DecorView和WMS会彼此关联,调用关系如下:

ViewRootImpl#setView
         ViewRootImpl#requestLayout
                   ViewRootImpl#scheduleTraversals
         Session#addToDisplay //将W传给WMS
                   WindowManagerService#addWindow


(未完待续)

      


0 0
原创粉丝点击