Android的Window工作机制

在Android中Window表示一个窗口,像我们日常使用的Activity就会有一个Window实例,用它来管理Activity中的视图呈现。Window其实是一个抽象类,它的具体实现类是PhoneWindow,每一个视图View的呈现都需要Window来实现,也可以说View是Window的实体存在

既然Window涉及到视图View的呈现,那么里面必然会有View的绘制行为,我们知道View绘制是通过ViewRootImpl来完成的,所以Window也必然会有对应的ViewRootImpl。Window管理着View的添加、删除、更新等操作,而ViewRootImpl则专门用来绘制Window管理的View

View在Window上的添加、删除、更新操作是通过WindowManager来实现,WindowManager是外界访问Window的入口,但是对Window的操作最终又会在WindowManagerService中实现,因为WindowManagerService在Android中是一个独立的进程,所以WindowManager和WindowManagerService的交互是一个IPC过程。下面先来分析一下Window的添加过程

Window的添加过程

Window添加视图的过程通过WindowManager的addView来实现,WindowManager是一个接口,它的真正实现是WindowManagerImpl,来看它的接口实现

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

    @Override
    public void removeViewImmediate(View view) {
        mGlobal.removeView(view, true);
    }

可以看到WindowManagerImpl并没有真正实现Window的三大操作,而是交给了WindowManagerGlobal来实现

    //WindowManagerGlobal的addView方法
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        //检查参数的合法性
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
       
        ****************省略代码*********************

            //创建一个ViewRootImpl
            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 {
                //利用ViewRootImpl的setView方法来完成view的绘制
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

mViews存储的是所有添加到Window的View,mRoots存储的是所有View对应的Viewrootimpl,mParams存储的是View的布局参数,而mDyingViews则存储那些正在被删除的View

通过Viewrootimpl的setView方法进入View的绘制阶段,来看看

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                ...
                // Schedule the first layout -before- adding to the window  
                // manager, to make sure we do the relayout before receiving  
                // any other events from the system.
                requestLayout();
                ...
                try {
                ...
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } 
    }

在setView方法中,通过requestLayout完成View的异步刷新,接着通过WindowSession来最终来完成Window的添加任务

  @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            //首先判断线程
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

  void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

绘制View首先会判断线程是否正确,这个就是我们平时在子线程中更新UI的时候会抛出的异常的原因了,然后执行scheduleTraversals

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

异步执行mTraversalRunnable接口

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    //执行
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

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

            performTraversals();

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

performTraversals会执行真正的View绘制,如下

private void performTraversals() {  
        ......  
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        ...
        performLayout(lp, desiredWindowWidth, desiredWindowHeight);
        ......  
        performDraw();
        }
        ......  
    }

从上面得知,Window在添加一个视图之前会先对View进行绘制,在完成绘制以后将通过WindowSession来实现跨进程的窗口添加工作,mWindowSession的一个Binder对象,真正的实现是Session,这也是Window添加过程中的一个IPC调用

   @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的addWindow方法,如此一来,Window的添加任务就交给了WindowManagerService了,具体它在里面是怎么实现的就不做深入

通过上面我们知道了Window的添加过程,至于它的删除和更新操作由于篇幅有限就不作分析了。我们知道,Android中有视图的组件有Activity、Dialog、Toast等,可以说有视图的地方就有Window,下面来分析一下Window在Activity中是如何创建的

Activity中Window的创建和添加

Activity的启动过程比较复杂,下面直接从ActivityThread的handleLaunchActivity开始分析Activity的创建

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       ...

        // Initialize before creating the activity
        WindowManagerGlobal.initialize();

        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward,
            ...
    }

在初始化了WindowManagerGlobal后,开始调用performLaunchActivity来创建Activity的实例


    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {           
            java.lang.ClassLoader cl = appContext.getClassLoader();
            //利用类加载器创建activity实例对象
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
        ...
        }
        try {
              ...
               appContext.setOuterContext(activity);
                //调用attach方法
                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 (r.isPersistable()) {
                    //调用oncreate方法
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    //调用oncreate方法
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                .....
                if (!r.activity.mFinished) {
                    //调用onstart方法
                    activity.performStart();
                    r.stopped = false;
                }

          //调用 activity的 OnRestoreInstanceState方法进行数据恢复                        
          mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                    r.persistentState);
        ...
        return activity;
    }

通过ClassLoader来创建一个Activity对象,后面是依次执行Activity的相关生命周期方法,在onCreate方法执行之前执行attach方法,来看看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);
        ...
        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);
    }

在attach方法中,创建了Window对象并设置窗口的相关回调,还设置了WindowManager,我们在Activity中通过getWindow和getWindowManager返回的实例就是在这里创建的了,到这里Window就已经创建完成。接下来将执行Activity的onCreate方法,我们知道Activity的视图也是在onCreate方法中通过setContentView来提供,来看setContentView方法

public void setContentView(View v) {
    //确保DecorView的创建
    ensureSubDecor();
    // 获取放置内容的ViewGroup
    ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content); 
    contentParent.removeAllViews();
    // 将内容view添加上去
    contentParent.addView(v);
    //通知activity视图已经发生改变
    mOriginalWindowCallback.onContentChanged();
}

首先确保DecorView的创建,DecorView是Activity视图的根视图,其中Activity视图的标题和内容视图都添加到DecorView中,它们的关系如下

private void ensureSubDecor() {
    if (!mSubDecorInstalled) {
        mSubDecor = createSubDecor();

*******省略代码*******

    }
}

如果DecorView没有初始化,就调用createSubDecor创建之

private ViewGroup createSubDecor() {
    TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);

    if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
        a.recycle();
        throw new IllegalStateException(
                "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
    }

    if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
    } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {
        // Don't allow an action bar if there is no title.
        requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
    }
    if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
        requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
    }
    if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
        requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
    }
    mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
    a.recycle();

    // Now let's make sure that the Window has installed its decor by retrieving it
    mWindow.getDecorView();

    final LayoutInflater inflater = LayoutInflater.from(mContext);
    ViewGroup subDecor = null;


    if (!mWindowNoTitle) {
        if (mIsFloating) {
            // If we're floating, inflate the dialog title decor
            subDecor = (ViewGroup) inflater.inflate(
                    R.layout.abc_dialog_title_material, null);

            // Floating windows can never have an action bar, reset the flags
            mHasActionBar = mOverlayActionBar = false;
        } else if (mHasActionBar) {
            /**
             * This needs some explanation. As we can not use the android:theme attribute
             * pre-L, we emulate it by manually creating a LayoutInflater using a
             * ContextThemeWrapper pointing to actionBarTheme.
             */
            TypedValue outValue = new TypedValue();
            mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);

            Context themedContext;
            if (outValue.resourceId != 0) {
                themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
            } else {
                themedContext = mContext;
            }

            // Now inflate the view using the themed context and set it as the content view
            subDecor = (ViewGroup) LayoutInflater.from(themedContext)
                    .inflate(R.layout.abc_screen_toolbar, null);

            mDecorContentParent = (DecorContentParent) subDecor
                    .findViewById(R.id.decor_content_parent);
            mDecorContentParent.setWindowCallback(getWindowCallback());

            /**
             * Propagate features to DecorContentParent
             */
            if (mOverlayActionBar) {
                mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
            }
            if (mFeatureProgress) {
                mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
            }
            if (mFeatureIndeterminateProgress) {
                mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
            }
        }
    } else {
        if (mOverlayActionMode) {
            subDecor = (ViewGroup) inflater.inflate(
                    R.layout.abc_screen_simple_overlay_action_mode, null);
        } else {
            subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
        }

        if (Build.VERSION.SDK_INT >= 21) {
            // If we're running on L or above, we can rely on ViewCompat's
            // setOnApplyWindowInsetsListener
            ViewCompat.setOnApplyWindowInsetsListener(subDecor,
                    new OnApplyWindowInsetsListener() {
                        @Override
                        public WindowInsetsCompat onApplyWindowInsets(View v,
                                WindowInsetsCompat insets) {
                            final int top = insets.getSystemWindowInsetTop();
                            final int newTop = updateStatusGuard(top);

                            if (top != newTop) {
                                insets = insets.replaceSystemWindowInsets(
                                        insets.getSystemWindowInsetLeft(),
                                        newTop,
                                        insets.getSystemWindowInsetRight(),
                                        insets.getSystemWindowInsetBottom());
                            }

                            // Now apply the insets on our view
                            return ViewCompat.onApplyWindowInsets(v, insets);
                        }
                    });
        } else {
            // Else, we need to use our own FitWindowsViewGroup handling
            ((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(
                    new FitWindowsViewGroup.OnFitSystemWindowsListener() {
                        @Override
                        public void onFitSystemWindows(Rect insets) {
                            insets.top = updateStatusGuard(insets.top);
                        }
                    });
        }
    }

    if (subDecor == null) {
        throw new IllegalArgumentException(
                "AppCompat does not support the current theme features: { "
                        + "windowActionBar: " + mHasActionBar
                        + ", windowActionBarOverlay: "+ mOverlayActionBar
                        + ", android:windowIsFloating: " + mIsFloating
                        + ", windowActionModeOverlay: " + mOverlayActionMode
                        + ", windowNoTitle: " + mWindowNoTitle
                        + " }");
    }

    if (mDecorContentParent == null) {
        mTitleView = (TextView) subDecor.findViewById(R.id.title);
    }

    // Make the decor optionally fit system windows, like the window's decor
    ViewUtils.makeOptionalFitsSystemWindows(subDecor);

    final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
            R.id.action_bar_activity_content);

    final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
    if (windowContentView != null) {
        // There might be Views already added to the Window's content view so we need to
        // migrate them to our content view
        while (windowContentView.getChildCount() > 0) {
            final View child = windowContentView.getChildAt(0);
            windowContentView.removeViewAt(0);
            contentView.addView(child);
        }

        // Change our content FrameLayout to use the android.R.id.content id.
        // Useful for fragments.
        windowContentView.setId(View.NO_ID);
        contentView.setId(android.R.id.content);

        // The decorContent may have a foreground drawable set (windowContentOverlay).
        // Remove this as we handle it ourselves
        if (windowContentView instanceof FrameLayout) {
            ((FrameLayout) windowContentView).setForeground(null);
        }
    }

    // Now set the Window's content view with the decor
    mWindow.setContentView(subDecor);

    contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
        @Override
        public void onAttachedFromWindow() {}

        @Override
        public void onDetachedFromWindow() {
            dismissPopups();
        }
    });

    return subDecor;
}

这里代码比较多,其实就是根据不同的Activity主题加载不同的布局来创建DecorView。因为布局的加载设置是一个异步的过程,从上面的setContentView方法中可以得知,创建好Activity的视图之后,会回调onContentChanged方法,此时代表可以显示视图给前台看了,然后调用makeVisible方法

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

如果Window还没有添加的话,则添加Window,此Window的视图是DecorView,然后设置DecorView为可见,这就完成了Activity的Winow的创建和添加

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值