Android---WindowWindowManager和WMS体系

Posted xiaoqiang_0719

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android---WindowWindowManager和WMS体系相关的知识,希望对你有一定的参考价值。

前言

本篇文章会从源码层面上分析Activity从创建直到在页面上显示的过程。

  1. 首先分析一下在这个过程中会遇到的一些接口和类
  2. 了解它们的源码以及功能之后再将它们串起来就能够了解整体的流程了

概述

类和接口

1. Window体系

Window体系说白了就是要在页面是显示的View,这个体系中包含多个类来共同完成view的显示其中包括
Activity、Window、PhoneWindow、DecorView

  • Activity
    • 这个概念在我们一开始学android的时候就理解为:一个Activity就是一个可视化的页面。
    • 因为它的setContentView()方法加载一个一个布局,这个布局也就跟我们看到的页面一样,所以可以说一个Activity就是一个页面
    • 然后我们深入setContentView()方法查看发现是调用了getWindow的setContentView方法,我们再来说一下Window
    public void setContentView(@LayoutRes int layoutResID) 
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    

  • Window

    • 查看Window源码发现Window是一个抽象类,他具体是实现类是PhoneWindow
    • 所以上述 getWindow().setContentView(layoutResID);方法中getWindow返回的就是PhoneWindow,也就是调用了PhoneWindow的setContentView()方法
  • PhoneWindow


   public PhoneWindow(Context context, Window preservedWindow,
            ActivityConfigCallback activityConfigCallback) 
        this(context);
        // Only main activity windows use decor context, all the other windows depend on whatever
        // context that was given to them.
        mUseDecorContext = true;
        if (preservedWindow != null) 
            mDecor = (DecorView) preservedWindow.getDecorView();
              ...
        
             ...
    
  • 在PhoneWindow的构造方法中我们发现PhoneWindow获取了它的内部类DecorView
    @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;
    
  • 到这里我们就可以知道PhoneWindow调用setContentView()方法将布局文件渲染在mContentParent这个viewGroup上了
  • 然后我们在找mContentParent是什么东西,源码第一句就判断mContentParent为空的估走了个installDecor()方法,我们判断这个方法因该是创建这个installDecor吧,好叻~继续扒源码
  private void installDecor() 
        mForceDecorInstall = false;
        if (mDecor == null) 
            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) 
            mContentParent = generateLayout(mDecor);

             .....

  • 可以看到installDecor方法再次判断mContentParent是否为空,然后调用了 mContentParent = generateLayout(mDecor);
  • 此方法不难看出,接收一个DecorView返回的这个名为mContentParent的ViewGroup ,ok继续
 protected ViewGroup generateLayout(DecorView decor) 
        // Apply data from current theme.

        TypedArray a = getWindowStyle();

        if (false) 
            System.out.println("From style:");
            String s = "Attrs:";
            for (int i = 0; i < R.styleable.Window.length; i++) 
                s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "="
                        + a.getString(i);
            
            System.out.println(s);
        

       .....

        mDecor.startChanging();
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//ID_ANDROID_CONTENT = 
                                                                            //com.android.internal.R.id.content;
        if (contentParent == null) 
            throw new RuntimeException("Window couldn't find content container view");
        

       ........

        return contentParent;
    
  • generateLayout() 这个方法源码贼多,但是我们大略可以猜到它是干嘛的,其实就是创建一个ViewGroup嘛,找return方法就完了

  • return contentParent; 说白了mContentParent 其实就是 DecorView的 android:id="@android:id/content"

  • 这个时候mContentParent不等于空了,然后 mLayoutInflater.inflate(layoutResID, mContentParent); 也就把这个布局渲染给了 DecorView的 android:id="@android:id/content"

  • DecorView

    • PhoneWindow的内部类,setContentView()方法最终完成的是将布局文件渲染到DecorView的content布局文件中

2. WindowManager体系

WindowManager体系的作用也就是管理Window最终管理的也就是渲染的View,主要有一下类或接口
ViewManager、 WindowManager、WindowManagerImpl、WindowManagerGlobal、 ViewRootImpl、WindowManagerService

  • ViewManager
    ViewManager是一个接口,它内部只有三个方法。添加、修改、删除
public interface ViewManager


    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);


  • WindowManager
    WindowManager也是一个接口它继承自ViewManager,它具体的实现是由WindowManagerImpl完成的

  • WindowManagerImpl
    WindowManagerImpl继承自WindowManager,重写了ViewManager的增删改方法,但是具体是委托给WindowManagerGlobal完成的

   @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) 
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) 
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
    

   @Override
    public void removeView(View view) 
        mGlobal.removeView(view, false);
    

  • WindowManagerGlobal

  • WindowManagerGlobal具体的增删改都是有它来完成的,以addView为例,将view,LayoutParams 作为参数传入到addView方法中。
    创建ViewRootImpl,并将view,ViewRootImpl,LayoutParams 添加到WindowManagerGlobal的List中

  private final ArrayList<View> mViews = new ArrayList<View>();
  private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
  private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
  • ViewRootImpl
  • 调用 root.setView(view, wparams, panelParentView); 这个方法主要做了两件事1、调用measure,layout,draw更新页面2、通知WindowManagerService进行Window的添加
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) 
    if (mView == null) 
        mView = view;
        ...
        //渲染UI
        requestLayout();
        ...
        try 
        ...
        //AIDL通知WindowManagerService
        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                getHostVisibility(), mDisplay.getDisplayId(),
                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                mAttachInfo.mOutsets, mInputChannel);
        
        mAttachInfo.mRootView = view;
    

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


    void scheduleTraversals() 
        if (!mTraversalScheduled) 
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) 
                scheduleConsumeBatchedInput();
            
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        
    
      //scheduleTraversals()中会通过handler去异步调用mTraversalRunnable接口。    

                     v
                     v
                     v
   final class TraversalRunnable implements Runnable 
        @Override
        public void run() 
            doTraversal();
        
    
    //也就是调用doTraversal方法

                     v
                     v
                     v
    void doTraversal() 
        if (mTraversalScheduled) 
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) 
                Debug.startMethodTracing("ViewAncestor");
            

            performTraversals();//最终会调用performTraversals

            if (mProfile) 
                Debug.stopMethodTracing();
                mProfile = false;
            
        
    

                     v
                     v
                     v
private void performTraversals()   
    ......  
    //测量View的宽高
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    ...
    //布置View的位置
    performLayout(lp, desiredWindowWidth, desiredWindowHeight);
    //监听事件
    if (triggerGlobalLayoutListener) 
        mAttachInfo.mRecomputeGlobalAttributes = false;
        //触发OnGlobalLayoutListener的onGlobalLayout()函数
        mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
    
    ......  
    //渲染View
    performDraw();
    
    ......  

//完成view的测量、布局、和 渲染
  • WindowManagerService
    ViewRootImpl通过进程间通信通知WindowManagerService添加Window,这地方有点复杂,我也看的不大清楚,就不误人子弟了 - -。
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
        Rect outOutsets, InputChannel outInputChannel) 
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
            outContentInsets, outStableInsets, outOutsets, outInputChannel);


整体流程

最后我在说一下我理解的整体的从Activity创建到显示页面的整体流程

  1. Avtivity的创建要从startActivity说起,首先会调用它的重载方法startActivityForResult(),由ActivityManagerService通过进程间的通信会调用到ApplicationActivity的ScheduleLaunchActivity发送一个启动Activity的消息交个Handler处理
  2. Handler收到消息后调用HandleLaunchActivity,最终会调用PerformLaunchActivity方法,使用类加载器加载Activity并调用Attach方法创建Window
  3. Window会使用其唯一子类PhoneWindow创建,并将layout渲染到PhoneWindow的内部类DectorView的ContentView中,渲染完成后。再执行Activity的OnResume方法,将Activity的Window对象中为View添加到WnidowManager中,此过程是有WindowManagerGlobal完成的。
  4. 在WindowManagerGlobal中创建ViewRootImpl,通过ViewRootImpl完成页面的更新和通知WindowManagerService完成Window的添加

以上是关于Android---WindowWindowManager和WMS体系的主要内容,如果未能解决你的问题,请参考以下文章