Window和WindowManager相关知识点(六)

前几节已经做好了铺垫,接下来将将了解Window和WindowManager相关知识点,本章也是简单讲解,配合源码查看效果更佳

一、Window表示一个窗口的概念,是一个抽象类,具体实现是PhoneWindow,通过WindowManager即可创建一个Window。WindowManager和WindowManagerService的交互是一个IPC的过程。Android中所有的视图都是通过Window来呈现的,不管是Activity、Dialog还是Toast,它们的视图都是附加在Window上的,因此Window实际是View的直接管理者。之前也介绍过View的事件分发机制,单击事件由Window传递给DecorView,然后再由DecorView传递给我们的View。

二、Window内部机制

Window的使用自行搜索,注意Window有三种类型,分别是应用Window、子Window和系统Window。应用类的Window对应一个Activity,子Window不能单独存在,需要附在特定的父Window之中。系统Window需要声明权限。它们对应的层级范围分别是1~ 99、1000 ~ 1999、2000 ~ 2999。

  1. WindowManager类继承ViewManager接口,具体实现类是WindowManagerImpl。Window的添加、删除、更新都是通过WindowManagerImpl来实现的。

  2. 添加过程

    • 调用WindowManagerImpl的addView方法,内部维护WindowManagerGlobal,Window的添加、删除、更新都需要委托给它来实现。主要代码如下

      root = new ViewRootImpl(view.getContext(), display);
      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);
      } 
      ........
      
    • 这部分代码在WindowManagerGlobal的addView方法内,其中mViews存储的是所有Window对应的View,mRoots存储的是所有Window对应的ViewRootImpl,mParams存储的是所有Window所对应的布局参数,其实还有一个mDyingViews,它存储的是那些正在被删除的View对象。

    • 最后调用ViewRootImpl的setView方法关联了View,并且方法内会调用自身的requestLayout,最终会调用performTraversals方法来完成View的绘制。接着会通过WindowSession的addToDisplay方法来完成Window的添加过程,其中WindowSession类型是IWindowSession,是个Binder对象,真正实现类是Session,所以添加过程是一次IPC过程,远程调用了Session中的addToDisplay方法,Session的addToDisplay中会通过WindowManagerService来添加,并存储相关信息。

    • requestLayout代码如下

         @Override
          public void requestLayout() {
              if (!mHandlingLayoutInLayoutRequest) {
                  checkThread();
                  mLayoutRequested = true;
                  scheduleTraversals();
              }
          }
      

      这里会通过checkThread()方法来判断线程,这也就是为什么子线程不能更新UI的原因。scheduleTraversals()方法内部会通过handler去异步调用执行都doTaversal()方法,doTraversal()方法内调用了performTraversals()方法,这就回到了之前文章View的绘制讲解过程了。

  3. 删除过程

    • 该过程同样是调用WindowManagerGlobal的removeView方法实现的,以下是主要代码

      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);
              }
      
    • 代码逻辑很清楚,首先找到View索引,然后通过removeViewLocked来删除View,removeViewLocked方法其实也是通过ViewRootImpl来完成删除操作的,这里查看源码会发现有两种删除View的方法,一个是removeView,还有一个是remvoeViewImmediate,前者是异步删除,后者是同步删除。

    • removeViewLocked方法内会更新添加过程描述的那几个集合mViews、mRoots和mDyingViews等

          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);
                  }
              }
          }
      
    • 从removeViewLocked代码可以发现,具体删除View是通过root.die()方法,该方法就是判断是否是异步删除,是那么就发送一条MSG_DIE的消息,然后指定ViewRootImpl的doDie()方法。

          boolean die(boolean immediate) {
              // Make sure we do execute immediately if we are in the middle of a traversal or the damage
              // done by dispatchDetachedFromWindow will cause havoc on r
              eturn.
              if (immediate && !mIsInTraversal) {
                  doDie();
                  return false;
              }
      
              if (!mIsDrawing) {
                  destroyHardwareRenderer();
              } else {
                  Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
                          "  window=" + this + ", title=" + mWindowAttributes.getTitle());
              }
              mHandler.sendEmptyMessage(MSG_DIE);
              return true;
          }
      
    • 和添加一样,删除过程也是一个IPC过程,通过Session的remove方法,最终调用的还是WindowManagerService的removeWindow方法,View的dispatchDetachedFromWindow()方法和WindowManagerGlobal的doRemoveView方法刷新数据。

  4. 更新过程

    • 同理,调用的是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);
              }
          }
      

三、Window与Activity联系

  1. 通过之前Android四大组件原理分析学习,我们可以直接跳到ActivityThread的handleLaunchActivity()方法查看Activity的创建过程。大概流程就是在Activity创建时会先创建Window、然后执行onCreate、然后执行onStart方法,接下来可能执行onRestoreInstance方法,再执行onResume方法,再将WindowManager和DecorView绑定,再显示DecorView。

  2.   private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
            // If we are getting ready to gc after going to the background, well
            // we are back active so skip it.
            unscheduleGcIdler();
            mSomeActivitiesChanged = true;
            .......
    
            // Initialize before creating the activity
            WindowManagerGlobal.initialize();
    
            Activity a = performLaunchActivity(r, customIntent);
    
            if (a != null) {
                r.createdConfig = new Configuration(mConfiguration);
                reportSizeConfigurations(r);
                Bundle oldState = r.state;
                handleResumeActivity(r.token, false, r.isForward,
                        !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
                ........
        }
    

    其中WindowManagerGlobal.initialize()方法创建了WindowManagerServer,接下来会调用performLaunchActivity()

        private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
            // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
    
            ActivityInfo aInfo = r.activityInfo;
            if (r.packageInfo == null) {
                r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                        Context.CONTEXT_INCLUDE_CODE);
            }
    
            ComponentName component = r.intent.getComponent();
            if (component == null) {
                component = r.intent.resolveActivity(
                    mInitialApplication.getPackageManager());
                r.intent.setComponent(component);
            }
    
            if (r.activityInfo.targetActivity != null) {
                component = new ComponentName(r.activityInfo.packageName,
                        r.activityInfo.targetActivity);
            }
    
            ContextImpl appContext = createBaseContextForActivity(r);
            Activity activity = null;
            try {
                java.lang.ClassLoader cl = appContext.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);
                }
            }
    
            try {
                Application app = r.packageInfo.makeApplication(false, mInstrumentation);
                ........
    
                if (activity != null) {
                    CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                    Configuration config = new Configuration(mCompatConfiguration);
                    if (r.overrideConfig != null) {
                        config.updateFrom(r.overrideConfig);
                    }
                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                            + r.activityInfo.name + " with config " + config);
                    Window window = null;
                    if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                        window = r.mPendingRemoveWindow;
                        r.mPendingRemoveWindow = null;
                        r.mPendingRemoveWindowManager = null;
                    }
                    appContext.setOuterContext(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, window, r.configCallback);
    
                    if (customIntent != null) {
                        activity.mIntent = customIntent;
                    }
                    r.lastNonConfigurationInstances = null;
                    checkAndBlockForNetworkAccess();
                    activity.mStartedActivity = false;
                    int theme = r.activityInfo.getThemeResource();
                    if (theme != 0) {
                        activity.setTheme(theme);
                    }
    
                    activity.mCalled = false;
                    if (r.isPersistable()) {
                        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                    } else {
                        mInstrumentation.callActivityOnCreate(activity, r.state);
                    }
                    if (!activity.mCalled) {
                        throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                            " did not call through to super.onCreate()");
                    }
                    r.activity = activity;
                    r.stopped = true;
                    if (!r.activity.mFinished) {
                        activity.performStart();
                        r.stopped = false;
                    }
                    if (!r.activity.mFinished) {
                        if (r.isPersistable()) {
                            if (r.state != null || r.persistentState != null) {
                                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                        r.persistentState);
                            }
                        } else if (r.state != null) {
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                        }
                    }
                    if (!r.activity.mFinished) {
                        activity.mCalled = false;
                        if (r.isPersistable()) {
                            mInstrumentation.callActivityOnPostCreate(activity, r.state,
                                    r.persistentState);
                        } else {
                            mInstrumentation.callActivityOnPostCreate(activity, r.state);
                        }
                        if (!activity.mCalled) {
                            throw new SuperNotCalledException(
                                "Activity " + r.intent.getComponent().toShortString() +
                                " did not call through to super.onPostCreate()");
                        }
                    }
                }
                r.paused = true;
    
                mActivities.put(r.token, r);
    
            } catch (SuperNotCalledException e) {
                throw e;
    
            }
    		.......
            return activity;
        }
    
  3. mInstrumentation.newActivity创建Activity,内部通过CloassLoader来加载的,接下来会调用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,
                Window window, ActivityConfigCallback activityConfigCallback) {
            attachBaseContext(context);
    
            mFragments.attachHost(null /*parent*/);
    
            mWindow = new PhoneWindow(this, window, activityConfigCallback);
            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);
            }
            mUiThread = Thread.currentThread();
    
            mMainThread = aThread;
            mInstrumentation = instr;
            mToken = token;
            mIdent = ident;
            mApplication = application;
            mIntent = intent;
            mReferrer = referrer;
            mComponent = intent.getComponent();
            mActivityInfo = info;
            mTitle = title;
            mParent = parent;
            mEmbeddedID = id;
            mLastNonConfigurationInstances = lastNonConfigurationInstances;
            if (voiceInteractor != null) {
                if (lastNonConfigurationInstances != null) {
                    mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
                } else {
                    mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                            Looper.myLooper());
                }
            }
    
            mWindow.setWindowManager(
                    (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                    mToken, mComponent.flattenToString(),
                    (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
            if (mParent != null) {
                mWindow.setContainer(mParent.getWindow());
            }
            mWindowManager = mWindow.getWindowManager();
            mCurrentConfig = config;
    
            mWindow.setColorMode(info.colorMode);
        }
    
  4. attach方法一看就懂,这里就是创建Window窗口的。

  5. 回到performLaunchActivity()方法,创建好Window窗口后,会调用mInstrumentation.callActivityOnCreate、performStart()方法,然后再调用mInstrumentation.callActivityOnRestoreInstanceState()方法,而该方法内会调用onRestoreInstanceState(),所以先有Activity的onStart方法,再有onRestoreInstanceState()方法。

  6. 最后回到开始的handleLaunchActivity方法,接下来会调用handleResumeActivity()方法,该方法内通过如下代码将Window关联DecorView,利用r.window.getDecorView()来获取DecorView,后续会介绍如何创建的DecorView

            if (a.mVisibleFromClient) {
                        if (!a.mWindowAdded) {
                            a.mWindowAdded = true;
                            wm.addView(decor, l);
                        } else {
                            // The activity will get a callback for this {@link LayoutParams} change
                            // earlier. However, at that time the decor will not be set (this is set
                            // in this method), so no action will be taken. This call ensures the
                            // callback occurs with the decor set.
                            a.onWindowAttributesChanged(l);
                        }
                    }
    
  7. 既然关联了DecorView就需要显示出来,如果WindowManager还没有添加DecorView,这里又会添加一遍,代码如下

     //1.handleResumeActivity
     if (r.activity.mVisibleFromClient) {
                        r.activity.makeVisible();
                    }
                  .......
                  
       //2.Activity     
        void makeVisible() {
            if (!mWindowAdded) {
                ViewManager wm = getWindowManager();
                wm.addView(mDecor, getWindow().getAttributes());
                mWindowAdded = true;
            }
            mDecor.setVisibility(View.VISIBLE);
        }
    

四、DecorView和setContentView的联系

  1. 从上面可以看出先是创建了Window,然后再执行了onCreate方法,将DecorView和WindowMananger绑定和显示DecorView在onResume方法后,所以在Activity的setContentView内getWindow是不为空的。

  2. 大概流程是先调用PhoneWindow的setContentView,然后会判断是否有DecorView,无则创建,然后再将View添加到DecorView的mContentParent中,再回调Activity的onContentChanged方法。

        public void setContentView(@LayoutRes int layoutResID) {
            getWindow().setContentView(layoutResID);
            initWindowDecorActionBar();
        }
    
  3. getWindow().setContentView()实际上是执行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();
            }
            mContentParentExplicitlySet = true;
        }
    
    • 如果当前mContentParent没有放到窗口,也就是第一次调用的时候,会调用installDecor方法。FEATURE_CONTENT_TRANSITIONS是标记当前内容加载有没有使用过渡动画,如果内容已经加载过了,不需要动画就会调用removeAllViews。添加完Content后设置了该标记,则添加Scene来过度启动,否则则将我们的资源文件转换成一个View树,添加到mContentParent上。

    • installDecor方法,

          private void installDecor() {
              mForceDecorInstall = false;
              if (mDecor == null) {
                 //1.这里会创建一个DecorView,第一次调用是空的
                  mDecor = generateDecor(-1);
                  mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
                  mDecor.setIsRootNamespace(true);
                  if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                      mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
                  }
              } else {
                  mDecor.setWindow(this);
              }
              if (mContentParent == null) {
              	//2. 将DecorView添加到Window上绑定布局
                  mContentParent = generateLayout(mDecor);
      
                  // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
                  mDecor.makeOptionalFitsSystemWindows();
      
                  final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                          R.id.decor_content_parent);
      			........
                  if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
                      mDecor.setBackgroundFallback(mBackgroundFallbackResource);
                  }
      
            ........
          }
      
    • generateLayout方法,通过该方法,我们可以知道为什么需要在setConteView之前调用requestFeature,因为该方法是用来根据主题加载布局等设置的,例如无标题栏等。

      protected ViewGroup generateLayout(DecorView decor) {
              // Apply data from current theme.
              //1. 根据当前设置的主题来加载默认布局
              TypedArray a = getWindowStyle();
              //2. 如果你在theme中设置了window_windowNoTitle,则这里会调用到,其他方法同理,
              //  这里是根据你在theme中的设置去设置的
              if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
                  requestFeature(FEATURE_NO_TITLE);
              } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
                  // Don't allow an action bar if there is no title.
                  requestFeature(FEATURE_ACTION_BAR);
              }
              //3. 是否有设置全屏
              if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
                  setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
              }
              
              ...//省略其他加载资源
              
              // 4. 添加布局到DecorView,前面说到,DecorView是继承与FrameLayout,它本身也是一个ViewGroup,而我们前面创建它的时候,只是调用了new DecorView,此时里面并无什么东西。而下面的步奏则是根据用户设置的Feature来创建相应的默认布局主题。举个例子,如果我在setContentView之前调用了requestWindowFeature(Window.FEATURE_NO_TITLE),这里则会通过getLocalFeatures来获取你设置的feature,进而选择加载对应的布局,此时则是加载没有标题栏的主题,对应的就是R.layout.screen_simple
      
              int layoutResource;
              int features = getLocalFeatures();
              // System.out.println("Features: 0x" + Integer.toHexString(features));
              if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
                  layoutResource = R.layout.screen_swipe_dismiss;
              } ... //省略其他判断方法
              } else {
                  // Embedded, so no decoration is needed.
                  layoutResource = R.layout.screen_simple;
                  // System.out.println("Simple!");
              }
      
              mDecor.startChanging();
              //5. 选择对应布局创建添加到DecorView中
              View in = mLayoutInflater.inflate(layoutResource, null);
              decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
              mContentRoot = (ViewGroup) in;
              // 6. andorid.id.content
              ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
              ...
              return contentParent;
          }
      
    • 最后回到setContentView()方法,它最后cb.onContentChanged()其实是调用的Activity的onContentChanged方法,Activity默认实现了Window.Callback接口

你可能感兴趣的:(Android进阶)