Window底层源码分析(一)

前言

Android 官方为了弱化进程的概念提出了一个组件,我们都知道android 有四大组件:活动,服务,广播,内容提供器,这四大组件可以说是组成了一个android 系统吧.今天我们要来说的是四大组件之一 activity中的一部分东西:window(窗口).
actiivty需要做的事有很多,比如控制自己的生命周期,和系统服务通信.但是我们一般使用的活动看起来就是一个窗口控制器.具体是为什么呢?

activity窗口初体验

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

这是一个活动创建的第一个方法.我们看到了它为我们设置了一个界面,这个界面设置在哪,继续更进去看..

Activity

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

可以看到我们里是通过getWindow()获取一个window,然后给它这是了一个布局.这个getWindow()获取到的是一个window对象,具体这是哪个对象,这个是特别容易找到的,在activity的attach()方法中实例化了,这个不是重点,我们知道这是一个phoneWindow对象就好了,接下来我们去看看phoneWindow的setContentView()方法吧.

Window的真面目

PhoneWindow

 @Override
    public void setContentView(int layoutResID) {

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

这个方法就是去创建一个DecorView,加载系统默认的布局,然后解析我们自己设置的布局文件,并添加到contentparentView中,具体的这个contentparent中是一个frameLayout 中添加了一个线性布局,有兴趣的同学可以跟进源码中看看.上面的代码也就是将布局初始化了,然后我们接下来进入到真真的流程中,看看窗口是怎么添加的.

我们进入我们最常进入的那个ActivityThread类中:

从handleLaunchActivity看起吧:
ActivityThread

 private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {

     ...

        Activity a = performLaunchActivity(r, customIntent); //创建一个activity

        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);
        ...
            }
        } else {
           ...
        }
    }

继续往下看

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {


    ...

if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                        wm.addView(decor, l);
                    } else{
                        a.onWindowAttributesChanged(l);
                    }
                }

        ...

上面代码中,我们可以看到从我们创建的activity中获取到了一个window 和一个View,而这个Window就是PhoneWindow,而这个View就是DecorView,我们往下就可以看到了下面他是将decor添加进了一个叫wm的东西里面了,这就是重点了,wm 是什么?在上面我们很容易就可以看到这个获取到activity中的WindowManager,但是真正的WindowManager是什么?我们回到actiivty.arrach()中去看看:

Activity

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



        mWindow = new PhoneWindow(this, window, activityConfigCallback);
       ...
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
       ...
    }

看到了context.getSystemService()方法,我们知道context是一个抽象的类,他的实现是在ContextImpl中,这里就不进去看了,我们也只要知道是获取到了一个WindowManager,WindowManager的实现类是WindowManagerImpl但是真正的操作都是在WindowManagerGlobl中,WindowManagerGlobl是一个单例的实现模式,至于为什么是这样,我觉得是应为WindowManagerGlobl中要管理好多的window的view的添加,所以这样就可以好管理一些.

WindowManagerGlobl

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

    ...

        ViewRootImpl root;

    ...
     root = new ViewRootImpl(view.getContext(), display);
      view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
         try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
}

这里我们可以看到最后他是通过讲我们初始化好的界面全都交给了ViewRootImpl去setView了意味着接下来我们对View的操作都是由ViewRootImpl这个类来负责的。我们下面去看看这个类的setView方法:

 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {

...

             requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }

        ...

}

在这个里面我们就可以看到了一个我们非常熟悉的方法requestLayout(),这个方法就是View绘制流程的入口,如果我们更进去就很容易可以看到我们的View就是从这里的DecorView开始一层一层的绘制,依次调用mesure,layout,draw方法来开始的。

但是我们也看到了一个重要的东西:mWindowSession,这个就是通过使用WindowSession和WindowManagerService之间的一个远程进程之间的一个IPC的通信过程,我将在Window底层源码分析(二)中进一步解释。

总结

通过上面的源码查看和分析,我们初步的了解到了:

  • 当我们一个Activity创建的时候,会创建一个PhoneWindow对象,(也就是初始化了一个DecorView)然后通过设置WindowManager给PhoneWindow绑定了一个单例的管理类WindowManagerGlobal。
  • 然后在Activity的生命周期执行到onResume的时候会调用一个WindowManagerGlobal这个管理类的addView的方法将decorView添加进ViewRootImpl的View管理类中
  • ViewRootImpl中就开始了View的各种一系列操作。

基本流程就是这样,用生活中的事例来说明这个机制的话,我觉得:WindowManagerGlobal就是boss他来管理着所有的Window,然后ViewRootImpl就是一个部门经理,他就负责View的一些事,PhoneWindow就是ViewRootImpl的好伙伴,他将ViewRootImpl管理不了的事(比如WindowManagerGlobal有一个命令下发了)然后处理后给ViewRootImpl。

你可能感兴趣的:(android)