一步步追踪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.** <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()方法
/*** 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
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.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
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中完成。
一步步追踪Dialog的创建流程(一)相关推荐
- Android系统(245)---SystemServer进程的创建流程
Android进程系列第三篇---SystemServer进程的创建流程 一.内容预览 SystemServer进程的启动.png 二.概述 前面进程系列已经更新了两篇,本文(基于Android O源 ...
- Android 9(P)应用进程创建流程大揭秘
Android 9 (P)应用进程创建流程大揭秘 Android 9 (P)系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 Android ...
- ue4小白人骨骼定义_UE4角色骨架创建流程_资源库
摘要:UE4角色骨架创建流程_资源库 使用UE4中的"动画索具"工具集可以创建初始骨架,下面为大家介绍如何使用UE4中的"动画索具"工具集制作出角色骨架吧! 初 ...
- spring系列-注解驱动原理及源码-spring容器创建流程
目录 一.spring容器的refresh()[创建刷新] 二.BeanFactory 的创建及预准备工作 1.prepareRefresh()刷新前预处理工作. 2.obtainFreshBeanF ...
- TObject简要说明-对象的创建流程
Delphi:TObject简要说明-对象的创建流程2009-08-14 08:57TObject = class //创建 constructor Create; //释放 procedure Fr ...
- camunda流程定义表无数据_创建流程实例时 act_ru_identitylink 表中没有出现相关的人员数据...
老师您好,我对流程实例有两个问题: 创建流程实例的方法,视频中给出的是 ProcessInstance processInstance = runtimeService.startProcessIns ...
- solr索引创建流程
solr索引创建流程: 分词组件Tokenizer 分词组件(Tokenizer)会做以下几件事情(这个过程称为:Tokenize),处理得到的结果是词汇单元(Token). 1.将文档分成一个一个单 ...
- 云原生kubernetes五 :pod创建流程
目录 1.pod创建流程 一.资源类型 二.创建资源配置清单 三.编写一个自定义配置清单
- mtd分区创建linux,浅析linux下mtd设备onenand存储器的分区和节点创建流程及yaffs2文件系统挂载...
浅析linux下mtd设备onenand存储器的分区和节点创建流程及yaffs2文件系统挂载 在arch/arm/mach-pxa/luther.c这个产品平台文件中,即: MACHINE_START ...
最新文章
- 《易学C++(第2版)》——1.4 C++能够做些什么
- 解决 GraphQL 的限流难题
- [JavaScript] 函数同名问题
- 让数据库支持SQL 2005 CLR 的必要条件
- centos软件软件包
- SSH2整合需要jar包解释
- rust这么拆除钢墙_rust腐蚀如何拆墙
- html 刷新div_HTML悬浮星星:
- 项目思考001---近期这个电台购物项目的一点点思考
- 从企金的授信方案延申到个金授信的思考
- 『优势特征知识蒸馏』在淘宝推荐中的应用
- 简单模拟QQ界面框架。
- QThread使用方法
- 【2021牛客暑期多校训练营9】E Eyjafjalla (倍增,dfs序,主席树)
- mysql 2008 教程_sql 2008 视频教程数据库从入门到精通自学视频教程_IT教程网
- iPhone开发:类似iChat的聊天泡泡
- 联想笔记本进入不了BIOS的解决方法
- 大势至USB端口管理软件网络版8.1注册破解
- Excel日期格式转为常规文本格式
- BZOJ_1778_[Usaco2010_Hol]_Dotp_驱逐猪猡_(期望动态规划+高斯消元+矩阵)
热门文章
- matlab用plot三点画圆_怎样用Matlab 过三个点画外接圆?
- ARM linux系统调用的实现原理
- CSS中div的边框
- 电脑计算机无法运行怎么办,如果计算机在打开电源后仍无法运行,则该怎么办?计算机无法进入系统的原因[图形]...
- 怎样退出计算机安全模式,如何退出?进入安全模式之后如何安全的退出啊 – 手机爱问...
- java计算机毕业设计合同管理MyBatis+系统+LW文档+源码+调试部署
- 安装报错:TypeError: _classify_installed_files() got an unexpected keyword argument ‘cmake_install_dir‘
- 开源PHP记事本,Boostnote 为程序员的开源式记事本
- 南加大计算机专业本科sat要求,南加州大学本科申请条件有哪些?
- 数仓工具—Hive集成篇之Kafka(03)