Window、Activity、View的关系

Activity的作用是创建一个窗口Window,不是真正的展示视图和管理View,实际上视图的展示是依附在Window上的,也是Window对View进行真正的管理,View是Window存在的实体。下面具体介绍三者之间的关系:

一、Window

Window表示一个窗口,它是一个抽象类,它的具体实现是PhoneWindow。在应用程序中,对Window的操作是通过WindowManager来进行的,而对window的真正处理是在WindowMangerService中进行的,WindowManagerService是一个独立的进程,所以WindowManager和WindowMangerService的交互是一个IPC过程。

Window有三种类型,应用Window(对应Activity)、子Window(对应Dialog)、系统Window(对应Toast、系统状态栏)。子Window是不能单独存在的,必须依附在父Window上,这也是Dialog必须依赖Activity才能展示的原因。

Window是分层的,Window的层级大小对应指向自己的Z坐标轴的Z坐标,层级越大(Z坐标越大)越在上层,会覆盖层级小的Window。应用Window的层级范围是1-99,子Window的层级范围是1000-1999,系统Window的层级范围是2000-2999。所以,Activity、Dialog、Toast同时存在时,我们可以看到,Toast在最上层,Dialog在中间层,Activity在最下层。

二、WindowManager

WindowManager是一个接口,它继承了ViewManager,它对View的操作主要是通过继承ViewManager里面的三个方法进行的(addView、updateViewLayout、removeView)。WindowManager的具体实现类是WindowManagerImpl类,WindowManagerImpl实现了那三个操作View的方法,而WindowManagerImpl类的实现实际又交给了一个代理类WindowManagerGlobal去处理,下面根据源码重点分析一下这个类。

WindowManagerGlobal的构造方法:

  private WindowManagerGlobal() {
  }

  public static WindowManagerGlobal getInstance() {
       synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
             }
               return sDefaultWindowManager;
            }
  }

通过构造方法和获取该类实例的方法可以看出,WindowManagerGlobal是单例的。

WindowManagerGlobal的几个重要的成员变量:

//存储所有Window所对应的View
private final ArrayList<View> mViews = new ArrayList<View>();

//存储所有Window所对应的ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();

//存储所有Window所对应的布局参数
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();

//存储所有将要被删除或者正在被删除的View
private final ArraySet<View> mDyingViews = new ArraySet<View>();

WindowManagerGlobal的addView方法关键代码:

 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
          .......................
          //关键代码
          root = new ViewRootImpl(view.getContext(), display);
          view.setLayoutParams(wparams);
          mViews.add(view);
          mRoots.add(root);
          mParams.add(wparams);
          root.setView(view, wparams, panelParentView);
          .......................
 }

从上面的源码可知,每次调用addView方法,都会创建一个跟View相对应的ViewRootImpl,并将ViewRootImpl、View、params都存储到相应的list中去(list分别对应上面那几个成员变量)。将View相关信息存储到列表中后,会调用ViewRootImpl的setView方法完成界面绘制和Window添加。在ViewRootImpl的setView方法中会调用requestLayout方法,在requestLayout中调用scheduleTraversals方法进行一系列绘制操作。requestLayout方法执行结束,继续走setView的后续代码,后续会通过WindowSession添加Window,WindowSession的真正实现类是Session,在Session内部会通过WindowManagerService真正实现Window的添加操作,所以Window的添加是一次IPC过程。

WindowManagerGlobal的removeView方法关键代码:

 public void removeView(View view, boolean immediate) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            removeViewLocked(index, immediate);
            if (curView == view) {
                return;
            }

            throw new IllegalStateException("Calling with view " + view
                    + " but the ViewAncestor is attached to " + curView);
        }
    }


  private void removeViewLocked(int index, boolean immediate) {
        ViewRootImpl root = mRoots.get(index);
        View view = root.getView();

        if (view != null) {
            InputMethodManager imm = InputMethodManager.getInstance();
            if (imm != null) {
                imm.windowDismissed(mViews.get(index).getWindowToken());
            }
        }
        boolean deferred = root.die(immediate);
        if (view != null) {
            view.assignParent(null);
            if (deferred) {
                mDyingViews.add(view);
            }
        }
    }

从上面的源码中可知,removeView调用了removeViewLocked方法,在removeViewLocked方法中会先调用ViewRootImpl的die方法(此时view并没有被删除掉),然后将要删除的View添加到mDyingViews 列表中,然后会触发ViewRootImpl的doDie方法,在doDie方法中会调用dispatchDetachedFromWindow方法执行真正的删除操作。在dispatchDetachedFromWindow方法内部,会通过Session调用WindowManagerService的remoeWindow方法删除Window,这是一个IPC过程。

WindowManagerGlobal的updateViewLayout方法关键代码:

 public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

        view.setLayoutParams(wparams);

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            root.setLayoutParams(wparams, false);
        }
    }

从上面的源码可知,在updateViewLayout方法里会更新View和ViewRootImpl的params布局参数,然后重新测量、布局、绘制。View重绘以后,还会通过Session来更新Window,通过Session操作WindowManagerService的relayoutWindow方法实现,这也是一个IPC过程。

三、Window、Activity、View的关系

Activity在启动过程中会调用ActivityThread的performLanuchActivity方法,在这方法中会创建Activty实例,并调用Activity的attach方法,在attach方法里,会创建Activity所对应的Window对象,并为Window设置回调接口(回调接口是该Activity,该Actiivty实现了Window的CallBack接口)。从Window的创建源码中可以发现,Window的具体实现类是PhoneWindow:

 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,
            Window window) {
        ........................
        //关键代码
        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        ........................
    }

Activity的视图加载是由setContentView完成的,下面从这个方法开始说起Activity视图的加载过程,以及Window、Activity、View三者之间的关系。

Activity的setContentView的源码:

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

从Activity的setContentView方法的源码中可以知道,最终调用的是 getWindow()的setContentView方法,此处的getWindow返回的就是Activity的attach方法中创建的PhoneWindow实例,所以接下来就将视线转移到PhoneWindow的setContentView方法里面,在PhoneWindow的setContentView方法里面会调用installDecor方法创建Activity的顶级View(DecorView),DecorView其实就是一个FrameLayout。下面看下PhoneWindow类里的相关源码(只摘出关键部分):

//PhoneWindow的setContentView方法
@Override
public void setContentView(int layoutResID) {
    //创建DecorView
    if (mContentParent == null) {
        installDecor();
    }

    //将我们自己写的xml文件布局添加到mContentParent中
    mLayoutInflater.inflate(layoutResID, mContentParent);
    mContentParent.requestApplyInsets();
 }

    //创建DecorView和mContentParent
    private void installDecor() {
        //创建DecorView
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
         }
          //创建mContentParent(是一个FrameLayout,我们自己写的xml文件布局就挂载到该View上)
          mContentParent = generateLayout(mDecor);
     }

  //创建mContentParent并返回
  protected ViewGroup generateLayout(DecorView decor) {
      //添加系统的一个布局文件到DecorView里(该布局文件的最外层是一个LinearLayout,上面是标题栏布局ViewStub,下面是contentParent布局)
      View in= mLayoutInflater.inflate(layoutResource, null);
      decor.addView(in,new ViewGroup.LayoutParams(MATCH_PARENT,MATCH_PARENT));

      //获取挂载我们自己的xml文件布局的根View
      ViewGroup contentParent=(ViewGroup)findViewById(ID_ANDROID_CONTENT);
      return contentParent;
 }

上面的过程执行后,只是View的过程执行完了,DecorView还没有被添加到Window中,最终会在Activity的makeVisible方法里,将DecorView通过WindowManager添加到Window中,并设置为Visible状态(设置为显示状态),此时Activity的视图才真正的呈现出来了:

void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}

为了更直观的看出Window、Activity、View三者之间的关系,下面附上三者的关系层级图:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/huideveloper/article/details/80733780
今日推荐