一步步追踪Dialog的创建流程(一)

一直想搞明白Dialog到底是怎么创建的,今天有点时间,追踪一下Dialog的创建过程。由于能力有限,具体的实现细节不做分析,主要是搞懂代码调用的流程,从new Diaolg到Dialog显示的屏幕上的调用关系。以下便从Dialog的构造函数入手,开始一步步追踪Dialog的创建流程。

一.Dialog的构造函数

从构造函数出发,所有的构造函数都会调用下面这个构造函数:

   public Dialog(Context context, int theme) {
        this(context, theme, true);
    }

    Dialog(Context context, int theme, boolean createContextThemeWrapper) {
        if (createContextThemeWrapper) {
            if (theme == 0) {
                TypedValue outValue = new TypedValue();
                context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
                        outValue, true);
                theme = outValue.resourceId;
            }
            mContext = new ContextThemeWrapper(context, theme);
        } else {
            mContext = context;
        }

        mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
        Window w = PolicyManager.makeNewWindow(mContext);
        mWindow = w;
        w.setCallback(this);
        w.setOnWindowDismissedCallback(this);
        w.setWindowManager(mWindowManager, null, null);
        w.setGravity(Gravity.CENTER);
        mListenersHandler = new ListenersHandler(this);
    }
剔除不重要的,这里面只有两样东西特别重要,mWindowManger和mWindow。它们分别是窗口管理类和代表一个窗口的类。创建mWindow使用的是PolicyManager这个类,这个类在android api中并没有,是一个系统级别的类。所以想使用这个类创建一个window,然后自己去做一个Dialog是比较困难的。暂时不想这些,那么想想代表这个窗口的类(Window)是什么样的?mWindowManager又是怎么管理窗口的呢?带着问题,继续追踪代码。先看看mWindow到底是个什么东西。

二.Window和PhoneWindow

对于Window这个类,先看看它的注释:

/**
 * Abstract base class for a top-level window look and behavior policy.  An
 * instance of this class should be used as the top-level view added to the
 * window manager. It provides standard UI policies such as a background, title
 * area, default key processing, etc.
 *
 * 

The only existing implementation of this abstract class is * android.policy.PhoneWindow, which you should instantiate when needing a * Window. Eventually that class will be refactored and a factory method * added for creating Window instances without knowing about a particular * implementation. */


这段话的意思是说:Window类是一个抽象顶层窗口外观和行为策略的抽象类。这个类的实例应该作为定层的view加入到window manager中.Window提供了标准的UI策略,比如背景,标题区域,默认的按键处理等等。

这个类的唯一实例就是PhoneWindow,当我们需要一个Window时,PhoneWindow应该被实例化。最终,这个类将会被重构,并且加入一个工厂方法,用来创建一个window的实例,这样就遮蔽了创建这个类的具体细节。

接下来看看PhoneWindow,这个Window的唯一实现类,是怎样实现UI的外观绘制与事件处理的。

下面是它的构造函数:

    public PhoneWindow(Context context) {
        super(context);
        mLayoutInflater = LayoutInflater.from(context);
    }

构造函数获得了一个LayoutInflater的实例,这个实例用于构造一个view,因此可以猜测PhoneWindow中肯定会构造某个view.

看完了构造函数该看什么了呢?这个类有很多很多函数,总不能通通看一遍吧?这个时候,没有思路了还得回过头看Dialog类,看它是怎么把一个Dialog显示出来的。

三.show()方法

想想我们是怎么使用Dilog的,一般创建一个Dilog对象,设置一堆东西,不管设置什么,最终要调用show()方法,不然Dialog就不会显示,所以,秘密似乎在show()方法中。
那么,show方法都做了什么呢?
    /**
     * Start the dialog and display it on screen.  The window is placed in the
     * application layer and opaque.  Note that you should not override this
     * method to do initialization when the dialog is shown, instead implement
     * that in {@link #onStart}.
     */
    public void show() {
        if (mShowing) {
            if (mDecor != null) {
                if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
                    mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
                }
                mDecor.setVisibility(View.VISIBLE);
            }
            return;
        }

        mCanceled = false;
        
        if (!mCreated) {
            dispatchOnCreate(null);
        }

        onStart();
        mDecor = mWindow.getDecorView();

        if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
            final ApplicationInfo info = mContext.getApplicationInfo();
            mWindow.setDefaultIcon(info.icon);
            mWindow.setDefaultLogo(info.logo);
            mActionBar = new WindowDecorActionBar(this);
        }

        WindowManager.LayoutParams l = mWindow.getAttributes();
        if ((l.softInputMode
                & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
            WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
            nl.copyFrom(l);
            nl.softInputMode |=
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
            l = nl;
        }

        try {
            mWindowManager.addView(mDecor, l);
            mShowing = true;
    
            sendShowMessage();
        } finally {
        }
    }
代码比较多,抓住主干:1.从mWindow中获得一个DecorView对象。2.使用WindowManager的addView()方法添加DecorView。那么,这个DecorView又是什么呢?

四.DecorView

DecorView的定义如下:
    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
从DecorVeiw的定义上就可以看到,它继承了FrameLayout,也就是说它是一个View.第三步show()方法中,DecorView首次被WindowManager添加,是不是意味着DecorView就是Window的根View呢?应该是的,不然,还能有谁?DecorView这里不做过多的分析,毕竟,我想弄明白的是Dialog创建到显示出来的过程。

五.mWindowManager,addView(...)到底做了什么?

    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
可以看到它又调用了mGlobal的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;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent and we're running on L or above (or in the
            // system context), assume we want hardware acceleration.
            final Context context = view.getContext();
            if (context != null
                    && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
                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);
                    }
                }
            }

            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);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

这个方法比较复杂了,虽然代码很长,但是它做的事情并不复杂,它不管前面做什么,最后,它是吧view,root,和wparams都加入到对应的数据结构中了。view就是Dialog中传下来的DecorView, wparams就是我们传下来的另一个参数,这里又做了一下处理。
这三个数据结构分别为:
    private final ArrayList mViews = new ArrayList();
    private final ArrayList mRoots = new ArrayList();
    private final ArrayList mParams =
            new ArrayList();
这里有出现了另一个看似很重的东西,就是root,它是ViewRootImpl的实例。那么ViewRootImpl又是干什么的呢?先了解一下,然后后面用到它的时候再仔细分析。
root,view,mParams三个肯定有联系的,最后的
root.setView(view, wparams, panelParentView);
函数同时用到了这三个,这个函数可定是建立三者联系的。所以这个方法要着重分析。在分析这个方法之前,先对ViewRootImpl,WindowSession有个感性的认识。

六.ViewRootImpl
它的构造函数如下:
   public ViewRootImpl(Context context, Display display) {
        mContext = context;
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mDisplay = display;
        mBasePackageName = context.getBasePackageName();

        mDisplayAdjustments = display.getDisplayAdjustments();

        mThread = Thread.currentThread();
        mLocation = new WindowLeaked(null);
        mLocation.fillInStackTrace();
        mWidth = -1;
        mHeight = -1;
        mDirty = new Rect();
        mTempRect = new Rect();
        mVisRect = new Rect();
        mWinFrame = new Rect();
        mWindow = new W(this);
        mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
        mViewVisibility = View.GONE;
        mTransparentRegion = new Region();
        mPreviousTransparentRegion = new Region();
        mFirst = true; // true for the first time the view is added
        mAdded = false;
        mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
        mAccessibilityManager = AccessibilityManager.getInstance(context);
        mAccessibilityInteractionConnectionManager =
            new AccessibilityInteractionConnectionManager();
        mAccessibilityManager.addAccessibilityStateChangeListener(
                mAccessibilityInteractionConnectionManager);
        mHighContrastTextManager = new HighContrastTextManager();
        mAccessibilityManager.addHighTextContrastStateChangeListener(
                mHighContrastTextManager);
        mViewConfiguration = ViewConfiguration.get(context);
        mDensity = context.getResources().getDisplayMetrics().densityDpi;
        mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
        mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context);
        mChoreographer = Choreographer.getInstance();
        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
        loadSystemProperties();
        mWindowIsRound = context.getResources().getBoolean(
                com.android.internal.R.bool.config_windowIsRound);
    }

构造函数里面获得了一个IWindowSession的实例,这个类又是干什么的????

七.IWindowSession

IWindowSession是一个接口,它的实现类是Session,Session的定义如下:
final class Session extends IWindowSession.Stub
可以看到它是一个binder类,可定用来和Service通信的。就这样了解下就好了,不能丢了主干。

八.root.setView(view, wparams, panelParentView)

又回到这个方法,看看这个方法到底做了什么:
    /**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
		...
                // 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 {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                      getHostVisibility(), mDisplay.getDisplayId(),
                       mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, 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();这里对调用requestLayout做了说明:在加入到windowManager之前,第一调用layout.为了确保收到任何系统其它事件之前做relayout.之后使用了mWindowSession.addToDisplay方法。mWindowSession实际上是一个Session的实例,addToDisplay方法如下:
    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outInputChannel);
    }

可以看到这里调用了mService.addWindow,这里明显是一个进程通信了,mService是WindowManagerService的实例,也就是说真正window的添加实际上在WindowManagerService中完成。

九.临时的小结
夜已经深了,劳累了一天,倦意已经溢了出来,是睡觉的时候了,明天还有早起上班。暂且分析到这里了,通过这些追踪,至少可以得出以下结论:
1.DecorView是根View.
2.ViewRootImpl中的setView方法使得DecorView和ViewRootImpl紧密相关。
3.WindowManagerService中的addWindow最终实现了Window的添加。
目前应该追踪到了开始要绘制view树的地方了,具体怎么绘制的,应该还有很长的代码要追踪,这一部分就放到下一节来探索吧。
最后,这是我首次尝试分析这部分代码,分析的比较粗糙,也可能有些地方不对,希望大家指正出来。





你可能感兴趣的:(Android源码分析)