`

Android 中Activity,Window和View之间的关系

 
阅读更多

我想大多数人,对于这3个东西的概念能区分,但是具体区别在哪却很难说出来。

我这里根据我个人的理解来讲讲我个人对这3个概念的理解。当然这里设计到通用的事件窗口模型等通用GUI设计,我这里就不打算讲了,纯粹从概念上来进行区分。

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中。

上篇讲解了3个对象之间的关系和创建的时机。这篇讲解窗口是如何被绘制出来的。

首先,我们看一个概念。就是View的draw方法的doc:

Manually render this view (and all of its children) to the given Canvas.

意 思是说把View绘制在画布上。个人觉得这个概念很重要,View和Canvas 的关系,按常规的思维,肯定认为View聚合了Canvas对象,然后在View的onDraw 方法中,在View中绘制图形。实际上恰恰相反,Canvas 是由系统提供,view通过draw方法来把自身绘制在画布上。如果这样来理解的话,很多东西理解起来就很自然了。系统在window中提供一个 Canvas对象,DocerView通过调用draw方法来将自己绘制到canvas上。draw方法实际上是一个递归方法,他会循环调用孩子View 的draw方法来完成整棵树的绘制。所以实际上一个界面的绘制所用的Cavans是同一个对象。Canvas内部聚合了Matrix对象来实现坐标系的变 换。

这里将的是题外话,只是想让大家理解一个东西。

下面回到系统如何来绘制一个窗口。

android 系统提供了WindowManager,WindowManager顾名思义窗口管理器。实际上它只是对WindowManager服务做了一个包装。其 内部实现通过ISessionWindow和IWindow接口来和WindowManager服务来通信,这里设计到IPC的概念。IPC即进程间的通 讯,ANDROID通过IBinder接口来实现,IBinder通过transact方法来实现进程间的交互,这是一个使用很不友好的接口,好在 android还提供了aidl的更人性化的接口,它使得IPC通信就像调用普通的JAVA方法那样便捷。不了解aidl的同学可以自行研究研究(其实我 自己也是半桶水,了解概念,而用的不熟悉)

我来看Window是如何被添加到WindowManager的.

Activity有一个public的方法setVisible用来控制Activity的窗口是否显示。

我们看其内部实现发现其调用了makeVisible方法,该方法就是让Window显示在屏幕当中的方法,实现如下:

 void makeVisible() {

        if (!mWindowAdded) {

            ViewManager wm = getWindowManager();

            wm.addView(mDecor, getWindow().getAttributes());

            mWindowAdded = true;

        }

        mDecor.setVisibility(View.VISIBLE);

    }
 

这时我们立马就明白了,Activity通过Context来获取WindowManager对象,然后把Window对象 的DocerView添加到了WindowManager 服务中,所以android的窗口的创建和显示并不是在同一个进程中,而是把窗口的绘制和管理交给了专门的WindowManager服务,这也是 android framework给我提供的基础服务。

在上面绿色的代码当中,我们看看mDeocr是在哪被创建的。

public void onWindowAttributesChanged(WindowManager.LayoutParams params) {

      // Update window manager if: we have a view, that view is

       // attached to its parent (which will be a RootView), and

       // this activity is not embedded.

        if (mParent == null) {

            View decor = mDecor;

            if (decor != null && decor.getParent() != null) {

                getWindowManager().updateViewLayout(decor, params);

            }

        }

    }
 

搜索代码发现其创建的地方如上面红色代码。

这时 我们已经知道,Activity创建好Window之后只要调用WindowManager 的addView方法来将Window的DocerView添加进去即可是使Window显示出来。还方法Window其实是Activity中的概念, 在WindowManager中是直接和View打交道的。

下面我们开始研究WindowManager对象,打开其源代码,发现它是一个接口,且只是简单的扩展了ViewManager接口.并增加了一个方法

getDefaultDisplay():Display.  内部还有一个继承自ViewGroup.LayoutParam的内部类。

我们在framework/base/core/java/android/view/WindowManagerImpl.java 中找到其实现类。

我们找到其核心的实现方法addView 方法,如下:

private void addView(View view, ViewGroup.LayoutParams params, boolean nest)

    {

        if (Config.LOGV) Log.v("WindowManager", "addView view=" + view);

        if (!(params instanceof WindowManager.LayoutParams)) {

            throw new IllegalArgumentException(

                    "Params must be WindowManager.LayoutParams");

        }

        final WindowManager.LayoutParams wparams

                = (WindowManager.LayoutParams)params;

        

        ViewRoot root;

        View panelParentView = null;

        

        synchronized (this) {

            // Here's an odd/questionable case: if someone tries to add a

            // view multiple times, then we simply bump up a nesting count

            // and they need to remove the view the corresponding number of

            // times to have it actually removed from the window manager.

            // This is useful specifically for the notification manager,

            // which can continually add/remove the same view as a

            // notification gets updated.

            int index = findViewLocked(view, false);

            if (index >= 0) {

                if (!nest) {

                    throw new IllegalStateException("View " + view

                            + " has already been added to the window manager.");

                }

                root = mRoots[index];

                root.mAddNesting++;

                // Update layout parameters.

                view.setLayoutParams(wparams);

                root.setLayoutParams(wparams, true);

                return;

            }

            

            // If this is a panel window, then find the window it is being

            // attached to for future reference.

            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&

                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {

                final int count = mViews != null ? mViews.length : 0;

                for (int i=0; i<count; i++) {

                    if (mRoots[i].mWindow.asBinder() == wparams.token) {

                        panelParentView = mViews[i];

                    }

                }

            }

            

           root =newViewRoot(view.getContext());

            root.mAddNesting = 1;

            view.setLayoutParams(wparams);

            

            if (mViews == null) {

                index = 1;

                mViews = new View[1];

                mRoots = new ViewRoot[1];

                mParams = new WindowManager.LayoutParams[1];

            } else {

                index = mViews.length + 1;

                Object[] old = mViews;

                mViews = new View[index];

                System.arraycopy(old, 0, mViews, 0, index-1);

                old = mRoots;

                mRoots = new ViewRoot[index];

                System.arraycopy(old, 0, mRoots, 0, index-1);

                old = mParams;

                mParams = new WindowManager.LayoutParams[index];

                System.arraycopy(old, 0, mParams, 0, index-1);

            }

            index--;

            mViews[index] = view;

            mRoots[index] = root;

            mParams[index] = wparams;

        }

        // do this last because it fires off messages to start doing things

       root.setView(view, wparams, panelParentView);

    }
 

我们看看我标记未红色的两行代码  root = new ViewRoot(view.getContext());和  root.setView(view, wparams, panelParentView);

创建了一个ViewRoot对象,然后把view添加到ViewRoot中。

ViewRoot对象是handler的一个实现,其聚合了ISessionWindow和IWindow对象来和WindowManager服务进行IPC通信。

分享到:
评论

相关推荐

    Android 悬浮Activity ( Dialog风格)

    在Android开发中,"悬浮Activity"通常指的是一个可以悬浮在其他应用之上,类似Dialog样式但功能更为丰富的Activity。这样的设计可以提供用户一种便捷的方式访问或操作应用,而无需完全退出当前正在使用的应用。悬浮...

    Android夜间模式实现,通过在window上加一层半透明的View

    在Android中,每个Activity都与一个Window对应,它是一个抽象层,负责处理显示内容和用户交互。我们可以通过设置Window的属性来改变应用的视觉效果,例如背景颜色、透明度等。 要实现夜间模式,我们需要做以下几步...

    android中的窗口activity高级应用

    **Translucence Window**(半透明窗口)是Android 5.0(Lollipop)引入的新特性,它允许Activity的背景变得半透明,从而实现过渡效果和沉浸式体验。通过设置Window的属性,如FLAG_TRANSLUCENT_STATUS和FLAG_...

    Activity之间以及fragment之间跳转 共享元素动画实现

    在Android应用开发中,UI交互的流畅性和用户体验密切相关,而Activity和Fragment之间的跳转以及共享元素动画正是提升这种体验的重要手段。本篇文章将详细讲解如何在Android中实现Activity与Fragment之间的平滑过渡...

    android中设置activity的出现方式

    这涉及到Android的窗口动画(Window Animation)和场景过渡(Transition)。在本篇文章中,我们将深入探讨如何实现这一功能。 首先,Android中的窗口动画是指当Activity进入或退出屏幕时显示的视觉效果。这可以通过...

    随意调节你的activity的大小,activity 自定义窗口大小

    在Android系统中,WindowManager服务负责管理所有应用的窗口,而Activity实际上就是一种特殊的Window。默认情况下,Activity会全屏显示,但我们可以通过修改Activity的属性和使用自定义布局来改变这一行为。 1. ...

    Android的Window类详解

    在Android框架的核心部分,`Window`类位于`frameworks\base\core\java\android\view\Window.java`文件中。该文件连同注释一起约有一千多行代码,其中涵盖了`Window`类的基本属性和核心功能。 #### 三、Window类的...

    android启动以及activity生命周期分析

    在Android开发过程中,理解应用程序如何启动及Activity的生命周期是非常重要的。这不仅有助于我们更好地管理程序的状态,还能帮助我们在遇到内存泄漏等问题时进行快速定位和解决。 #### ActivityThread `...

    将一个activity转换成一个view并显示

    然后通过startActivity(String id, Intent intent)这个方法获取Window获取当前Window对象,再然后调用getDecorView()方法获取当前activity对应的view,这样就可以使用addView(View child)方法添加到activity中。

    Android的Window类

    本文将深入探讨Android中的`Window`类及其相关特性,帮助开发者更好地理解其工作原理以及如何在实际应用中使用。 #### 二、Window类介绍 `Window`类是Android框架提供的一个核心类,用于管理一个应用窗口的所有...

    Android中Window添加View的底层原理

    本文将深入探讨Android中Window添加View的底层原理,包括Window和WindowManager的概念、交互方式以及添加View的关键步骤。 首先,Window是一个抽象类,实际的实现是PhoneWindow。创建Window并不直接操作,而是通过...

    WindowView-一款强大的Android源码.rar

    5. 数据绑定和MVVM架构:WindowView可能采用了现代Android开发中的MVVM(Model-View-ViewModel)架构,利用DataBinding库将UI和数据模型绑定,提高代码可读性和可维护性。 6. 线程通信:在多线程环境下,WindowView...

    android 使用activity自定义下拉选择列表

    在Android开发中,Activity是应用的基本组件,用于与用户交互并展示内容。当我们需要实现一个自定义的下拉选择列表时,通常会涉及到对UI组件的定制和触摸事件的处理。下面将详细介绍如何在Android的Activity中创建...

    Android中Window的管理深入讲解

    Android 中 Window 管理深入讲解 Android 中的 Window 管理是 Android 开发中非常重要...Android 中的 Window 管理是非常复杂的,需要深入理解 Window 和 WindowManager 的关系,以及 Window 的 flags 等属性的使用。

    android webview中html页面点击按钮跳转到activity

    在Android开发中,Webview是一个非常重要的组件,它允许我们加载和显示网页内容。当我们需要在Webview中实现HTML页面与原生Android应用的交互时,例如点击HTML按钮跳转到一个新的Activity,就需要利用到JavaScript...

    Android应用程序窗口(Activity)的窗口对象(Window)

    在本文中,我们就将详细分析Android应用程序窗口对象的创建过程。为了方便接下来描述类型为PhoneWindow的应用程序窗口的创建过程,我们将这两个图拿过来,如以下的图1和图2所示:图1Activity和Win

    webview+html跳转activity

    在Android开发中,WebView是一个非常重要的组件,它允许我们在应用程序中加载和显示网页内容。WebView不仅能够呈现HTML、CSS和JavaScript,还支持与原生Android应用程序的交互,从而实现混合式应用开发。在这个主题...

    深入理解Android中的Window和WindowManager

    Window表示一个窗口的概念,Window是...Android中,所有的视图都是通过Window来呈现,不管是Activity、Dialog、还是Toast,它们的视图实际上都是附加在Window上,因此Window是实际View的直接管理者,单击事件由Window传

    Android 设置一个底部控件view随着软键盘的弹出而上移

    有一些开源库,如`android-useful-dialogs`和`android-keyboard-aware-scroll-view`,已经实现了键盘弹出时底部视图上移的功能。可以直接集成这些库,简化开发过程。 在"Android 底部控件随软键盘弹出demo"这个...

Global site tag (gtag.js) - Google Analytics