• activity window view 关系



    1、Activity , Window和View的关系是什么?

    跟踪Activity的源码就会发现:
    Activity.attch() -> PolicyManager -> Policy -> PhoneWindow -> mLayoutInflater.inflate()&mContentParent.addView()
    这只是一个简单的跟踪过程描述。通过跟踪源代码,就可以很清晰的看出他们三者的关系。

    Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图)
    LayoutInflater像剪刀,Xml配置像窗花图纸。

    1)一个Activity构造的时候会初始化一个Window,准确的说是PhoneWindow。
    2)这个PhoneWindow有一个“ViewRoot”,引号是说其实这个“ViewRoot”是一个View或者说ViewGroup,是最初始的根视图。
    3)“ViewRoot”通过addView方法来一个个的添加View。比如TextView,Button等
    4)这些View的事件监听,是由WindowManagerService来接受消息,并且回调Activity函数。比如onClickListener,onKeyDown等

    2、Activity是android的显示视图么?
    不是

    3、LayoutInflater是做什么的 ? layoutInflater.inflater()做什么的?
    一般来讲,我们用LayoutInflater做一件事:inflate。inflate这个方法总共有四种形式,目的都是把xml表述的layout转化为View。
    This class is used to instantiate layout XML file into its corresponding View objects . It is never be used directly -- use
    getLayoutInflater() or getSystemService(String)getLayoutInflater() or getSystemService(String) to retrieve a standard
    LayoutInflater instance that is already hooked up to the current context and correctly configured for the device you are
    running on

    ——————————————————————————————————————————————————————————————————

    Activity是Android应用程序的载体,允许用户在其上创建一个用户界面,并提供用户处理事件的API,如onKeyEvent, onTouchEvent等。 并维护应用程序的生命周期(由于android应用程序的运行环境和其他操作系统不同,android的应用程序是运行在框架之内,所以他的应用程序不能当当从进程的级别去考虑,而更多是从概念上去考虑。android应用程序是由多个活动堆积而成,而各个活动又有其独立的生命周期)。Activity本身是个庞大的载体,可以理解成是应用程序的载体,如果木有Activity,android应用将无法运行。也可以理解成android应用程序的入口。Acivity的实例对象由系统维护。系统服务ActivityManager负责维护Activity的实例对象,并根据运行状态维护其状态信息。
    但在用户级别,程序员可能根愿意理解成为一个界面的载体。但仅仅是个载体,它本身并不负责任何绘制。Activity的内部实现,实际上是聚了一个Window对象。Window是一个抽象类,它的具体是在android_src_home/framework/policies/base/phone/com/android/internal/policy/impl目录下的PhoneWindow.java。
    当我们调用Acitivity的 setContentView方法的时候实际上是调用的Window对象的setContentView方法,所以我们可以看出Activity中关于界面的绘制实际上全是交给Window对象来做的。绘制类图的话,可以看出Activity聚合了一个Window对象。
    下面是PhoneWindow中的setContentView方法的实现:

        @Override
        public void setContentView(View view, ViewGroup.LayoutParams params) {
            if (mContentParent == null) {
                installDecor();
            } else {
                mContentParent.removeAllViews();
            }
            mContentParent.addView(view, params);
            final Callback cb = getCallback();
            if (cb != null) {
                cb.onContentChanged();
            }
        }
    Window内部首先判断mContentParent是否为空,然后调用installDecor方法(安装装饰器),我们看看这个方法如何实现的   
      private void installDecor() {
            if (mDecor == null) {
                mDecor = generateDecor();
                mDecor.setIsRootNamespace(true);
            }
            if (mContentParent == null) {
                mContentParent = generateLayout(mDecor);
                mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
                if (mTitleView != null) {
                    if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                        View titleContainer = findViewById(com.android.internal.R.id.title_container);
                        if (titleContainer != null) {
                            titleContainer.setVisibility(View.GONE);
                        } else {
                            mTitleView.setVisibility(View.GONE);
                        }
                        if (mContentParent instanceof FrameLayout) {
                            ((FrameLayout)mContentParent).setForeground(null);
                        }
                    } else {
                        mTitleView.setText(mTitle);
                    }
                }
            }
        }
    在该方法中,首先创建一个DecorView,DecorView是一个扩张FrameLayout的类,是所有窗口的根View。我们在Activity中调用的setConctentView就是放到DecorView中了。这是我们类图的聚合关系如下:
    Activity--->Window--->DecorView
    这是我们得出这3个类之间最直接的一个关系。
    我们详细分析一下,类对象是如何被创建的。
    先不考虑Activity的创建(因为 Acitivity的实例由ActivityManager维护,是在另一个进程设计到IPC的通信,后面会讲到),而考虑Window和View的创建。
    Activity被创建后,系统会调用它的attach方法来将Activity添加到ActivityThread当中。我们找到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,
                Object lastNonConfigurationInstance,
                HashMap<String,Object> lastNonConfigurationChildInstances,
                Configuration config) {
            attachBaseContext(context);
          
    mWindow= PolicyManager.makeNewWindow(this);
            mWindow.setCallback(this);
            if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
                mWindow.setSoftInputMode(info.softInputMode);
            }
            mUiThread = Thread.currentThread();
            mMainThread = aThread;
            mInstrumentation = instr;
            mToken = token;
            mIdent = ident;
            mApplication = application;
            mIntent = intent;
            mComponent = intent.getComponent();
            mActivityInfo = info;
            mTitle = title;
            mParent = parent;
            mEmbeddedID = id;
            mLastNonConfigurationInstance = lastNonConfigurationInstance;
            mLastNonConfigurationChildInstances = lastNonConfigurationChildInstances;
            mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
            if (mParent != null) {
                mWindow.setContainer(mParent.getWindow());
            }
            mWindowManager = mWindow.getWindowManager();
            mCurrentConfig = config;
        }
    我们看红色的代码部分,就是创建Window对象的代码。感兴趣的同学可以跟踪去看看具体是如何创建的。其实很简单,其内部实现调用了Policy对象的makeNewWindow方法,其方法直接new了一个PhoneWindow对象如下:
    public PhoneWindow makeNewWindow(Context context) {
            return new PhoneWindow(context);
    }
    这时我们已经可以把流程串起来,Activity创建后系统会调用其attach方法,将其添加到ActivityThread当中,在attach方法中创建了一个window对象。
    下面分析View的创建。我们知道Window聚合了DocerView,当用户调用setContentView的时候会把一颗View树仍给DocerView.View树是已经创建好的实例对象了,所以我们研究的是DocerView是个什么东西,它是如何被创建的。
    我们回头看看Window实现里边的setContentView方法,我们看上面代码的红色部分setContentView->
    installDecor-> generateDecor.
    generateDecor直接new了一个DecorView对象:  
    protected DecorView generateDecor() {
            return new DecorView(getContext(), -1);
    }
    我们可以去看看DecorView的实现,它是PhoneWindow的一个内部类。实现很简单,它默认会包含一个灰色的标题栏,然后在标题栏下边会包含一个空白区域用来当用户调用setContentView的时候放置用户View,并传递事件,这里不做详细分析,感兴趣同学可以自己研究研究。
    当DecorView创建好之后再回到Window中的setContentView方法中来,见上面代码蓝色部分,调用
      mContentParent.addView(view, params); 
    来将用户的View树添加到DecorView中。

    到这时为止,我想我们已经很清晰的认识到它们3者之间的关系,并知道其创建流程。
    现在总结一下:
    Activity在onCreate之前调用attach方法,在attach方法中会创建window对象。window对象创建时并木有创建Decor对象对象。用户在Activity中调用setContentView,然后调用window的setContentView,这时会检查DecorView是否存在,如果不存在则创建DecorView对象,然后把用户自己的View 添加到DecorView中。

    PhoneWindow,ViewRoot,Activity之间的大致关系

    在android里,我们都知道activity.但是一个activity跟一个Window是一个什么关系呢?

    在activity.java中,我们可以看到两个变量,分别是:

    private Window mWindow;

    private WindowManager mWindowManager;

     

    这这个变量是在哪里赋值的呢?可以看到attach函数, 

    mWindow = PolicyManager.makeNewWindow(this); 

    跟进去看一下,可以发现返回的时候一个PhoneWindow对象,PhoneWindow是Window类的派生类。

    mWindow.setWindowManager(null, mToken, mComponent.flattenToString());

    if (mParent != null) {

    mWindow.setContainer(mParent.getWindow());

    }

    mWindowManager = mWindow.getWindowManager();

     

    仔细跟踪下这段代码中我们可以得到WindowManager对象,并且这个对象是在系统唯一的,这个对象同样被赋值给PhoneWindow的成员变量。

     

    我们在新建一个activity的时候,往往喜欢用如下的代码来定义该activity的UI界面,那么这个具体的实现是怎么来实现的呢?

     

    public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

     

    我们跟踪setContentView就知道了。可以发现activity其实把这个操作交给了自己的mWindow来完成,通过跟踪PhoneWindow中的setContentView函数可以得出以下知识:

    1,每一个PhoneWiondow中都有一个叫DecorView的对象,该对象是该PhoneWiondow的框架view,可以找到这么一段代码:

    View in = mLayoutInflater.inflate(layoutResource, null);

    decor.addView(in, new ViewGroup.LayoutParams(FILL_PARENTFILL_PARENT));

    layoutResource则是资源ID,通过往上翻代码就可以找得到。

     

    2,一个PhoneWiondow中都有一个叫FrameLayout的对象mContentParent,该对象是从上述layoutResource中的布局xml中获得的,这也将是activity中所有用户新增加view都会被包含在这个对象当中。

     

    总体可以这样大致说明:一个activity包含有一个PhoneWiondow对象,而所有的UI部件都是放在PhoneWiondow中。

    ViewRoot这个类在androidUI结构中扮演的是一个中间者的角色,连接的是PhoneWindowWindowManagerService.

    WindowManagerService中我们知道它读取android系统里所有事件,键盘事件,轨迹球事件等等,它怎么分发到各个activity的呢?就是通过这个ViewRoot

    在每个PhoneWindow创建的时候,系统都会向WindowManger中的一个保存View数组增加PhoneWindowDecorView对象,WindowManger在保存好这个View对象的同时,也会新创建一个ViewRoot对象用来沟通WindowManagerService

    可以查看WindowManagerImplViewRoot中的代码,同时ViewRoot中我们可以看到一个W类型,该类型派生自IWindow.stub可以知道这个可以被用来作远程调用。

    res = sWindowSession.add(mWindow, mWindowAttributes,

    getHostVisibility(), mAttachInfo.mContentInsets);

     

    IwindowSession则是WindowManagerService中的远程调用接口,上述代码也表明了每创建了一个PhoneWindow,也就创建了一个ViewRoot,并将在WindowManagerService注册。

     

    接下来简单的看下KeyEvent的传递。在WindowManagerService

    focus.mClient.dispatchKey(event);

     

    这个段代码的mClient则就是ViewRoot中的W类型,通过远程调用,可以看看ViewRoot的处理:

    boolean handled = mView != null

    ? mView.dispatchKeyEventPreIme(event) : true;

     

    mView则是PhoneWindowDecorView对象,接下来怎么具体传递可以慢慢去跟了
  • 相关阅读:
    Windows10 Docker 安装 dotnet core sdk 超时
    解决 jQuery-datepicker无法弹出日期的问题
    SQL2008 'OFFSET' 附近有语法错误。 在 FETCH 语句中选项 NEXT 的用法无效。
    “entities.LastOrDefault()”引发了类型“System.NotSupportedException”的异常
    快速开发平台
    快速设计ComboBox下拉框
    流程设计-流程模式
    流程设计-流程工具
    快速开发一款APP
    SDP开发
  • 原文地址:https://www.cnblogs.com/bigben0123/p/4244202.html
Copyright © 2020-2023  润新知