源码解析:dialog, popupwindow, 和activity 的第一个view是怎么来的?

问题

在慢慢熟悉android 的过程中,发现一个view 或者layout的初始化,或者构造的流程还是比较清楚的,也就是加到父控件中,然后就开始了对应的生命周期。但是整个界面的父控件,或者说系统的第一个view, 是怎么来的,如何初始化和绘制的呢?

概述

概述:带着困扰我的问题,在前文的基础上,继续分析应用界面和framework的关系,通过分析viewrootimpl 的来源,并结合dialog, popupwindow, 和activity 的 根view的创建流程,回答了问题界面的根view 或者第一个view 是如何初始化,并加入到framework 中的。

分析viewrootimpl 的来源

本文分析是接上篇《源码分析:LayoutParams的wrap_content, match_parent, 和具体值》,在上文中简要分析了windowmanager中对界面的处理。

使用各种搜索方法,可以看到,全部android代码中只有一处引用ViewRootImpl 这个类,那就是

android.view.WindowManagerImpl.addView(View, LayoutParams, CompatibilityInfoHolder, boolean)

通过分析代码可以看到,windowmanager在addview() 的过程中,为了管理添加进来的view,使用了三个数组

    private View[] mViews;
    private ViewRootImpl[] mRoots;
    private WindowManager.LayoutParams[] mParams;

在代码处理中,通过view 的context 来构造一个对应的ViewRootImpl ,然后把view, rootViewImpl, 和layoutParams 三个变量,存到数组中。并在最后,setView。

            root = new ViewRootImpl(view.getContext());
            root.mAddNesting = 1;
            if (cih == null) {
                root.mCompatibilityInfo = new CompatibilityInfoHolder();
            } else {
                root.mCompatibilityInfo = cih;
            }

            view.setLayoutParams(wparams);

            if (mViews == null) {
                index = 1;
                mViews = new View[1];
                mRoots = new ViewRootImpl[1];
                mParams = new WindowManager.LayoutParams[1];
            } else {
                index = mViews.length + 1;
                Object[] old = mViews;
                mViews = new View[index];
                System.arraycopy(old, 0, mViews, 0, index-1);
                old = mRoots;
                mRoots = new ViewRootImpl[index];
                System.arraycopy(old, 0, mRoots, 0, index-1);
                old = mParams;
                mParams = new WindowManager.LayoutParams[index];
                System.arraycopy(old, 0, mParams, 0, index-1);
            }
            index--;

            mViews[index] = view;
            mRoots[index] = root;
            mParams[index] = wparams;
        // do this last because it fires off messages to start doing things
        root.setView(view, wparams, panelParentView);

在setview中,就是前文measure 过程中提出的问题, view是哪里来的-参见-《尽量理解xml 中LayoutParams的wrap_content, match_parent, 和具体值

好,现在知道了,rootview ,最终处理的view就是就是从windowmanager 中add进来的。那么顺藤摸瓜,看看到底哪里调用了WindowManagerImpl.addView()

ViewRootImpl(Context) - android.view.ViewRootImpl
	addView(View, LayoutParams, CompatibilityInfoHolder, boolean) : void - android.view.WindowManagerImpl
		addView(View, LayoutParams, CompatibilityInfoHolder) : void - android.view.WindowManagerImpl
		addView(View, LayoutParams) : void - android.view.WindowManagerImpl
			addIntruderView() : void - com.android.systemui.statusbar.phone.PhoneStatusBar
			addNavigationBar() : void - com.android.systemui.statusbar.phone.PhoneStatusBar
			addPanelWindows() : void - com.android.systemui.statusbar.tablet.TabletStatusBar (5 matches)
			addStartingWindow(IBinder, String, int, CompatibilityInfo, CharSequence, int, int, int) : View - com.android.internal.policy.impl.PhoneWindowManager
			addView(View) : void - android.view.WindowManagerImpl
			advance() : void - com.android.systemui.statusbar.tablet.TabletTicker
			handleResumeActivity(IBinder, boolean, boolean) : void - android.app.ActivityThread
			handleShow() : void - android.widget.Toast.TN
			invokePopup(LayoutParams) : void - android.widget.PopupWindow
			makeVisible() : void - android.app.Activity
			onBarViewAttached() : void - com.android.systemui.statusbar.phone.PhoneStatusBar
			onCreate() : void - com.android.systemui.LoadAverageService
			openPanel(PanelFeatureState, KeyEvent) : void - com.android.internal.policy.impl.PhoneWindow
			setVisible(boolean) : void - android.widget.ZoomButtonsController
			show() : void - android.app.Dialog
			show() : void - com.android.internal.policy.impl.KeyguardViewManager
			show(int) : void - android.widget.MediaController
			showCompatibilityHelp() : void - com.android.systemui.statusbar.tablet.TabletStatusBar
			showSafeModeOverlay() : void - com.android.server.am.ActivityManagerService
			start() : void - com.android.systemui.statusbar.StatusBar
			startAnimation(Runnable, int, int, boolean, boolean) : void - com.android.systemui.screenshot.GlobalScreenshot
			updateRecentsPanel() : void - com.android.systemui.statusbar.phone.PhoneStatusBar
			updateSettings() : void - com.android.internal.policy.impl.PhoneWindowManager

通过查看调用,可以看到很多地方都有调用。大概过一下,就能发现很多熟悉的东西,比如PhoneStatusBar,ActivityThread,PhoneWindow,PopupWindow,Activity,Toast,Dialog 等等。那这里感觉就比较明显了,这些熟悉的控件和类,就是通过windowmanager ,来把自己的view和界面加到系统中了。

分析Dialog 是如何加入到windowmanager 的

柿子先捡软的捏, 挑个简单的先。看下show() : void - android.app.Dialog

这不就是经常调用的大名鼎鼎的show() 嘛。 dialog写完最后调用的show() 方法,没错就是它。从这个控件来看确实就是这样通过windowmanager, show 一个dialog,就是windowmanager.addview一下啦。 那么addview的时候,自然就应该是dialog的根view,父控件喽。查看一下代码验证一下我们的猜测。

    /**
     * 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)) {
            mActionBar = new ActionBarImpl(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 {
        }
    }

被add进 windowmanager中的是这个mDecor, 来自mWindow.getDecorView();

    /**
     * Retrieve the top-level window decor view (containing the standard
     * window frame/decorations and the client's content inside of that), which
     * can be added as a window to the window manager.
     *
     * <p><em>Note that calling this function for the first time "locks in"
     * various window characteristics as described in
     * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}.</em></p>
     *
     * @return Returns the top-level window decor view.
     */
    public abstract View getDecorView();

从注释中的描述,确实是 top-level window decor view。 印证了前面的猜测。

接下来看下这个mDecor到底是什么。

找到具体的实现com.android.internal.policy.impl.PhoneWindow.getDecorView()

    @Override
    public final View getDecorView() {
        if (mDecor == null) {
            installDecor();
        }
        return mDecor;
    }

调用了installDecor()

    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
        }
<span style="white-space:pre">	</span>//......
    }

继续generateDecor();

    protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }
        public DecorView(Context context, int featureId) {
            super(context);
            mFeatureId = featureId;
        }
        /** The feature ID of the panel, or -1 if this is the application's DecorView */
        private final int mFeatureId;
    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker

最后,从类定义中看到,DecorView 是一个 FrameLayout 并实现了RootViewSurfaceTaker

所以,回顾一下,这个 top-level window decor view,被add 进windowmanager 中的就是一个framelayout。

这点通过 hierarchyviewer 分析 UI 时得到印证。

如何使用,请移步官方文档《Using Hierarchy Viewer》,已经有很多人写了中文文档,不再赘述。

好了,到这里知道了系统dialog 中对应的 顶层 view 是一个framelayout, 对应到framework中 viewrootimpl 中的rootview。

同理,PopupWindow 是怎么干的

然后举一反三,看下PopupWindow 是怎么搞的。

invokePopup(LayoutParams) : void - android.widget.PopupWindow
	showAsDropDown(View, int, int) : void - android.widget.PopupWindow
	showAtLocation(IBinder, int, int, int) : void - android.widget.PopupWindow

这个接口showAsDropDown就是PopupWindow 的api嘛,

这里简单的列出关键代码,就不一一分析了。

        mWindowManager.addView(mPopupView, p);
        if (mBackground != null) {
            final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
            int height = ViewGroup.LayoutParams.MATCH_PARENT;
            if (layoutParams != null &&
                    layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                height = ViewGroup.LayoutParams.WRAP_CONTENT;
            }

            // when a background is available, we embed the content view
            // within another view that owns the background drawable
            PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
            PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, height
            );
            popupViewContainer.setBackgroundDrawable(mBackground);
            popupViewContainer.addView(mContentView, listParams);

            mPopupView = popupViewContainer;
        } else {
            mPopupView = mContentView;
    public void setContentView(View contentView) {
        if (isShowing()) {
            return;
        }

        mContentView = contentView;

        if (mContext == null && mContentView != null) {
            mContext = mContentView.getContext();
        }

        if (mWindowManager == null && mContentView != null) {
            mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        }
    }

所以PopupWindow  是把setContentView 的view  加入到了windowmanager 中。

接下来是activity

那最常用的activity 中又是怎样的呢?

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

这里的mDecor 就不像前面的dialog 和popupWindow 中那么容易分析了。

首先扫了一下activity 这个类,发现没有赋值语句, 查看一下基类,也没有。最后查看所有mDecor 的调用。

mDecor - android.app.Activity
	dispatchKeyEvent(KeyEvent) : boolean - android.app.Activity
	handleDestroyActivity(IBinder, boolean, int, boolean) : void - android.app.ActivityThread (2 matches)
	handleResumeActivity(IBinder, boolean, boolean) : void - android.app.ActivityThread (2 matches)
	handleSendResult(ResultData) : void - android.app.ActivityThread
	handleWindowVisibility(IBinder, boolean) : void - android.app.ActivityThread
	makeVisible() : void - android.app.Activity (2 matches)
	onWindowAttributesChanged(LayoutParams) : void - android.app.Activity
	setVisible(boolean) : void - android.app.Activity
	updateVisibility(ActivityClientRecord, boolean) : void - android.app.ActivityThread

逐个查看,最后仅在handleResumeActivity() : void - android.app.ActivityThread 中 找到相关赋值。

    final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();

        ActivityClientRecord r = performResumeActivity(token, clearHide);

        if (r != null) {
            final Activity a = r.activity;

            if (localLOGV) Slog.v(
                TAG, "Resume " + r + " started activity: " +
                a.mStartedActivity + ", hideForNow: " + r.hideForNow
                + ", finished: " + a.mFinished);

            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

            // If the window hasn't yet been added to the window manager,
            // and this guy didn't finish itself or start another activity,
            // then go ahead and add the window.
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                }
            }
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                <span style="color:#ff0000;">View decor = r.window.getDecorView();</span>
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                <span style="color:#ff0000;">a.mDecor = decor;</span>
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }
<span style="white-space:pre">		</span>//......

    }

这里代码涉及较多内容,集中注意力,看decorView ,注意到decor = r.window.getDecorView();。

而ActivityClientRecord r = performResumeActivity(token, clearHide);

也就是说是具体resume 的activity 的 window 的decorView()。

通过前面dialog 部分的分析,我们知道使用phoneWindow 的话,那就是framelayout 的那个 decorView。而目前还没有其他类型的window,这从window的继承关系可以看出。

在这里我们看到,activity 在resume的时候,通过判断是否要visible ,来把activity 的window 的decorView 加到windowmanager中,那么在activity的生命周期就应该在对应的状态中从windowmanager中移除该decorView。

在前面mDecor 的调用中,确实发现了在android.app.ActivityThread.handleDestroyActivity(IBinder, boolean, int, boolean),中。

    private void handleDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance) {
        ActivityClientRecord r = performDestroyActivity(token, finishing,
                configChanges, getNonConfigInstance);
        if (r != null) {
            cleanUpPendingRemoveWindows(r);
            WindowManager wm = r.activity.getWindowManager();
            <span style="color:#ff0000;">View v = r.activity.mDecor;</span>
            if (v != null) {
                if (r.activity.mVisibleFromServer) {
                    mNumVisibleActivities--;
                }
                IBinder wtoken = v.getWindowToken();
                if (r.activity.mWindowAdded) {
                    if (r.onlyLocalRequest) {
                        // Hold off on removing this until the new activity's
                        // window is being added.
                        r.mPendingRemoveWindow = v;
                        r.mPendingRemoveWindowManager = wm;
                    } else {
                       <span style="color:#ff0000;"> wm.removeViewImmediate(v);</span>
                    }
                }
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>//......

    }

至此,通过看了dialog, popupwindow, 和activity 中的部分源码, 知道了普通界面(dialog, popupwindow, 和activity )的第一个view, 是怎么来的,是如何加入到系统中的,也就是windowManager。

一句话结论

应用层面的界面都是通过windowmanager 加入到framework 中的,ViewRootImpl 是framework对view 的抽象, 界面管理的根节点。

留的尾巴

弄清楚第一个的问题后,接下来争取比较完整地回顾一下view 的创建,绘制,layout过程。to be continued...

在分析过程中,简单的接触到了framework中处理activity的部分流程,handleResumeActivity(),handleDestroyActivity()。另一个问题就是系统framework 是如何管理activity的生命周期的。//TODO

弄清楚第一个的问题后,接下来争取比较完整地回顾一下view 的创建,绘制,layout过程。to be continued...

在分析过程中,简单的接触到了framework中处理activity的部分流程,handleResumeActivity(),handleDestroyActivity()。另一个问题就是系统framework 是如何管理activity的生命周期的。//TODO

源码解析:dialog, popupwindow, 和activity 的第一个view是怎么来的?,布布扣,bubuko.com

时间: 2024-10-11 05:48:19

源码解析:dialog, popupwindow, 和activity 的第一个view是怎么来的?的相关文章

android源码解析(十七)--&gt;Activity布局加载流程

好吧,终于要开始讲讲Activity的布局加载流程了,大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与android中另外一个很重要的组件Service最大的不同,但是这个展示的界面的功能是Activity直接控制的么?界面的布局文件是如何加载到内存并被Activity管理的?android中的View是一个怎样的概念?加载到内存中的布局文件是如何绘制出来的? 要想回答这些问题,我们就需要对android的界面加载与绘制流程有所了解,这里我们先来学习一下Act

Java集合干货系列-(一)ArrayList源码解析

前言 今天来介绍下ArrayList,在集合框架整体框架一章中,我们介绍了List接口,ArrayList继承了AbstractList,实现了List.ArrayList在工作中经常用到,所以要弄懂这个类是极其重要的.构造图如下:蓝色线条:继承绿色线条:接口实现 正文 ArrayList简介 ArrayList定义 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomA

第四章 CopyOnWriteArraySet源码解析

注:在看这篇文章之前,如果对CopyOnWriteArrayList底层不清楚的话,建议先去看看CopyOnWriteArrayList源码解析. http://www.cnblogs.com/java-zhao/p/5121944.html 1.对于CopyOnWriteArraySet需要掌握以下几点 创建:CopyOnWriteArraySet() 添加元素:即add(E)方法 删除对象:即remove(E)方法 遍历所有对象:即iterator(),在实际中更常用的是增强型的for循环去

Dialog与FragmentDialog源码解析

<代码里的世界> -UI篇 用文字札记描绘自己 android学习之路 转载请保留出处 by Qiao http://blog.csdn.net/qiaoidea/article/details/46402845 [导航] - 弹出式对话框各种方案 从仿QQ消息提示框来谈弹出式对话框的实现方式 (Dialog,PopupWind,自定义View,Activity,FragmentDialog) - Dialog源码解析 从源码上看Dialog与DialogFragment 1.概述 前一篇写了

android源码解析之(十五)--&gt;Activity销毁流程

继续我们的源码解析,上一篇文章我们介绍了Activity的启动流程,一个典型的场景就是Activity a 启动了一个Activity b,他们的生命周期回调方法是: onPause(a) –> onCreate(b) –> onStart(b) –> onResume(b) –> onStop(a) 而我们根据源码也验证了这样的生命周期调用序列,那么Activity的销毁流程呢?它的生命周期的调用顺序又是这样的呢? 这里我们我做一个简单的demo,让一个Activity a启动A

【Android应用开发】EasyDialog 源码解析

示例源码下载 : EasyDialog 简介 : -- 作用 : 用于在界面进行一些介绍, 说明; -- 效果图 : 一. EasyDialog 源码解析 1. 实现原理 实现原理 : -- EasyDialog 效果 : 在点击后, 会从屏幕外飞入对话框, 飞入恰好能够正好处于特定 View 组件的上方 或者下方; -- 本质 : 点击按钮弹出的对话框会填充整个屏幕, 背景设置成透明的, 然后会计算组件坐标, 记录坐标位置, 再在弹出的整个对话框中 绘制一个 带小三角对话框的布局, 并让其执行

android源码解析(二十二)--&gt;Toast加载绘制流程

前面我们分析了Activity.Dialog.PopupWindow的加载绘制流程,相信大家对整个Android系统中的窗口绘制流程已经有了一个比较清晰的认识了,这里最后再给大家介绍一下Toast的加载绘制流程. 其实Toast窗口和Activity.Dialog.PopupWindow有一个不太一样的地方,就是Toast窗口是属于系统级别的窗口,他和输入框等类似的,不属于某一个应用,即不属于某一个进程,所以自然而然的,一旦涉及到Toast的加载绘制流程就会涉及到进程间通讯,看过前面系列文章的同

Android EventBus3.0使用及源码解析

叨了个叨 最近因为换工作的一些琐事搞的我一个头两个大,也没怎么去学新东西,实在是有些愧疚.新项目用到了EventBus3.0,原来只是听说EventBus的鼎鼎大名,一直没仔细研究过.趁着周末有些时间,研究下代码,也算没有虚度光阴. EventBus GitHub : https://github.com/greenrobot/EventBus EventBus3.0简介 EventBus是greenrobot出品的一个用于Android中事件发布/订阅的库.以前传递对象可能通过接口.广播.文件

Volley 源码解析(转)

项目:Volley,分析者:grumoon,校对者:Trinea 本文为 Android 开源项目源码解析 中 Volley 部分项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo分析者:grumoon,校对者:huxian99.Trinea,校对状态:完成 1. 功能介绍 1.1. Volley Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架.在 Google I/O 2013 大会上发布. 名字由来:a burs