Android Window,View,Activity之间关系

在Android一个View是怎么展示在屏幕上?过程是什么样的呢?现提出问题再带着问题去查看源码。
Android所有展示的页面所有的View都被装载在Window中,Window是一个抽象类,它的唯一实现类是PhoneWindow,而Activity中有一个window的对象这点从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) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window);  //注释:1
        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);//注释:2
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }

如果对attach()方法不是很了解可以查询下Android Activity的启动流程
注释1:new 了一个PhoneWindow的对象,这样activity就拥有了window对象,
但是这样远远不够将View展示出来,因为这只是有了一个窗口真正的绘制工作并没有开始。
再看注释2:Activity中window调用了setWindowManager方法,WindowManager又是干什么用的呢?这里引用皇叔的博客中的一段话来下个定义:

WindowManager对Window进行管理,说到管理那就离不开对Window的添加、更新和删除的操作,在这里我们把它们统称为Window的操作。对于Window的操作,最终都是交由WMS来进行处理。

如果大家熟悉AMS的话对这种方式应该不会陌生,它们其实相似。接着查看setWindowManager,他是Window的一个方法,

/**
     * Set the window manager for use by this Window to, for example,
     * display panels.  This is not used for displaying the
     * Window itself -- that must be done by the client.
     *
     * @param wm The window manager for adding new windows.
     */
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }//注释3
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

看注释3的位置,先判断传入的WM是否为空,如果为空则再次执行(WindowManager)context.getSystemService(Context.WINDOW_SERVICE)
下面看getSystemService方法,这个方法是通过Binder机制来获取WMS,再看getSystemService返回的对象是什么?具体的实现是在ContextImpl中,

@Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }

SystemServiceRegistry又是个什么东东呢?看一下:

static{
...
registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher() {
            @Override
            public WindowManager createService(ContextImpl ctx) {
                return new WindowManagerImpl(ctx);
            }});
...
}

SystemServiceRegistry 的静态代码块中会调用多个registerService方法,这里只列举了和本文有关的一个。registerService方法会将传入的服务的名称存入到SYSTEM_SERVICE_NAMES中。从上面代码可以看出,传入的Context.WINDOW_SERVICE对应的就是WindowManagerImpl实例,因此得出结论,Context的getSystemService方法得到的是WindowManagerImpl实例。
在返回到注释3的位置的下一行代码调用了WindowManagerImpl对象的createLocalWindowManager方法,这个方法又是干嘛的呢?

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

这个方法很简单就是就是又一次创建了WindowManagerImpl对象,和通过之前那次不同的是这次传入了PhoneWindow对象,这时WindowManager开始真正和WIndow关联起来。
哪View是被怎么Add到Window上的呢?这里以最典型的应用程序窗口Activity为例,Activity在启动过程中,如果Activity所在的进程不存在则会创建新的进程,创建新的进程之后就会运行代表主线程的实例ActivityThread,不了解的请查看Android应用程序进程启动过程(前篇)这篇文章。ActivityThread管理着当前应用程序进程的线程,这在Activity的启动过程中运用的很明显,不了解的请查看Android深入四大组件(一)应用程序启动过程(后篇)这篇文章。当界面要与用户进行交互时,会调用ActivityThread的handleResumeActivity方法,如下所示。

...
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;
                    // Normally the ViewRoot sets up callbacks with the Activity
                    // in addView->ViewRootImpl#setView. If we are instead reusing
                    // the decor view we have to notify the view root that the
                    // callbacks may have changed.
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);//注释4
                }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
            } else if (!willBeVisible) {
                if (localLOGV) Slog.v(
                    TAG, "Launch " + r + " mStartedActivity set");
                r.hideForNow = true;
            }
...

注意注释4:调用了在注释3位置返回的WindowManagerImpl对象的addview方法,这个方法是ViewManager中的方法,ViewManager是一个接口,WindowManager是个接口同时又实现了ViewManager接口,而WindowManagerImpl又实现了WindowManager,这是只捋清楚这个方法的层级,清楚后放到一边继续查看WindowManagerImpl中的addview方法

@Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);//注释5
    }

看注释5:又调用了mGlobal的addview方法,如下

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
...
//熟悉的LayoutParams出来了
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
//根据父window调整当前window的尺寸
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
//查看系统属性的更改

            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }
            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }
//添加View的所有准备工作就绪开始添加
            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);//注释6
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

注释6:调用了ViewRootImpl中的setView方法:

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

主要就是调用了mWindowSession的addToDisplay方法。mWindowSession是IWindowSession类型的,它是一个Binder对象,用于进行进程间通信,IWindowSession是Client端的代理,它的Server端的实现为Session,此前包含ViewRootImpl在内的代码逻辑都是运行在本地进程的,而Session的addToDisplay方法则运行在WMS所在的进程。

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

addToDisplay方法中会调用了WMS的addWindow方法,并将自身也就是Session,作为参数传了进去,每个应用程序进程都会对应一个Session,WMS会用ArrayList来保存这些Session。这样剩下的工作就交给WMS来处理,在WMS中会为这个添加的窗口分配Surface,并确定窗口显示次序,可见负责显示界面的是画布Surface,而不是窗口本身。WMS会将它所管理的Surface交由SurfaceFlinger处理,SurfaceFlinger会将这些Surface混合并绘制到屏幕上。

结论:Activity持有Window的对象,View在Window上的增删等操作又是通过WindowManager来管理的,而WindowManager又是通过Binder机制获取到的WMS的映射,WMS把View真正显示到屏幕上。

部分源码线上链接https://www.androidos.net.cn/android/8.0.0_r4/xref/frameworks/base/core/java/android/app/ContextImpl.java
https://www.androidos.net.cn/android/8.0.0_r4/xref/frameworks/base/core/java/android/app/SystemServiceRegistry.java

你可能感兴趣的:(Android Window,View,Activity之间关系)