一直想搞明白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.** <p>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(...)到底做了什么?

    @Overridepublic 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 thingstry {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<View> mViews = new ArrayList<View>();private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();private final ArrayList<WindowManager.LayoutParams> mParams =new ArrayList<WindowManager.LayoutParams>();

这里有出现了另一个看似很重的东西,就是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 addedmAdded = 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;
<span style="white-space:pre">     </span>...// 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();
<span style="white-space:pre">       </span>...
<span style="white-space:pre">       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();}}</span>}}}

这个函数很长,主要的步骤是调用了requestLayout();这里对调用requestLayout做了说明:在加入到windowManager之前,第一调用layout.为了确保收到任何系统其它事件之前做relayout.之后使用了mWindowSession.addToDisplay方法。mWindowSession实际上是一个Session的实例,addToDisplay方法如下:

    @Overridepublic 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树的地方了,具体怎么绘制的,应该还有很长的代码要追踪,这一部分就放到下一节来探索吧。
最后,这是我首次尝试分析这部分代码,分析的比较粗糙,也可能有些地方不对,希望大家指正出来。

一步步追踪Dialog的创建流程(一)相关推荐

  1. Android系统(245)---SystemServer进程的创建流程

    Android进程系列第三篇---SystemServer进程的创建流程 一.内容预览 SystemServer进程的启动.png 二.概述 前面进程系列已经更新了两篇,本文(基于Android O源 ...

  2. Android 9(P)应用进程创建流程大揭秘

           Android 9 (P)应用进程创建流程大揭秘 Android 9 (P)系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 Android ...

  3. ue4小白人骨骼定义_UE4角色骨架创建流程_资源库

    摘要:UE4角色骨架创建流程_资源库 使用UE4中的"动画索具"工具集可以创建初始骨架,下面为大家介绍如何使用UE4中的"动画索具"工具集制作出角色骨架吧! 初 ...

  4. spring系列-注解驱动原理及源码-spring容器创建流程

    目录 一.spring容器的refresh()[创建刷新] 二.BeanFactory 的创建及预准备工作 1.prepareRefresh()刷新前预处理工作. 2.obtainFreshBeanF ...

  5. TObject简要说明-对象的创建流程

    Delphi:TObject简要说明-对象的创建流程2009-08-14 08:57TObject = class //创建 constructor Create; //释放 procedure Fr ...

  6. camunda流程定义表无数据_创建流程实例时 act_ru_identitylink 表中没有出现相关的人员数据...

    老师您好,我对流程实例有两个问题: 创建流程实例的方法,视频中给出的是 ProcessInstance processInstance = runtimeService.startProcessIns ...

  7. solr索引创建流程

    solr索引创建流程: 分词组件Tokenizer 分词组件(Tokenizer)会做以下几件事情(这个过程称为:Tokenize),处理得到的结果是词汇单元(Token). 1.将文档分成一个一个单 ...

  8. 云原生kubernetes五 :pod创建流程

    目录 1.pod创建流程 一.资源类型 二.创建资源配置清单 三.编写一个自定义配置清单

  9. mtd分区创建linux,浅析linux下mtd设备onenand存储器的分区和节点创建流程及yaffs2文件系统挂载...

    浅析linux下mtd设备onenand存储器的分区和节点创建流程及yaffs2文件系统挂载 在arch/arm/mach-pxa/luther.c这个产品平台文件中,即: MACHINE_START ...

最新文章

  1. 《易学C++(第2版)》——1.4 C++能够做些什么
  2. 解决 GraphQL 的限流难题
  3. [JavaScript] 函数同名问题
  4. 让数据库支持SQL 2005 CLR 的必要条件
  5. centos软件软件包
  6. SSH2整合需要jar包解释
  7. rust这么拆除钢墙_rust腐蚀如何拆墙
  8. html 刷新div_HTML悬浮星星:
  9. 项目思考001---近期这个电台购物项目的一点点思考
  10. 从企金的授信方案延申到个金授信的思考
  11. 『优势特征知识蒸馏』在淘宝推荐中的应用
  12. 简单模拟QQ界面框架。
  13. QThread使用方法
  14. 【2021牛客暑期多校训练营9】E Eyjafjalla (倍增,dfs序,主席树)
  15. mysql 2008 教程_sql 2008 视频教程数据库从入门到精通自学视频教程_IT教程网
  16. iPhone开发:类似iChat的聊天泡泡
  17. 联想笔记本进入不了BIOS的解决方法
  18. 大势至USB端口管理软件网络版8.1注册破解
  19. Excel日期格式转为常规文本格式
  20. BZOJ_1778_[Usaco2010_Hol]_Dotp_驱逐猪猡_(期望动态规划+高斯消元+矩阵)

热门文章

  1. matlab用plot三点画圆_怎样用Matlab 过三个点画外接圆?
  2. ARM linux系统调用的实现原理
  3. CSS中div的边框
  4. 电脑计算机无法运行怎么办,如果计算机在打开电源后仍无法运行,则该怎么办?计算机无法进入系统的原因[图形]...
  5. 怎样退出计算机安全模式,如何退出?进入安全模式之后如何安全的退出啊 – 手机爱问...
  6. java计算机毕业设计合同管理MyBatis+系统+LW文档+源码+调试部署
  7. 安装报错:TypeError: _classify_installed_files() got an unexpected keyword argument ‘cmake_install_dir‘
  8. 开源PHP记事本,Boostnote 为程序员的开源式记事本
  9. 南加大计算机专业本科sat要求,南加州大学本科申请条件有哪些?
  10. 数仓工具—Hive集成篇之Kafka(03)