FireMonkey 结构性初略分析
Delphi 下的FireMonkey,很好地实现了 DirectUI与跨平台。学习了解他,对DirectUI编程及项目的跨平台实现有一定帮助。
虽然作为开发者个体,并不需要了解太多这些东西,只要求拿来能用能实现功能就行,但对 FireMonkey的学习分析,对自己程序设计思想的提升,会有一定帮助。
昨天用FireMonkey控件写了一个小例子,发现他的 Animation类在实现控件的小动画时,很高效,很灵活。
初步印象是 FireMonkey的内核有很多值得学习的地方,尤其他的界面渲染上,可以深入了解。鉴于都有代码,了解只是时间上的问题。
今天才开始认真地看了下 FireMonkey的代码。跟踪查看了他的程序启动的各个步骤。先初略整理一下。在了解框架之后,再逐步深入了解其他各方面。
FireMonkey跨平台实现:
FM为了考虑跨平台,使用了很多服务接口类,来转嫁各种服务任务处理。
应用程序管理接口 | IFMXApplicationService |
系统字体接口 | IFMXSystemFontService |
窗体创建接口 | IFMXWindowService |
系统菜单接口 | IFMXMenuService |
计时器接口 | IFMXTimerService |
鼠标拖曳接口 | IFMXDragDropService |
粘贴板接口 | IFMXClipboardService |
鼠标形状接口 | IFMXCursorService |
鼠标消息接口 | IFMXMouseService |
系统桌面接口 | IFMXScreenService |
本地化与文本输出接口 | IFMXLocaleService, IFMXTextService |
上下文菜单显示接口 | IFMXContextService |
绘图与设备接口 | IFMXCanvasService, IFMXDeviceService |
界面外观与窗口外观接口 | IFMXStyleService, IFMXWindowBorderService |
其他接口 |
IFMXSystemInformationService, IFMXLoggingService |
看似这些接口很全很复杂。其实,所有接口都转到一个平台服务类来进行处理。这个平台服务类根据操作系统的不同分为 TPlatformWin, TPlatformIOS, TPlatformMac, TPlatformAndroid.
用接口转到这个类来处理,与直接用这类来处理有什么区别?
在调用服务接口类完成相应任务时,被调用的服务是一个公用的接口,与平台无关。所以程序中的所有对象都可以调用接口来实现相关服务任务。而不用考虑这些服务是怎样实现的。调用者只需提交指令。
然后服务接口会根据指令。并根据指定的平台,交由平台服务类去进行具体服务任务的完成。
当然,也可以让程序中所有对象直接调用服务平台类来实现任务。但是每个对象在发出任务指令时,都要先进行平台判断与指定。
每发出一次指令都指定一次平台,与通过通一的接口,让接口统一去指定平台。可想而知,哪个更科学,更方便。
当然完成这个接口体系,任务宏大。先要规划十几个服务接口的服务内容。然后再由平台去完成这些服务任务。而每个接口要负责的指令都得先归类规划好。
了解了跨平台,再来细说具体一个程序的运行流程。
程序启动流程分析:
FM的程序在启动时,会在装载单元文件FMX.Platform 时首先执行 RegisterCorePlatformServices 过程。来注册对应的平台服务类对象。
FM是根据宏定义(MSWINDOWS 或 IOS 或 ANDRODIO)来决定 uses 那个平台的 Services 单元。并执行这个单元里的RegisterCorePlatformServices。如Windows系统,uses的是 MX.Platform.Win 单元
RegisterCorePlatformServices 执行,首先创建的一个服务类对象TPlatformWin,这个类对象有N多个接口(前面所列的各种服务接口),再将这个TPlatformWin注册到各个接口服务中去。如: TPlatformServices.Current.AddPlatformService(IFMXMouseService, PlatformWin); 以便让程序中的对象(组件)调用相应接口时,能转回到平台服务对象来处理。
PlatformWin 在创建的时候,先向全局原子表添加一个字符串,(FM 就是用这个字符串标识来查找自己的 FORM 的)。同时创建一个Application类对象。来接管系统的消息和负责窗体的创建。
完成服务类 PlatformWin 与应用类 Application 的创建后。程序的初始化基本完成。(当然FM还执行了其他N多配套的初始化动作。如创建一个Screen对象,建立一个管理窗口的堆栈。来负责管理应用程序的所有窗体)。
初始化完成后,开始启动 Applicaion。进入程序的主体运行机制。启动步骤如下:
执行Application的Initialize。只是方便让开发者员加入程序初始化代码。
执行 Application.CreateForm, 创建第一个窗体 From。并指定为MainForm
执行 Application.Run; 进入消息循环。
总结:
程序启动
装入FMX.Platform
执行RegisterCorePlatformServices
创建平台服务类TPlatformWin
注册各种平台服务接口
创建应用类TApplication
Application. Initialize用户初始化
Application.CreateForm创建第一个窗体,并指定为MainForm
MainForm指定 ParneWnd时,创建 Application的影子窗体,
Application的影子窗体创建时,绑定消息处理过程 WndPro
Application.Run 进入消息循环
程序开始运行并处理消息。
TApplication分析
TApplication的Create并没太多的动作。只是创建了一个TIdleMessage和TApplicationFormFactor,这两个的具体作用以后再了解。
Application 的Initialize 也不执行任何动作。
Application 的CreateForm负责程序所有窗体的构建。Application 的 FCreateForms 类 来取得所创建的窗体的指针与Class类型, Instance 信息。并将创建的类保存一个TFormRegistry 堆栈。
Application 的创建窗体,只是创建了窗体的类对象 Form。而真正显示在桌面上我们能看到的窗体,是在 Form 创建时,通过Form 的CreateHandle 来创建完成。而CreateHandle事件又调用了窗体服务接口WinService 的CreateWindow。这个WinService,其实就是初始化过程中注册了的服务类 PlatformWin。真正执行创建窗体的动作,经过七转八拐最后交给了 PlatformWin 的 CreateWindow。(FM 的很多任务,都是通过这种接口转移来实现的)
PlatformWin的CreateWindow是个复杂的过程。他根据传来的Form类,设定WindowClass的各个参数。这中间还考虑到了窗体在Delphi的IDE环境下的一些设定。同时,他还判定窗体是普通窗体还是Popup型窗体。还会为窗体注册各种服务接口,如Menu,DropDrag等。
Form在创建实体窗口时。会指定他的ParneWnd为 ApplicationHWND; ApplicationHWND会调用 PlatformWin的CreateAppHandle来创建一个FMAppClass。这时,Application的真正的实体隐形窗口FMAppClass Window 就出生了。
FM的消息循环,正是绑定FMAppClass窗体的句柄以及消息过程 WndPro 来实现的。
Applicaion 会在创建第一个窗体时,将其指定为 MainForm。用户的交互操作,都是在 MainForm 上实现的。
Application的消息循环:
Application执行Run. 进入消息循环
Run 会调用 App服务接口 AppService。当 然AppService又指向了PlatformWin. 执行PlatformWin 的 Run.
PlatformWin.Run 中会执行 Application 的HandleMessage。当然又是通过AppService 回到执行 PlatformWin 的 HandleMessage。
PlatformWin 的HandleMessage 才开始正式的消息循环。在这里,我们能看到熟悉的 PeekMessage, TranslateMessage 和DispatchMessage。
HandleMessage 通过消息处理过程 WndPro. 来过滤和分发所有消息
当得到 WM_QUIT或 FMAppClass 的 WM_CLOSE 时,退出消息循环,终止程序。
总结:
Application创建 (没太多动作)
Application初始化 (没动作)
Application CreateForm 创建主窗体Form类
Form类执行 CreateHandle 创建窗口实体,将指定ParneWnd
Form指定 ParneWnd时,创建 Application的影子窗体,
创建Application的影子窗体,并绑定消息过程 WndPro
Application执行Run. 进入消息循环
Run执行 PlatformWin的HandleMessage
HandleMessage执行WndPro 过滤与分发消息。
程序进入正常的消息过程。
处理消息,实现交互操作。
得到 WM_QUIT或 FMAppClass 的 WM_CLOSE 时,退出消息循环,终止程序。
消息分发机制分析:
Screen对象会把Application CreatForm产生的Form全加入一个队列。
在消息循环中,根据消息句柄 hWnd 在Screen对象中找对应的Form。并将消息派发给Form。如果Form=nil,执行 Application的消息检测。侦测WM_CLOSEWM_DESTROY等消息,判断是否退出程序。
进行窗口消息过虑,响应 WM_LBUTTONDOWN,WM_LBUTTONUP,WM_MOUSEMOVE,WM_MOUSEWHEEL 等鼠标消息WM_CHAR, WM_KEYDOWN,WM_KEYUP等按键消息。
对不同的消息作不同的处理,主要是鼠标与按键类的消息分发。执行相应的 MouseDown, MouseUp, KeyDown, KeyUp 事件。
Form的鼠标消息处理:
Form有三个私有对象 FCaptured, FFocused,FHovered。
FCaptured:记录按下时的鼠标移所在位置的对象。
FHovered:记录正常(未按下时)的鼠标移所在位置的对象。
FFocused:记录键盘焦点对象。
在消息体制内,From 通过ObjectAtPoint来确定消息对象Obj。并将对应的消息分发给Obj. 如MouseUp,MouseDown,MouseMove等,先取得鼠标位置的Obj.然后根据情况是否派发消息给Obj。
MouseUp,MouseDown 事件会先侦测 Obj的Drag (拖曳事件) 执行BeginAutoDrag或终止 Drag。在没有Drag时,直接将消息分发给Obj。这时FCaptured 对象在处理Drag起到了关键作用。
Form在处理MouseMove时,会更新 Hovered。并触发原 Hovered的MouseLeave和新的 Hovered(即当前Obj)MouseEnter。这时再将MouseMove消息再派发给Obj。
CursorService 来负责鼠标形状的改变。
按键消息 KeyDown,KeyUp 是通过 Focused 派发。
Captured是在鼠标按下时取得的对象。全程将鼠标消息派发给 Captured, 以确保按下后鼠标拖动时与MouseUp, MouseLeave消息处理。
总结:
Run执行循环,并通过WndPro过滤,并分发消息
根据hWnd查找Form,将消息派发给Form
处理Form的消息,过滤消息,
处理FORM自身的消息,并响应相关事件。
派发相关消息 如鼠标,键盘等给Form里的Object
通过Captured,Hovered,Focused 三个对象来细化消息处理
让Object响应,并执行相应的事件。实现交互操作。
参考:http://my.oschina.net/isixth/blog/361097
FireMonkey 结构性初略分析的更多相关文章
- Easy51RTOS入门级初略分析
main.c #include "reg51.h" #include "os_cfg.h" #define TASK_DELAY0 TIME_PER_SEC/1 ...
- Hadoop架构的初略总结(1)
Hadoop架构的初略总结(1) Hadoop是一个开源的分布式系统基础架构,此架构可以帮助用户可以在不了解分布式底层细节的情况下开发分布式程序. 首先我们要理清楚几个问题. 1.我们为什么需要Had ...
- Hadoop架构的初略总结(2)
Hadoop架构的初略总结(2) 回顾一下前文,我们总结了以下几个方面.我们为什么需要Hadoop:Hadoop2.0生态系统的构成:Hadoop1.0中HDFS和MapReduce的结构模型. 我们 ...
- WindowsPhone-GameBoy模拟器开发五--使用XNA初略实现Gameboy显示系统
开篇前,最近弄了个空间,大家不嫌弃的话可以上去讨论讨论J http://www.lihengzhe.cn 这一次,就来简单地实现gameboy的实现机制.先说一下本次内容涉及到的技术,其实也就一项—X ...
- libsvm数据处理初略流程
- Spring简单的小例子SpringDemo,用于初略理解什么是Spring以及JavaBean的一些概念
一.开发前的准备 两个开发包spring-framework-3.1.1.RELEASE-with-docs.zip和commons-logging-1.2-bin.zip,将它们解压,然后把Spri ...
- Backbone案例的初略理解
版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明http://www.blogbus.com/monw3c-logs/217636180.html 先说一下Backbone的执行顺序: ...
- 初略 异步IO
import asyncio asyncio.coroutine() from concurrent.futures import ThreadPoolExecutor def task(): pri ...
- Java线程池ThreadPoolExecutor初略探索
在操作系统中,线程是一个非常重要的资源,频繁创建和销毁大量线程会大大降低系统性能.Java线程池原理类似于数据库连接池,目的就是帮助我们实现线程复用,减少频繁创建和销毁线程 ThreadPoolExe ...
随机推荐
- 在Ubuntu / Ubuntu Kylin下安装和卸载 Nodepadqq
在Ubuntu / Ubuntu Kylin下安装和卸载 Nodepadqq 对于Ubuntu发行版本可以通过PPA安装,命令如下: sudo add-apt-repository p ...
- ping++微信渠道,第二次拉起不能进行支付返回订单号重复问题
项目中用到了支付功能,采用的是ping++实现的,上线运行一年多都很正常,但是最近突然出现有买家反映说不能进行支付的情况 通过了解和沟通之后发现发现是重复拉起失败,然后我们对问题进行了排查. 测试过程 ...
- CSS-实现倒影效果box-reflect
我需要的效果: html: <img src="images/my1.jpg" width="20%"/> css: img{-webkit-b ...
- 洛谷——P2737 [USACO4.1]麦香牛块Beef McNuggets
https://www.luogu.org/problemnew/show/P2737 题目描述 农夫布朗的奶牛们正在进行斗争,因为它们听说麦当劳正在考虑引进一种新产品:麦香牛块.奶牛们正在想尽一切办 ...
- Netty学习_Netty框架入门教程:Netty入门之HelloWorld实现
我们可能都学过Socket通信/io/nio/aio等的编程.如果想把Socket真正的用于实际工作中去,那么还需要不断的完善.扩展和优化.比如很经典的Tcp读包写包问题,或者是数据接收的大小,实际的 ...
- abp ueditor 多图以及文件无法上传
abp .net core使用ueditor遇到的问题:多图和上传文件无法上传,提示“http://请求错误”. 400 bad request解决办法: 因为abp默认启用了ValidateAnti ...
- 邁向IT專家成功之路的三十則鐵律 鐵律二十三:IT人的成家之道-樸實
根據內政部一份2013年最新的調查報告指出台灣人的離婚率位居全球第三,想想看如果這是經濟成長率的排名表現那該有多好.然而究竟為何在台灣這塊小小的土地上,不僅離婚非常高而且晚婚的人也非常的多,其原因肯定 ...
- Unity -- 入门教程三
进入这个页面,按编译器版本进行下载,我用的是2010,所以要下载这个. 安装就不用我教了,下面开始看我是如何导入Unity VS的. 点击Import之后我们会发现并没有发生什么,但是接下来我们按一下 ...
- Direct2D教程(一)Direct2D已经来了,谁是GDI的终结者?
什么是Direct2D 一言以蔽之,就是Windows 7平台上的一个2D图形API,可以提供高性能,高质量的2D渲染.大多数人对Direct2D可能都比较陌生,以至于我之前在论坛上提到这个词的时候, ...
- Linux以下基于TCP多线程聊天室(server)
接上篇博文,本文是server端的实现,主要实现的功能,就是现实client的连接.转发client发送的消息.以及client掉线提示等功能,同一时候能够在这这上面扩展和TCP以及线程相关的功能木块 ...