第3 章
窗 口
Ability是应用所具备能力的抽象,也是应用程序的重要组成部分,是Harmony应用程
序的一大核心。一个应用可以包含多个Ability,而Ability 又可以分为FA(Feature
Ability)和PA(ParticleAbility)两种类型,FA 仅支持PageAbility,它对用户是可见的,承
载了一个业务可视化界面,即用户可通过FA 与应用程序进行交互,PA 支持DataAbility
(第9章)和ServiceAbility(第10章),它在后台运行,对用户是不可见的,PA 无法提供与
用户交互的能力。本章主要对PageAbility进行讨论。
通过阅读本章,读者可以掌握:
如何使用PageAbility。
如何在PageAbility之间进行交互。
如何进行跨设备迁移PageAbility。
如何使用AbilitySlice。
PageAbility与AbilitySlice的生命周期。
3.1 PageAbility概述
PageAbility是FA 唯一支持的模板,它本质上是一个窗口,用于提供与用户交互的能
力,类似于Android中的Activity。另外,HarmonyOS提供了AbilitySlice,AbilitySlice是
指应用的单个页面控制逻辑的总和,相当于页面内的子窗口,类似于导航窗口,其功能与
PageAbility相同,在切换时可以在同一个PageAbility内完成,也可以跳转至不同的Page
Ability。PageAbility之间的切换相当于Web网页之间的切换,而AbilitySlice之间的切换
相当于在一个Web页面下不同导航窗口之间的切换。
图3-1 PageAbility与AbilitySlice
的关系
PageAbility可以使用一个或多个AbilitySlice,也可以不使用。在创建HarmonyOS工程
时,包含了一个默认的AbilitySlice(MainAbilitySlice.
java)。当在一个PageAbility中使用多个AbilitySlice
时,这些AbilitySlice所提供的功能之间应该具有高度
的相关性,换言之,页面提供的功能之间有高度相关性
时,应该在一个PageAbility下使用两个AbilitySlice,
而不必使用两个Page Ability,以减少冗余。Page
Ability和AbilitySlice的关系如图3-1所示。
为了提高开发者的开发效率,使用DevEcoStudio
创建HarmonyOS工程时,IDE 提供了一些Ability模
28 HarmonyOS 应用程序开发与实战(Java 版)
板供开发者使用,如图3-2所示,读者可以使用这些Ability模板快速生成一个HarmonyOS
工程的框架,相当于一个简单的HelloWorld工程。
图3-2 Ability模板
3.2 PageAbility的基本用法
为了提高开发效率,DevEcoStudio也为开发人员提供了自动创建PageAbility的功
能,在创建过程中会自动创建Page类型的Ability类,同时创建一个AbilitySlice类以及布
局文件,并自动向config.json文件中添加PageAbility的配置信息,这些都是开发工具自动
完成的。为了使读者能够更好地理解和使用PageAbility,本节将介绍如何手动创建Page
Ability、布局文件,以及如何装载布局文件,并在config.json中配置一个PageAbility。
3.2.1 手动创建Page Ability 类
PageAbility是一个普通的Java类,其必须继承Ability类,该类属于ohos.aafwk.ability
包,下面给出MyFirstAbility类的创建代码:
package com.example.createpageability;
import ohos.aafwk.ability.Ability;
public class MyFirstAbility extends Ability {
}
3.2.2 在config.json 文件中注册Page Ability
在HarmonyOSApp中,所有Ability在使用前必须在config.json文件中的abilities配
第3 章 窗口 29
置项配置。abilities是一个对象数组,每一个对象表示一个Ability,包括PageAbility、Data
Ability和ServiceAbility。MyFirstAbility的配置代码如下:
{
"skills": [
{
"actions": [
"com.example.myfirstability"
]
}
],
"orientation": "unspecified",
"formsEnabled": false,
"name": "com.example.createpageability.MyFirstAbility",
"icon": "$media:icon",
"description": "$string:myfirstability_description",
"label": "$string:myfirstability_label",
"type": "page",
"launchType": "standard"
}
下面对abilities配置信息中的常用属性进行介绍:
(1)skills表示该Ability能够接收Intent的特征,是一个对象数组。
skills中对象又有3种属性:
actions:表示能够接收的Intent的action值,可以包含一个或者多个action;
entities:表示能够接收的Intent的Ability的类别,可以包含一个或者多个entity;
uris:表示能够接收的Intent的uri,可以包含一个或者多个uri。
(2)orientation表示该Ability的显示模式,该标签仅适用于page类型的Ability。
其只有4种取值:
unspecified:由系统自动判断显示方向;
landscape:横屏模式;
portrait:竖屏模式;
followRecent:跟随栈中最近的应用。
(3)formsEnabled表示是否支持卡片功能,仅适用于page类型的Ability。
(4)name表示该Ability的名称,取值可采用反向域名方式表示,由包名加类名组成,
如“com.example.createpageability.MyFirstAbility”,也可以使用“.”开头的类名方式表示,
如“.MyFirstAbility”,一个应用中的每个Ability的name属性应该是唯一的。
(5)icon表示Ability图标资源文件的索引,用于配置该Ability的图标,这里使用
“$media:icon”来表示对resources/base/media下的icon.png文件的引用。
(6)description表示对Ability的描述,这里使用“$string:myfirstability_description”
来表示对resources/base/element下的string.json文件中定义的字符串的引用,以后所有
的字符串都将在这个文件中定义,使用“$string:”来引用。
(7)label表示Ability对用户显示的名称。
30 HarmonyOS 应用程序开发与实战(Java 版)
(8)type表示该Ability的类型。
其一共有四种取值:
page:表示基于Page模板开发的FA,用于提供与用户交互的能力;
service:表示基于Service模板开发的PA,用于提供后台运行任务的能力;
data:表示基于Data模板开发的PA,用于对外部提供统一的数据访问抽象;
CA:表示支持其他应用以窗口方式调起该Ability。
(9)launchType表示Ability的启动模式。
它支持“standard”“singleMission”和“singleton”3种模式:
standard:表示该Ability可以有多实例,“standard”模式适用于大多数应用场景;
singleMission:表示该Ability在每个任务栈中只能有一个实例;
singleton:表示该Ability在所有任务栈中仅可以有一个实例。例如,具有全局唯一性
的呼叫来电界面即采用“singleton”模式。该标签仅适用于手机、平板、智慧屏、车机、智能
穿戴。
(10)reqPermissions:表示此Ability需要申请的权限。
3.2.3 创建布局文件
HarmonyOSApp所有的布局文件将放在entry/src/main/resources/base/layout目录
下面,首先,创建一个LayoutResourceFile并命名为my_first_layout.xml,然后输入以下
代码:
关于布局和组件,之后的章节会详细进行介绍,读者在此模仿例子使用即可,这里用到
第3 章 窗口 31
的是方向布局DirectionalLayout,也是创建布局文件时,IDE会创建的默认布局。这里用到
了两个组件:Text和Button。
3.2.4 静态装载布局文件
创建完布局文件后,需要将布局文件加载到PageAbility上,才能显示布局中的组件。
通常需要在PageAbility启动时装载布局文件,也就是需要在PageAbility的生命周期方
法中的onStart()方法里完成。关于生命周期方法,后面小节会详细介绍,读者只需要知道
onStart()方法在PageAbility启动时调用即可,通常会在这个方法里做一些初始化的工作,
例如,加载布局文件,初始化组件,为组件添加事件监听器等。
现在需要调用父类Ability的onStart()方法,并使用super.setUIContent()方法加载之
前创建的布局文件my_first_layout.xml,注意在写Java代码时,用到了其他类或其他类的
静态方法时,需要import特定的类,这一点在之后的代码中不做体现,代码如下:
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
public class MyFirstAbility extends Ability {
//调用父类onStart()方法
public void onStart(Intent intent)
{
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_my_first_layout);
}
}
在HarmonyOSApp中,系统会将所有静态资源与一个int类型的值进行绑定,并将这
些值以常量的形式定义在static类型的类ResourceTable中,以便通过这个静态类调用这
些值,通过该值引用相关资源。这些值是自动生成的,以资源文件的名称加上资源类型(作
为前缀)作为变量名。例如,布局文件生成的ID需要加上前缀Layout,本例的布局文件是
my_first_layout.xml,因此在ResourceTable类中会自动生成ID:Layout_my_first_layout。
根据这个生成规则,还要求资源文件的命名必须符合Java标识符的命名规则,否则无法在
ResourceTable自动生成ID。
3.2.5 显示Page Ability
目前为止,一个小型但完整的PageAbility已经创建完成,最后一步就是显示这个创建
好的PageAbility。如果想让MyFirstAbility作为应用的主Ability(即程序运行后的显示
的第一个页面)显示,可以修改MyFirstAbility的配置信息的skills部分,将其修改为如下
形式:
"skills": [
{
"entities": [
"entity.system.home"
32 HarmonyOS 应用程序开发与实战(Java 版)
],
"actions": [
"action.system.home"
]
}
]
需要注意的是,一个应用只能有一个主Ability,但在config.json 文件中还有其他
Ability的actions也设为“action.system.home”,而HarmonyOS只会显示在config.json文
件中遇到的第一个主Ability。因此如果需要将您的Ability设为主Ability,就需要将您的
Ability配置信息作为abilities中的第一个元素,或者删除其他的action属性值为“action.
system.home”的配置项。将MyFirstAbility作为主Ability显示时,打开应用会看到如
图3-3(左)所示的页面,而如果从其他页面显示MyFirstAbility,如从图3-3(右)显示
MyFirstAbility,显示效果和图3-3(左)相同。
图3-3 显示MyFirstAbility
3.2.6 销毁Page Ability
在PageAbility使用完后,需要关闭(或销毁)PageAbility,调用如下代码即可销毁
PageAbility:
terminateAbility();
该方法属于Ability类,如果在AbilitySlice(后面章节介绍)中需要调用该方法,需要获
得包含该AbilitySlice的Ability对象。
第3 章 窗口 33
3.3 PageAbility之间的交互
本小节将介绍两个不同的PageAbility之间如何进行交互,例如,在两个不同Page
Ability之间传递数据、通过显式和隐式的方式在一个PageAbility中显示另一个Page
Ability。
3.3.1 Intent 的基本概念
Intent是对象之间传递信息的载体。例如,当一个Ability需要启动另一个Ability时,
或者一个AbilitySlice需要导航到另一个AbilitySlice时,可以通过Intent指定启动的目标
同时携带相关数据。Intent的构成元素包括Operation与Parameters。
Operation又包含以下7项属性:
(1)Action:表示动作,可以使用系统内置的Action,也可以使用用户在config.json文
件中自定义的Action。
(2)Entity:表示类别,可以使用系统内置的Action,也可以使用用户在config.json文
件中自定义的Action。
(3)URI:表示URI描述。如果在Intent中指定了URI,则Intent将匹配指定的URI
信息。
(4)Flags:表示处理Intent的方式。如Intent.FLAG_ABILITY_CONTINUATION
标记在本地的一个Ability是否可以迁移到远端设备继续运行。
(5)BundleName:表示包描述。
(6)AbilityName:表示待启动的Ability 名称。如果在Intent 中同时指定了
BundleName和AbilityName,则Intent可以直接匹配到指定的Ability。
(7)DeviceId:表示运行指定Ability的设备ID。
Parameters是一种支持自定义的数据结构,开发者可以通过Parameters传递某些请求
所需的额外信息。
3.3.2 显式使用Intent
所谓显式使用就是同时指定Operation属性的BundleName和AbilityName两项子属
性,即根据Ability的全称启动Ability,隐式使用就是指未同时指定Operation 属性的
BundleName和AbilityName,根据Operation的其他属性启动Ability,通常指定Ability指
定的action属性启动PageAbility。
【例3.1】 首先需要创建一个名为ability_main.xml的布局文件,同时添加两个Button
组件并绑定到MainAbility,主Ability用于演示显式使用Intent和隐式使用Intent的区别。
布局文件代码如下:
然后创建一个名为ExplicitAbility的PageAbility,同时绑定一个布局文件,并在配置
文件中进行注册,由于代码简单,因此不在此赘述。显式地显示PageAbility的步骤如下:
(1)创建Intent对象;
(2)使用Intent.OperationBuilder类构造包含BundleName与AbilityName的Operation
对象;
(3)通过Intent的setOperation()方法指定Intent的Operation对象;
(4)调用startAbility()方法显示PageAbility。
按照以上步骤编写显示ExplicitAbility的代码如下:
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
//指定设备标识,空串表示当前设备
.withDeviceId("")
//指定包名
.withBundleName("com.example.explicitintent")
//指定Page Activity 的name 属性值
.withAbilityName("com.example.explicitintent.ExplicitIntentAbility")
.build();
intent.setOperation(operation);
startAbility(intent);
其中,withDeviceID用于指定设备ID,在config.json文件中的deviceConfig中进行配
置,空串表示当前设备。withBundleName指定的是HarmonyOSApp的包名,在config.json文
件中bundlename属性指定。withAbilityName指定的是PageAbility的全名(包名+类
名)。最后需要调用build()方法返回一个Operation对象。执行程序,单击“Explicit”按钮
将会显示ExplicitAbility,效果如图3-4所示。
第3 章 窗口 35
图3-4 显式使用Intent
3.3.3 隐式使用Intent
首先创建一个名为Implicit1Ability的PageAbility类,同时绑定一个布局文件,然后
在config.json文件中添加如下配置信息:
{
"skills": [
{
"actions": [
"action.implicit"
]
}
],
"orientation": "unspecified",
"name": "com.example.intentuse.Implicit1Ability",
"icon": "$media:icon",
"description": "$string:implicit1",
"label": "$string:implicit1",
"type": "page",
"launchType": "standard"
}
由于在隐式使用Intent的方式中需要用到action属性,因此需要配置action为“action.
implicit”,并通过如下代码显示Implicit1Ability:
36 HarmonyOS 应用程序开发与实战(Java 版)
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withAction("action. implicit ")
.build();
intent.setOperation(operation);
startAbility(intent);
隐式使用Intent时,不需要指定BundleName与AbilityName,只需要指定Action即
可。注意,由于在实际开发过程中,有可能遇到多个PageAbility指定同一个action,因此,
使用隐式显示PageAbility时,HarmonyOS会弹出一个列表,列表中是所有绑定了同一个
action的PageAbility,供用户选择到底使用哪一个PageAbility。这也是和显式地显示
PageAbility的一个重要区别,由于显式方法指定确定的BundleName与AbilityName,因
此会显示指定PageAbility。下面通过一个例子介绍多个PageAbility指定同一个action
时会出现的情况。
再创建一个名为Implicit2Ability的PageAbility,同时绑定布局文件,在config.json文
件中的配置信息如下所示:
{
"skills": [
{
"actions": [
"action.implicit"
]
}
],
"orientation": "unspecified",
"name": "com.example.intentuse.Implicit2Ability",
"icon": "$media:icon",
"description": "$string:implicit2",
"label": "$string:implicit2",
"type": "page",
"launchType": "standard"
}
显然,这两个PageAbility指定了同一个action,使用如下代码显示PageAbility:
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withAction("action. implicit ")
.build();
intent.setOperation(operation);
startAbility(intent);
执行程序,单击“Implicit”按钮执行以上代码,系统将会弹出一个列表供用户选择进入
哪个PageAbility,如图3-5所示。
第3 章 窗口 37
图3-5 隐式使用Intent
3.3.4 Page Ability 之间的通信
一般来说,PageAbility之间是需要进行通信的,而通信方式可以分为两种,一种是
PageAbility 向下一个PageAbility 传递数据;另一种是PageAbility 向上一个Page
Ability返回数据。
第一种通信方式是单向的,因此只用通过Intent对象的setParam()函数携带相关数据
并调用startAbility()方法传递到下一个PageAbility即可,代码如下:
Intent intent = new Intent();
intent.setParam("data","mydata");
startAbility(intent);
执行这段代码,就能将数据传到下一个PageAbility,在下一个PageAbility中,通过调
用Intent.getStringParam()方法就能获得传递过来的数据,代码如下:
Intent myintent = getIntent();
String data = myintent.getStringParam("data");
第二种通信方式是双向的,从PageAbility1传递数据并跳转到PageAbility2时,需要
PageAbility2返回数据给PageAbility1。此时,需要使用startAbilityForResult()方法。
当使用该方法跳转到PageAbility2 时,由PageAbility2 返回到PageAbility1 后,Page
Ability1会自动调用onAbilityResult()方法,因此,需要提前在PageAbility1中重写该方
法用于接收从PageAbility2传递过来的数据,该方法的原型:
protected void onAbilityResult (int requestCode, int resultCode,
Intent resultData);
38 HarmonyOS 应用程序开发与实战(Java 版)
onAbilityResult()方法的参数含义如下。
(1)requestCode:请求码,通过在启动Ability时设置,即startAbilityResult()方法的
第2个参数。
(2)resultCode:响应码,在PageAbility2中调用setResult()方法时设置。
(3)resultData:由PageAbility2返回的Intent对象形式的数据。
这里重点介绍一下requestCode和resultCode。因为在PageAbility中可能会通过
startAbilityForResult()方法显示多个PageAbility,所以onAbilityResult()方法可能是多
个PageAbility共享的,这就要求在onAbilityResult()方法中区别是哪一个PageAbility
返回的结果。可以通过requestCode或resultCode单独区分不同的PageAbility,也可以使
用requestCode和resultCode共同区分不同的PageAbility。如果在PageAbilityl中通过
startAbilityForResult()方法显示PageAbility2,那么requestCode应该在PageAbility1中
指定,而resultCode应该在PageAbility2中指定。
【例3.2】 演示两个Page Ability 之间是如何通信的。在Page Ability1 调用
startAbilityForResult()方法显示Page Ability2,并传递一个字符串类型数据,Page
Ability2接收这个数据,并显示在页面上,然后关闭PageAbility2并返回一些数据给Page
Ability1,PageAbility1在onAbilityResult()方法中接收来自PageAbility2的数据。
首先创建一个名为pageability1_layout.xml的布局文件,布局中包括一个Text,用于显
示PageAbility2返回的数据,一个Button,用于显示PageAbility2,代码如下:
然后创建一个名为pageability2_layout.xml的布局文件,布局中包括一个Text,用于显
示PageAbility1传递来的数据,一个Button,用于关闭PageAbility2,代码如下:
第3 章 窗口 39
接下来创建一个名为PageAbility1的PageAbility类,主要代码如下:
public class PageAbility1 extends Ability {
Button button;
Text text;
public void onStart(Intent intent){
super.onStart(intent);
//加载布局文件
super.setUIContent(ResourceTable.Layout_pageability1_layout);
//通过组件ID 获取组件
button = (Button)findComponentById(ResourceTable.Id_button_page1);
text = (Text)findComponentById(ResourceTable.Id_text_page1);
//绑定按钮监听事件
button.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
Intent intent = new Intent();
//传递一个字符串
intent.setParam("pageability1_data",
"这是Page Ability1 传递的数据");
Operation operation = new Intent.OperationBuilder()
.withBundleName("com.example.interactionofpage")
.withAbilityName("com.example.interactionofpage
.PageAbility2").build();
intent.setOperation(operation);
//显示PageAbility2,设置请求码为100
startAbilityForResult(intent,100);
40 HarmonyOS 应用程序开发与实战(Java 版)
}
});
}
//重写onAbilityResult 方法,当PageAbility2 关闭时自动调用
protected void onAbilityResult(int requestCode, int resultCode,
Intent resultData){
switch (requestCode){
case 100:
switch(resultCode){
case 101:
//接收返回的数据
String data = resultData.getStringParam
("pageability2_data");
//将接收到的数据显示在页面中Text 组件上
if(data!=null)text.setText(data);
break;
}
break;
}
}
}
这段代码实现了使用startAbilityForResult()方法从PageAbility1显示PageAbility2,
并传递一个字符串类型数据,重写了onAbilityResult(),对requestCode和resultCode进行
了判断,如果满足条件,则接收返回的数据,现在需要创建一个名为PageAbility2的Page
Ability类,主要代码如下:
public class PageAbility2 extends Ability {
Button button;
Text text;
public void onStart(Intent intent){
super.onStart(intent);
//加载布局文件
super.setUIContent(ResourceTable.Layout_pageability2_layout);
//获取PageAbility1 传递的数据
Intent myintent = getIntent();
String data = myintent.getStringParam("pageability1_data");
//通过组件ID 获取组件
button = (Button)findComponentById(ResourceTable.Id_button_page2);
text = (Text)findComponentById(ResourceTable.Id_text_page2);
//将传递来的数据显示在页面的Text 组件上
if(data != null)text.setText(data);
//绑定按钮监听,返回PageAbility1,并返回一个字符串类型数据
button.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
Intent resultintent = new Intent();
//设置返回到PageAbility1 的数据
resultintent.setParam("pageability2_data",
"这是PageAbility2 返回的数据");
第3 章 窗口 41
//设置响应码以及返回的Intent 对象
setResult(101, resultintent);
//关闭PageAbility2
terminateAbility();
}
});
}
}
现在,两个PageAbility已经准备就绪,最后在config.json文件中注册这两个Page
Ability即可使用这两个PageAbility。配置信息如下:
{
"orientation": "unspecified",
"name": "com.example.interactionofpage.PageAbility1",
"icon": "$media:icon",
"description": "$string:pageability1",
"label": "$string:pageability1",
"type": "page",
"launchType": "standard"
},
{
"orientation": "unspecified",
"name": "com.example.interactionofpage.PageAbility2",
"icon": "$media:icon",
"description": "$string:pageability2",
"label": "$string:pageability2",
"type": "page",
"launchType": "standard"
}
运行程序,效果如图3-6所示。
图3-6 PageAbility交互效果图
42 HarmonyOS 应用程序开发与实战(Java 版)
3.4 PageAbility的启动类型
在之前的例子中,所有Ability的配置信息launchType的值都为standard,这也是
launchType属性的默认值。launchType还有两个值为singleMission和singleton,本节将
对这3种属性值的作用进行介绍。
standard表示此Ability可以创建多个实例,且在任何情况下,无论PageAbility被显
示多少次,每次被显示都会创建一个新的PageAbility实例。
singleMission表示此Ability在每个任务栈中只能有一个实例。如果要显示的Page
Ability在栈顶,那么再次显示这个PageAbility时,不会再创建新的PageAbility实例,而
是直接使用这个PageAbility实例。如果PageAbility上面有其他的PageAbility,那么首
先弹出这些PageAbility,然后再复用这个PageAbility。
singleton表示该Ability在所有任务栈中只能有一个实例,例如,具有全局唯一性的呼
叫来电界面即采用“singleton”模式。
其中涉及的栈是HarmonyOS管理PageAbility的模式。由于HarmonyOSApp只能
显示一个PageAbility,HarmonyOSApp会使用栈来管理App中的所有PageAbility,只
有在栈顶的PageAbility才能显示,如果要让非栈顶的PageAbility显示,那么就需要将要
显示的PageAbility之前的所有PageAbility销毁,使得要显示的PageAbility位于栈顶,
要销毁PageAbility,则需要调用terminateAbility()方法。
【例3.3】 由于singleMission模式和singleton模式都只允许Ability只有一个实例,
本例仅演示standard和singleMission启动类型的区别。
首先创建一个名为LaunchTypeAbility1的PageAbility类,主要代码如下:
public class LaunchTypeAbility1 extends Ability {
private static int count = 0; //计数器
public void onStart(Intent intent){
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_launchtype1);
//每创建一个LaunchTypeAbility1 对象,计数器加一
count++;
Button button = (Button)findComponentById(ResourceTable.Id_button1);
Text count1 = (Text)findComponentById(ResourceTable.Id_count1);
if(count1 != null){
//将计数器的值显示在页面中
count1.setText(String.valueOf(count));
}
//设置按钮单击事件监听器
button.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withBundleName("com.example.launchtype").withAbilityName
第3 章 窗口 43
("com.example.launchtype.LaunchTypeAbility1").build();
intent.setOperation(operation);
startAbility(intent);
}
});
}
}
其次在config.json文件中添加这个类的配置信息,注意,先将这个类的launchType属
性都设置为standard,在LaunchTypeAbility1重复显示LaunchTypeAbility1,这样可以很
好地观察到在standard和singleMission这两种启动模式下,计数器的变化情况。
LaunchTypeAbility1这个类中定义了一个变量count作为计数器,每次创建一个
LaunchTypeAbility1实例对象,计数器都会加一,在standard 启动模式下,显示三次
LaunchTypeAbility1,系统则会创建三个LaunchTypeAbility1实例对象,因此计数器的值
变为3,页面效果如图3-7所示。
现在将LaunchTypeAbility1 的启动模式改为singleMission,然后再显示三次
LaunchTypeAbility1。由于在singleton模式下,显示LaunchTypeAbility1并不会创建新的
LaunchTypeAbility1实例对象,可以观察到计数器的值都为1,页面效果如图3-8所示。
图3-7 standard启动模式的页面效果图
图3-8 singleMission启动模式的页面效果
3.5 PageAbility的跨设备迁移
HarmonyOS的一大技术特性是分布式任务调度,所谓分布式任务调度,就是构建统一
的分布式服务管理(发现、同步、注册、调用)机制,支持对跨设备的应用进行远程启动、远程
44
HarmonyOS
应用程序开发与实战(Java
版)
调用、远程连接以及迁移等操作,能够根据不同设备的能力、位置、业务运行状态、资源使用
情况,以及用户的习惯和意图,选择合适的设备运行分布式任务。而PageAbility的跨设备
迁移则依赖了分布式设备调度中的业务迁移能力,是分布式任务调度的一个具体实现。跨
设备迁移支持将Page在同一用户的不同设备间迁移,以便支持用户无缝切换的诉求。实现
这个操作的前提是参加跨设备迁移的设备在同一个网段内,或者登录了同一个HUAWEI
账号,接下来将介绍跨设备前需要准备的工作。
3.5.1
跨设备迁移前的准备工作
在进行跨设备迁移之前(后面几章所讲的跨设备调用DataAbility、ServiceAbility也
同样要进行这些准备), 需要对HarmonyOS 设备做一些准备工作:
(1)打开HarmonyOS 设备的蓝牙,并把设备名称修改为可识别的名称,如图3-9所示;
(2)将HarmonyOS 设备连入Wi-Fi,并且所有参与跨设备迁移的HarmonyOS 设备在
同一个网段;
(3)所有参与跨迁移的HarmonyOS 设备登录同一个HUAWEI 账号;
(4)选择“设置”→“超级终端”查看附近设备如图3-10 所示。
图3-
9
设置设备名称图3-10
附近设备
3.5.2
获取设备列表
跨设备迁移需要知道目标设备的ID,因此需要提前获取所有可用的设备ID 。
yOS 提供了一个用于获取所有设备信息的方法,即Dggt()
HarmoneviceManaer.etDeviceLis
方法,该方法返回一个List列表,类型是DeviceInfo。DeviceInfo类型描述了设备的名称和
第3 章 窗口 45
设备ID等相关信息,实现代码如下:
List deviceInfoList =
DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE)
其中,getDeviceList()方法的参数表示获取哪种状态下的设备的信息,一共有3种取值:
(1)DeviceInfo.FLAG_GET_ONLINE_DEVICE:所有在线设备;
(2)DeviceInfo.FLAG_GET_OFFLINE_DEVICE:所有离线设备;
(3)DeviceInfo.FLAG_GET_ALL_DEVICE:所有设备。
一般会使用第一个值,因为只有设备在线,才能进行迁移PageAbility操作。
【例3.4】 实现一个获取在线设备列表的PageAbility,单击一个设备会返回该设备的
ID。首先创建一个名为device_ids.xml的布局文件,代码如下:
布局中包含了一个ListContainer组件,用于显示所有设备信息,再创建一个名为
device_id_item.xml的布局文件,作为ListContainer组件的Item 布局,代码如下:
该布局文件中放置的两个TextField组件,分别显示设备名称和设备ID。接下来是
PageAbility的实现。创建一个名为DeviceIDAbility的PageAbility类,代码如下:
public class DeviceIDAbility extends Ability {
//存放获取到的在线设备信息
private List deviceInfoList;
//展示设备信息的容器
private ListContainer listContainer;
//获取所有在线设备信息
private static List getAllOnlineDeviceInfo(){
List deviceInfoList =
DeviceManager.getDeviceList (DeviceInfo.FLAG_GET_ONLINE_DEVICE);
if(deviceInfoList == null || deviceInfoList.size() == 0)
{
return new ArrayList<>();
}
else{
return deviceInfoList;
}
}
public void onStart(Intent intent) {