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 ...
随机推荐
- 《Vim实用技巧》第2版读书笔记
学习前的建议: 一.如果你需要一个功能强大的文本编辑器,速度飞快.使用方便.代码补全.编程强大.定制性强,Vim就是你需要的: 二.学习入门曲线陡峭,但是使用后期异常简单,效率超高: 三.号称可以跟上 ...
- ZOJ 2619: Generator
类型:概率 + 解方程组(高斯消元法) + KMP(好吧其实我用的是暴力~)题意:你可以等概率的选择大写字母里的前n个字母,在纸上写啊写,一直到出现给定的字符串.问写的字母个数的期望.思路: 期望递推 ...
- 使用iframe实现提交表单不刷新页面
正常情况下,当你向服务器发送数据的时候,你的浏览器将会打开action页面,并且不会重回到当前页面.但是有的时候,我们因为各种各样的要求,而不希望浏览器在提交数据的时候去刷新当前的页面或者转向到新的页 ...
- 使用KNN对iris数据集进行分类——python
filename='g:\data\iris.csv' lines=fr.readlines()Mat=zeros((len(lines),4))irisLabels=[]index=0for lin ...
- Tavas and Malekas
题面 题目描述 给你两个字符串a和b,告诉所有你b在a中一定匹配的位置,求有中不同的字符串a.a的长度为n,b的长度为m,一定匹配的位置有p个.若b在a中的一定匹配的位置为x,说明a[x-x+m-1] ...
- SQL-基础学习1--SELECT,LIMIT,DISTINCT,注释
所使用的数据库资料在:数据库资料 1.1 基础概念 1.数据库(database) 保存有组织的数据的容器(通常是一个文件或一组文件) 注意:常用的mysql,等是数据库管理系统DBMS:由这些软件创 ...
- DELPHI是怎么实现跨平台的?
DELPHI是怎么实现跨平台的? 首先跨平台必须要兼容原来的语法,以线程的临界区对象为例: TCriticalSection = class(TSynchroObject){$IFDEF POSIX} ...
- Android---简单的动画
- mysql 存储过程时间月份减法
declare startTime VARCHAR(19) default '2014-00-00 00:00:00'; declare tempTime VARCHAR(19) default NO ...
- SilverLight:布局(1) Border(边框)对象、Grid(网格)对象
ylbtech-SilverLight-Layout:布局(1) Border(边框)对象.Grid(网格)对象 A, Border(边框)对象 B, Grid(网格)对象 C, Grid(网格)对象 ...