简析Window、WindowManager的工作流程

一、简介

1、Activity

Activity并不负责视图控制,它只是控制生命周期和处理事件。真正控制视图的是Window。一个Activity包含了一个Window。

2、Window

Window是视图的承载器,内部持有一个 DecorView,而这个DecorView才是 view 的根布局。Window是一个抽象类,Activity中持有的是其子类PhoneWindow。PhoneWindow中有个内部类DecorView,通过创建DecorView来加载Activity中设置的布局R.layout.activity_main。Window 通过WindowManager将DecorView加载其中,并将DecorView交给ViewRoot,进行视图绘制以及其他交互。

3、DecorView

DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图。DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上下三个部分,上面是个ViewStub,延迟加载的视图(应该是设置ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。其布局如下:


    
    
    
        
    
    

4、ViewRoot

ViewRoot可能比较陌生,但是其作用非常重大。所有View的绘制以及事件分发等交互都是通过它来执行或传递的。ViewRoot对应ViewRootImpl类,它是连接WindowManagerService和DecorView的纽带,View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRoot来完成。真正操控绘制View的是ViewRootImpl,View通过WindowManager来转接调用ViewRootImpl。

二、Window、Activity、DecorView以及ViewRoot关系

Activity中持有Window、Window中持有DecorView、ViewRootImpl中持有DecorView、WindowManagerGlobal中持有ViewRootImpl。

  • Window在Activity的attach方法中创建
  • DecorView在Window的setContentView方法中创建
  • ViewRootImpl在WindowManagerGlobal的addView方法中创建,addView会传入DecorView,DecorView会设置到ViewRootImpl

当用户点击屏幕产生一个触摸行为,这个触摸行为则是通过底层硬件来传递捕获,然后交给ViewRootImpl,接着将事件传递给DecorView,而DecorView再交给PhoneWindow,PhoneWindow再交给Activity,然后接下来就是我们常见的View事件分发了。硬件(WindowManager) -> ViewRootImpl -> DecorView -> PhoneWindow -> Activity。

三、Activity的Window创建

1、新建Window

Activity在attach方法中创建window对象并设置WindowManager

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);//创建一个Window对象
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);//设置回调,向Activity分发点击或状态改变等事件
        mWindow.setOnWindowDismissedCallback(this);

        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//给Window设置WindowManager对象
    }

2、初始化DecorView(setContentView中实现)

Activity的setContentView()方法如下:

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

调用attach方法中生成的window的setContentView方法,Window实现类PhoneWindow中的setContentView如下:

public void setContentView(int layoutResID) {
    // 第一步 创建DecorView,获取mContentParent,mContentParent是DecorView中的
    // android.id.content,setContentView设置的View就放在mContentParent中。
    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 {
        // 第二步 将View添加到DecorView
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        // 第三步  通知Activity视图发生改变
        cb.onContentChanged();
    }
}

 1)、 创建DecorView,获取mContentParent

mContentParent是DecorView中的android.id.content,setContentView设置的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设置布局格式,并返回mContentParent
        mContentParent = generateLayout(mDecor); 
    }
    ......
}

a、 创建DecorView在generateDecor中实现,源码如下:

protected DecorView generateDecor() {
    return new DecorView(getContext(), -1);
}

这时等到的DecorView还是一个空白的 FrameLayout。

b、获取mContentParent

mContentParent就是DecorView布局中@android:id/content所对应的FrameLayout。为了初始化DecorView的内部结构,PhoneWindow还需要通过generateLayout加载我们定义的布局到DecorView的android:id/content中。代码如下:

protected ViewGroup generateLayout(DecorView decor){
    // 前面线根据不同系统版本和主题确定布局文件放在layoutResource中
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
}

b1、onResourcesLoaded是DecorView中的方法,代码如下:

void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
    final View root = inflater.inflate(layoutResource, null);
    mDecorCaptionView.addView(root,
                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
    mContentRoot = (ViewGroup) root;
}

先加载了布局文件,然后将布局文件放在DecorView的成员变量mDecorCaptionView中。

b2、

ID_ANDROID_CONTENT是com.android.internal.R.id.content。contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);获取的contentParent 就是我们setContentView是把View放入的父控件。

2)、第二步 将View添加到DecorView 

这步就比较简单了。mLayoutInflater.inflate(layoutResID, mContentParent); layoutResID就是我们自己定义的布局文件,mContentParent就是上面获取DecorView中的content。通过inflate就可以加载布局文件并设置到mContentParent中。到这里Activity中的布局文件已经加载到DecorView中了。

3)、第三步  通知Activity视图发生改变

cb这样获取的cb = getCallback(); getCallback返回的是Window中定义的mCallback。那这个mCallback在什么地方赋值呢?

在Activity的attach()方法新建Window时有这样一句代码:mWindow.setCallback(this); 这里setCallback就是为mCallback赋值,所以Callback的具体实现在Activity中。通过调用cb.onContentChanged实际调用Activity中的onContentChanged,这样就通知了Activity视图发生改变。

3、WindowManeger添加DecorView

经过上面的步骤DecorView就初始化完成了。Activity中定义的布局也加载到DecorView的mContentParent中了。但是DecorView还没有被WindowManager添加到Window中。

在ActivityThread的handleResumeActivity中先调用Activity的onResume方法,然后调用Activity的makeVisible()。在makeVisible将DecorView添加到Window中。然后Activity的界面才能被看到。makeVisible源码如下:

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

四、WindowManeger添加View

最终调用wm.addView(decor, l),将DecorView添加到当前Activity的窗口上面。WindowManager是个抽象类,其具体实现为WindowManagerImplWindowManagerImpl中的addView()方法如下:

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);//调用WindowManagerGlobal的addView方法
}

mGlobalWindowManagerGlobal类型,WindowManagerGlobal的mGlobal源码如下:

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    //...

    ViewRootImpl root;
    View panelParentView = null;

    //...

    root = new ViewRootImpl(view.getContext(), display);//创建一个ViewRootImpl对象

    //...

    root.setView(view, wparams, panelParentView);//将DecorView到ViewRootImpl中,这里的view就是DecorView
    //...
}

ViewRootImpl的setView源码如下:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    // 第一步 完成异步刷新,开始绘制整个ViewTree    
    requestLayout(); 
    // 第二步 完成Window的添加
    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,  
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
}

1、 第一步 完成异步刷新,开始绘制整个ViewTree

ViewRootImpl的requestLayout源码如下:

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

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);//第2步
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();//第3步
    }
}

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

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

        performTraversals();//第4步

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

经过上面1、2、3、4步的调用最终调用performTraversals方法。ViewRootImpl的performTraversals方法如下:

private void performTraversals() {
    //...
    //获得view宽高的测量规格,mWidth和mHeight表示窗口的宽高,lp.widthhe和lp.height表示DecorView根布局宽和高
    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//执行测量
    //...
    performLayout(lp, mWidth, mHeight);//执行布局
    //...
    performDraw();//执行绘制
    //...
}

下面以测量为例,布局和绘制类似。performMeasure的源码如下:

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    if (mView == null) {
        return;
    }
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

mView就是DecorView的实例,DecorView是FrameLayout子类,mView.measure就是调用FrameLayout的measure,FrameLayout的measure在其父类View中定义的。View中的measure源码如下:

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    ......
    if (forceLayout || needsLayout) {
        // first clears the measured dimension flag
        mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;

        resolveRtlPropertiesIfNeeded();

        int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
        if (cacheIndex < 0 || sIgnoreMeasureCache) {
            // measure ourselves, this should set the measured dimension flag back
            onMeasure(widthMeasureSpec, heightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        } else {
            long value = mMeasureCache.valueAt(cacheIndex);
            // Casting a long to int drops the high 32 bits, no mask needed
            setMeasuredDimensionRaw((int) (value >> 32), (int) value);
            mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }
        ......
    }
    ......
}

最终调用了onMeasure方法,FrameLayout的onMeasure方法如下:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int count = getChildCount();
    ......
    setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
            resolveSizeAndState(maxHeight, heightMeasureSpec,
                    childState << MEASURED_HEIGHT_STATE_SHIFT));
    count = mMatchParentChildren.size();
    if (count > 1) {
        for (int i = 0; i < count; i++) {
            final View child = mMatchParentChildren.get(i);
            ......
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }
    }
}

FrameLayout中先通过setMeasuredDimension设置自己的大小。然后遍历子View调用child.measure完成子View的测量。在子View的measure中又会调用onMeasure完成自己的测量。这样就将测量过程一层一层传递下去了。流程如下图:

简析Window、WindowManager的工作流程_第1张图片

2、第二步 完成Window的添加 

然后调用    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,  
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);   完成Window的添加。

mWindowSession的类型是IWindowSession ,它是一个Binder对象,真正实现的是类Session。这个类的位置是在framework/base/services/core/java/com/android/server/wm/Session.java。Session中addToDisplay的源码如下:

public class Session extends IWindowSession.Stub
        implements IBinder.DeathRecipient {
    final WindowManagerService mService;

    @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);
    }
}

Session内部会通过WindowManagerService来实现Window添加。WindowManagerService也是一个Binder对象,最终添加逻辑在WindowManagerService中实现。

你可能感兴趣的:(Android)