一般来说,window manager 是应用的一部分,会在图形用户界面中控制窗口的放置和外观。本文会对Firefox OS是如何进行窗口管理的内容进行探究。
Firefox OS中, Window Management是System app的一部分,主要负责:
- App的生命周期以及app之间的交互。
- UI 元素的布局,调整,显示及动画/切换
- 全系统的UI逻辑,包括 web activities, app 消息, 以及任务管理(task manager)
- App-指定的UI特性如弹出框,情景菜单和错误页面。
在深入理解上述条目的细节前,让我们来看下应用是如何在Gaia内部启动的。
App在Gaia中的启动
app在 Firefox OS 中的启动有几种方式,例如,通过其他app的系统消息,或者在HomeScreen中点击app图标等。
控制app开启的事件是由 Gecko引擎和 System App 处理的,下面会有详细阐述。
App 结构
所有的 Gaia webapps 都是 packaged apps,它们都是基本的zip文件,其中会包含所有的应用程序文件: HTML, CSS, JavaScript, images, manifest, 等等。每个在 Gaia 中的 web 应用都会以下面 的基本结构进行组织:
apps
/
[
app
name
]
/
-
js
-
styles
-
locales
-
test
-
index
.
html
-
manifest
.
webapp
当一个内嵌的Gaia app 从 homescreen 启动时,Gecko 会试图打开 URL 地址 manifest://[app name].gaiamobile.org:8080, 解析对应的
manifest.webapp
文件,之后运行文件中 launch_path
指定的文件 — 在所有内嵌 webapp中都是 index.html
。index.html
文件会加载所有需要的样式和 JavaScript。
注意 : 此处有一个非正式的约定,Gaia app的主要 js 入口点一般是 [app name].js
或 main.js
.
App启动次序
启动事件会发给Gecko。一旦 Gecko 准备就绪, system/js/app_window_factory.js 文件中的 AppwindowFactory
就会收到一个 app的 webapps-launch
事件或一个用于处理挂起消息的 open-app
事件。
window.addEventListener('applicationready', function appReady(e) { window.removeEventListener('applicationready', appReady); window.addEventListener('webapps-launch', self); window.addEventListener('webapps-close', self); window.addEventListener('open-app', self); });
关于 handleEvent 动作的细节, this.launch(config)
会启动一个 app window 或 activity。一旦app关闭, Appwindow
会收到 webapps-close
事件。
主进程中的 launch()
方法如下:
var app = AppWindowManager.getApp(config.origin); if (app) { app.reviveBrowser(); } else if (config.origin !== homescreenLauncher.origin) { new AppWindow(config); } else if (config.origin == homescreenLauncher.origin) { homescreenLauncher.getHomescreen().ensure(); }
首先,会检查 app 变量是否存在并试图到Gecko 中恢复app的运行。否则,如果是常规的app,我们会创建一个关于 app 的 AppWindow
实例。另一个特殊的情况是 homescreenLauncher
— 此时我们会执行一些必要的操作。
AppWindow
Firefox OS 会使用专门的 mozBrowser API 使 web 页面看起来像一个 app。 窗口管理的核心只是使用 mozBrowser
API 封装来处理 内部的 iFrames (窗口)。之所以创建这个特殊的 moz-browser
类型,主要是为了使iFrame 看起来更像一个真实的浏览器窗口。
AppWindow
可以创建,容纳以及管理 mozBrowser
iFrame。AppWindow
可以操控所有由 mozBrowser
iFrame 本身触发的 mozBrowser
事件并且还可以显示相关的UI特性。
App 生命周期管理
app整个的生命周期如下:
- App launch
- 将 iframe 添加到 System DOM tree中
- Start App 开启动画
- App opened
- App close 动画
- App closed
- 从 DOM tree 中移除 iframe
- App terminated
启动 app
当用户点击 homescreen 上的应用图标时,homescreen 会使用 mozApps API 来通知 Gecko 引擎打开对应的app。当Gecko 准备就绪时,它会发送一个适当的事件给 system app。
Killing apps
在下面情况下,App 将会被 kill 掉:
- app crashes
- OOM killer kills it
- app 由任务管理器关闭
window.close()
被调用
对于活动的应用程序而言,在关闭动画后,被 kill app 的 iFrame 就会从 DOM tree中移除。 对于不活动的应用程序,在它们被kill 后, 对应的 iframe 就会立刻移除。
在下面情况下,Apps 会被中断:
- 对于web activities来说: 当activity 调用者被打开时
- 弹出框 (Popups): 当 window.open 调用者被打开时
- App: 不做任何事情时
重启 app
在下面情况下, Apps 会被重启:
- homescreen app: 当按下 home 键时
- 如果是从任务管理器打开或通过边缘手势滑动(正在实验中的功能),Zombie apps 会通过同样的URL复活,
app是如何被渲染的
在启动一个app时,屏幕会分为下面几部分:
- System header
- App iframe
- 底部包装栏 (如果在浏览器框架模式下)
App 布局
应用 iframe 的主要容器如下:
<iframe id="browser2" mozallowfullscreen="true" mozbrowser="true" remote="true"... ... src="", data-url="" data-frame-type="window" data-frame-origin="..."> </iframe>
iframe 中包括:
- 启动路径(
data-url
,data-frame-origin
) - mozbrowser iframe 属性 (
mozallowfullscreen="true"
,mozbrowser="true"
) - 容器,覆盖视窗,app指定UI
调整 AppWindow大小
AppWindow 会在下面几种情况下被调整:
- system app 只有在转屏时才会调整大小。
- 对于一般的 app, 在下列情况下会调整大小:
- system app 调整大小
- keyboard 开启/关闭 动画结束时
- 状态栏变化时
window.resizedBy()
或window.resizeTo()
被调用时- software home 按钮被选中时
总之,屏幕的大小会受到下面要素的影响:
- Orientation 状态
- Keyboard 状态
- AttentionScreen 状态 (被呼叫,有短信等)
- Chrome 导航状态
- 全屏状态
manifest.fullscreen
/parentWindow
- Software homebutton 状态
AppWindow 方向
app 的方向可以由每个单独的app或由系统全局控制。您可以在 manifest.webapp
中使用 orientation
属性来设置方向。例如
"orientation": "default",
您也可以使用orientation API 来锁定和解锁方向:
screen.mozLockOrientation([‘portrait-primary’]); screen.mozUnlockOrientation();
可以用下面的属性值来强制对方向进行设定:
default
: 系统默认方向portrait
: 强制屏幕竖直方向显示landscape
: 强制屏幕横向显示
要获取更多的细节,请参考 Screen.lockOrientation , 您也可以在 gaia/test_apps/uitest/js/API/orientation.js. 查看设定的实例。
App 可见性
只有当关掉屏幕时, System app 才会到后台;而正常的 app 到后台要依赖下面几个因素:
- Audio competing
- Process policy
- Rendering
注意: 当父 iframe 页面非活动时,页面可见性会被继承。
在下面情况下,APP通常会在前台运行:
- 启动动画开始时
- 滑动(Swipe-in)动画结束时
- 锁屏界面被解锁时
下面情况下,App 通常会在后台运行:
- 关闭动画结束时
- 呼叫屏幕(callscreen)显示3秒之后
- 屏幕关闭时
对上面的规则而言,有一些意外的情况:
- 在正常渠道下,伴有音频播放的Active app
- Apps 调用内嵌的 web activities
- Apps 打开
window.open('', '', 'dialog')
注意: 此处的 Active app 是指当前正在前台运行的应用;而 inactive app是指挡在后台运行的应用(界面不可见)。
AppWindow 动画和过渡
为了让用户获得更顺畅的使用体验,Gaia 的窗口管理器(Window Manager)也会提供 app window 动画和过渡效果。
AppWindow 动画和过渡效果是由下面的状态来管理的:
displayedApp
— 当前的 ApprunningApps
/numRunningApps
— 运行的app集合openFrame
/closeFrame
— 打开/关闭动画的过渡框架
在调用 setDisplayedApp()
方法时, app 会通过下图中列出的状态来启动。
在控制 Firefox OS app动画流程时,有下面几个技巧:
- 在 app opened 之前,我们要确认它已经从后台状态中恢复。 我们通常会取一个 1 x 1 截屏来进行重绘。
- 在 app 准备好 opened 时, 我们会同时执行当前 app 关闭的动画和下一个 app 启动的动画。
- 在 app opening 和 closing 期间,我们会运行代码对转屏进行锁定和解锁。
- 仅仅当 app resized 一次之后, 在 opening 时,我们才会运行代码执行 app resize 操作;否则,我们会忽略 resizing 步骤。
- 我们在改变页面的可见性时,会再次使用 1 x 1 截屏(请看上面)。
AppWindow 特定UI
有一些特定的 UI 元素只和特定的 app 相关,如 Browser chrome, modal dialogs, context menus, popups, 以及 error pages.
下面让我们来讨论下。
弹出框(Modal dialogs)
在 desktop Firefox 中,,如果您启动了浏览器开发面板, 在其中输入如 alert()
, confirm()或
prompt()
等命令,就会在屏幕中心弹出一个对话框,并遮盖了下面的内容。Firefox OS 中的 modle dialogs 和这个效果是相同的。
快捷菜单(Context menu)对话框
移动开发者都会非常熟悉 快捷菜单(或长按菜单)。一般在设计app时,为了使用户能够更简便的使用 app,最常用的用户动作应该是对用户可见的。我们可以将一些无法直接放置在UI上,但又经常使用的动作放在快捷菜单中。
授权(Authentication) (https) 对话框
定义在 system/js/app_authentication_dialog.js 文件中。
值选择,时间,日期对话框
定义在 system/js/value_selector/.
权限对话框
定义在 system/js/permission_manager.js 和 system/js/media_recording.js 文件。
特定应用
有些应用需要一个特殊的 appWindow
对象来处理它们包含的特定功能。其中包括:
- Homescreen
- FTU
- Keyboard
- Cost control
- Secure camera
- Lockscreen
子窗口管理
子窗口是由 其他 app/pages 直接或间接打开的。如:
- Attention window
- Popup window
- Activity window
- Trusted UI / Trusted window
当子窗口被正常终止时,它的父窗口应该被重新打开。有些自窗口可会也会包含其他的子窗口。父窗口与子窗口间进程优先级的管理也是一个问题。
视窗(Attention Window)
Attention Windows 用来获取用户的注意力:
- 拨号界面 — dialer
- 时钟界面 — clock
- 权限确认界面
当前这些 attention window 都是被强制使用的默认方向(竖向优先)。
委托(Trusted) UI
Persona 和 mozPay API 使用的时委托 UI。它们使用特定的尺寸sizing: 80% 显示。在 trusted UI 运行时Homescreen是部分显示的。
历史记录管理 (History Management)
本节我们会讲解一些可以在 Firefox 中管理历史记录的组件。
任务管理器
任务管理器 Task manager (card 视图) 可通过长按 home 按钮触发。它会显示设备上的app历史,此时可以动态的kill一个app。
Web activity 处理
内嵌 Activities 会创建一个新的引用页面为 activity 提供数据。
Window Activities 会重复使用已存在的app window 来反映 acitivity 数据。
边缘手势 (测试中)
这个测试的边缘手势动能在 Firefox 2.0+ 开发模式版本才存在,它会允许您使用手动滑动 设备的 右/左边缘来在app和web页面之间切换。
How is the next app to display chosen?
- Child window of the active app
- Launch time is newer
- Find the head window of the next app stack
How is the previous app chosen?
- Parent window of the active app
- Launch time is older
- Find the rear window of the previous app stack
截屏管理
截屏工具会被 任务管理器(卡片视图)所使用,来显示历史记录中的app。当app 关闭动画结束时, 就会获取到一个app的屏幕截图。