赞
踩
一个Page可以由一个或多个AbilitySlice构成,AbilitySlice是指应用的单个页面及其控制逻辑的总和。
当一个Page由多个AbilitySlice共同构成时,这些AbilitySlice页面提供的业务能力应具有高度相关性。
虽然一个Page可以包含多个AbilitySlice,但是Page进入前台时界面默认只展示一个AbilitySlice。默认展示的AbilitySlice是通过setMainRoute()方法来指定的。如果需要更改默认展示的AbilitySlice,可以通过addActionRoute()方法为此AbilitySlice配置一条路由规则。此时,当其他Page实例期望导航到此AbilitySlice时,可以在Intent中指定Action,详见下文的不同Page间导航。
src/main/java/com/example/harmonyos01java/slice/MainAbilitySlice.java
public class MainAbility extends Ability {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setMainRoute(MainAbilitySlice.class.getName());
addActionRoute("action.second", SecondAbilitySlice.class.getName());
}
}
src/main/config.json
addActionRoute()方法中使用的动作命名,需要在应用配置文件(config.json)中注册
{ "module": { "abilities": [ { "skills":[ { "actions":[ "action.system.home", "action.second" ] } ], "name": "com.example.harmonyos01java.MainAbility", "page": "page" ... } ] ... } ... }
系统管理或用户操作等行为均会引起Page实例在其生命周期的不同状态之间进行转换。Ability类提供的回调机制能够让Page及时感知外界变化,从而正确地应对状态变化(比如释放资源),这有助于提升应用的性能和稳健性。
这一部分我不进行太多的描述
这里我们借用一下官方文档的一张图
非常直观的表现了Page的若干个状态
AbilitySlice作为Page的组成单元,其生命周期是依托于其所属Page生命周期的,AbilitySlice生命周期回调与Page的相应回调类似,因此不再赘述。
AbilitySlice和Page具有相同的生命周期状态和同名的回调,当Page生命周期发生变化时,它的AbilitySlice也会发生相同的生命周期变化。此外,AbilitySlice还具有独立于Page的生命周期变化,这发生在同一Page中的AbilitySlice之间导航时,此时Page的生命周期状态不会改变。
Page和AbilitySlice的生命周期很多时候都是一致的,值得注意的是他们都需要编写他们的onStart方法
当AbilitySlice处于前台且具有焦点时,其生命周期状态随着所属Page的生命周期状态的变化而变化。当一个Page拥有多个AbilitySlice时,例如:MyAbility下有FooAbilitySlice和BarAbilitySlice,当前FooAbilitySlice处于前台并获得焦点,并即将导航到BarAbilitySlice,在此期间的生命周期状态变化顺序为:
对应两个slice的生命周期方法回调顺序为:
FooAbilitySlice.onInactive() --> BarAbilitySlice.onStart() --> BarAbilitySlice.onActive() --> FooAbilitySlice.onBackground()
在整个流程中,MyAbility始终处于ACTIVE状态。但是,当Page被系统销毁时,其所有已实例化的AbilitySlice将联动销毁,而不仅是处于前台的AbilitySlice。
当发起导航的AbilitySlice和导航目标的AbilitySlice处于同一个Page时,您可以通过present()方法实现导航。
这个也是我们上一个博客中有用到的
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
Button button = (Button) findComponentById(ResourceTable.Id_button);
// 点击按钮跳转至第二个页面
button.setClickedListener(listener -> present(new SecondAbilitySlice(), new Intent()));
}
如果开发者希望在用户从导航目标AbilitySlice返回时,能够获得其返回结果,则应当使用presentForResult()实现导航。用户从导航目标AbilitySlice返回时,系统将回调onResult()来接收和处理返回结果,开发者需要重写该方法。返回结果由导航目标AbilitySlice在其生命周期内通过**setResult()**进行设置。
@Override protected void onStart(Intent intent) { ... Button button = ...; button.setClickedListener(listener -> presentForResult(new TargetSlice(), new Intent(), 0)); ... } @Override protected void onResult(int requestCode, Intent resultIntent) { if (requestCode == 0) { // Process resultIntent here. } }
在我的理解下requestCode是用来辨别不同的接受请求的AbilitySlice的,
值得注意的是,系统为每个Page维护了一个AbilitySlice实例的栈,每个进入前台的AbilitySlice实例均会入栈。当开发者在调用present()或presentForResult()时指定的AbilitySlice实例已经在栈中存在时,则栈中位于此实例之上的AbilitySlice均会出栈并终止其生命周期。
AbilitySlice作为Page的内部单元,以Action的形式对外暴露,因此可以通过配置Intent的Action导航到目标AbilitySlice。Page间的导航可以使用**startAbility()或startAbilityForResult()**方法,获得返回结果的回调为onAbilityResult()。在Ability中调用setResult()可以设置返回结果。
这里我们尝试着制作一个demo
新建一个新java项目(第二个)
在src/main/java/xxx.xxx/slice
下新建一个SecondAbilitySlice类,使其继承于AbilitySlice类
在src/main/java/xxx.xxx
下新建一个SecondAbility类,使其继承于Ability类
在src/main/resources/base/layout
新建一个ability_second.xml的Layout Resource File
在src/main/resources/base/graphic
新建一个background_button.xml的Graphic Resourse File
修改src/main/resources/base/element/string.json
、src/main/resources/base/en.element/string.json
为
{ "string": [ { "name": "entry_MainAbility", "value": "entry_MainAbility" }, { "name": "mainability_description", "value": "Java_Empty Ability" }, { "name": "entry_SecondAbility", "value": "entry_SecondAbility" }, { "name": "secondability_description", "value": "Java_Empty Ability" }, { "name": "string_tip_to_jump", "value": "Press the button to jump" }, { "name": "string_tip_jump_success", "value": "Jump success" } ] }
修改src/main/resources/base/zh.element/string.json
为
{ "string": [ { "name": "entry_MainAbility", "value": "entry_MainAbility" }, { "name": "mainability_description", "value": "Java_Empty Ability" }, { "name": "entry_SecondAbility", "value": "entry_SecondAbility" }, { "name": "secondability_description", "value": "Java_Empty Ability" }, { "name": "string_tip_to_jump", "value": "按下按钮以跳转" }, { "name": "string_tip_jump_success", "value": "跳转成功" } ] }
我们需要在config.json中添加HAP包的配置信息
{ "app": { ... }, "deviceConfig": {}, "module": { ... "abilities": [ { "skills": [ { "entities": [ "entity.system.home" ], "actions": [ "action.system.home" ] } ], "orientation": "unspecified", "name": "com.example.harmonyos02pagenavigator.MainAbility", "icon": "$media:icon", "description": "$string:mainability_description", "label": "$string:entry_MainAbility", "type": "page", "launchType": "standard" }, { "skills": [ { "entities": [ "entity.system.home" ], "actions": [ "action.system.home", "action.second" ] } ], "orientation": "unspecified", "name": "com.example.harmonyos02pagenavigator.SecondAbility", "icon": "$media:icon", "description": "$string:secondability_description", "label": "$string:entry_SecondAbility", "type": "page", "launchType": "standard" } ] } }
添加完毕后我们开始编写前面新建的几个空类
SecondAbilitySlice
package com.example.harmonyos02pagenavigator.slice;
import com.example.harmonyos02pagenavigator.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
public class SecondAbilitySlice extends AbilitySlice {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_second);
}
}
SecondAbility
package com.example.harmonyos02pagenavigator; import com.example.harmonyos02pagenavigator.slice.MainAbilitySlice; import com.example.harmonyos02pagenavigator.slice.SecondAbilitySlice; import ohos.aafwk.ability.Ability; import ohos.aafwk.content.Intent; public class SecondAbility extends Ability { @Override protected void onStart(Intent intent) { super.onStart(intent); super.setMainRoute(SecondAbilitySlice.class.getName()); addActionRoute("action。second",SecondAbilitySlice.class.getName()); } }
开始编写前面新建的几个新xml
ability_second.xml
<?xml version="1.0" encoding="utf-8"?>
<DependentLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical">
<Text
ohos:id="$+id:string_jump_success"
ohos:width="match_content"
ohos:height="match_content"
ohos:text="$string:string_tip_jump_success"
ohos:text_color="#000000"
ohos:text_size="32fp"
ohos:center_in_parent="true"/>
</DependentLayout>
background_button.xml
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle">
<corners
ohos:radius="100"/>
<solid
ohos:color="#007DFF"/>
</shape>
修改已经存在的layout ability_main.xml
<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_parent" ohos:width="match_parent" ohos:alignment="center" ohos:orientation="vertical"> <Button ohos:id="$+id:button_jump" ohos:height="match_content" ohos:width="match_content" ohos:text_color="#FFFFFF" ohos:text="$string:string_tip_to_jump" ohos:padding="8vp" ohos:left_padding="50vp" ohos:right_padding="50vp" ohos:background_element="$graphic:background_button" ohos:layout_alignment="horizontal_center" ohos:text_size="25vp" /> </DirectionalLayout>
修改已经存在的类
src/main/java/xxx.xxx/slice/MainAbilitySlice
package com.example.harmonyos02pagenavigator.slice; import com.example.harmonyos02pagenavigator.ResourceTable; import ohos.aafwk.ability.AbilitySlice; import ohos.aafwk.content.Intent; import ohos.aafwk.content.Operation; import ohos.agp.components.Button; public class MainAbilitySlice extends AbilitySlice { @Override public void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); Button button = (Button) findComponentById(ResourceTable.Id_button_jump); Intent secondIntent = new Intent(); Operation operation = new Intent.OperationBuilder().withAction("action.second").build(); secondIntent.setOperation(operation); button.setClickedListener(listener -> startAbility(secondIntent)); } @Override public void onActive() { super.onActive(); } @Override public void onForeground(Intent intent) { super.onForeground(intent); } }
运行测试
跨设备迁移(下文简称“迁移”)支持将Page在同一用户的不同设备间迁移,以便支持用户无缝切换的诉求。以Page从设备A迁移到设备B为例,迁移动作主要步骤如下:
看上去十分复杂的东西,官方文档却不是很长,官方应该是觉得这个部分不会特别难,所以我决定做一个demo来熟悉这个技术
新建Java Phone Application项目
修改string.json
src/main/resources/base/element/string.json
、src/main/resources/base/zh.element/string.json
{ "string": [ { "name": "entry_MainAbility", "value": "跨设备迁移" }, { "name": "mainability_description", "value": "Java_Empty Ability" }, { "name": "mainability_tip", "value": "输入内容以跨设备迁移" }, { "name": "mainability_button", "value": "跨设备迁移" }, { "name": "mainability_success", "value": "跨设备成功" } ] }
src/main/resources/base/element/en.string.json
{ "string": [ { "name": "entry_MainAbility", "value": "Cross-equipment migration" }, { "name": "mainability_description", "value": "Java_Empty Ability" }, { "name": "mainability_tip", "value": "Enter content migrate across equipment" }, { "name": "mainability_button", "value": "cross-equipment migration" }, { "name": "mainability_success", "value": "success" } ] }
修改config.json
添加跨设备迁移权限
{
...
"module": {
...
"reqPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}
]
}
}
修改ability_background.xml (ui修改)
<DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_parent" ohos:width="match_parent" ohos:alignment="center" ohos:orientation="vertical"> <TextField ohos:id="$+id:message_textfield" ohos:height="match_content" ohos:width="300vp" ohos:hint="$string:mainability_tip" ohos:left_margin="24vp" ohos:padding="5vp" ohos:background_element="$graphic:background_text_field" ohos:text_alignment="center" ohos:right_margin="24vp" ohos:text_size="16vp"/> <Button ohos:id="$+id:button_jump" ohos:height="match_content" ohos:width="match_content" ohos:text_color="#FFF" ohos:text="$string:mainability_button" ohos:top_margin="30vp" ohos:padding="8vp" ohos:left_padding="50vp" ohos:right_padding="50vp" ohos:background_element="$graphic:background_button" ohos:layout_alignment="horizontal_center" ohos:text_size="15vp" /> </DirectionalLayout>
新建两个Graphic Resource File
background_text_field.xml
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle">
<corners
ohos:radius="200"/>
<solid
ohos:color="#0d000000"/>
<stroke
ohos:color="#33000000"
ohos:width="0.4vp"/>
</shape>
background_button.xml
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle">
<corners
ohos:radius="100"/>
<solid
ohos:color="#007DFF"/>
</shape>
使MainAbility实现IAbilityContinuation
几乎没有什么实现
package com.example.harmonyos02cemigration; import com.example.harmonyos02cemigration.slice.MainAbilitySlice; import ohos.aafwk.ability.Ability; import ohos.aafwk.ability.IAbilityConnection; import ohos.aafwk.ability.IAbilityContinuation; import ohos.aafwk.content.Intent; import ohos.aafwk.content.IntentParams; import ohos.bundle.IBundleManager; import ohos.security.SystemPermission; public class MainAbility extends Ability implements IAbilityContinuation { @Override public void onStart(Intent intent) { super.onStart(intent); super.setMainRoute(MainAbilitySlice.class.getName()); requestPermissions(); } /** * */ private void requestPermissions() { if (verifySelfPermission(SystemPermission.DISTRIBUTED_DATASYNC) != IBundleManager.PERMISSION_GRANTED) { requestPermissionsFromUser(new String[] {SystemPermission.DISTRIBUTED_DATASYNC}, 0); } } /** * Page请求迁移后,系统首先回调此方法,开发者可以在此回调中决策当前是否可以执行迁移, * 比如,弹框让用户确认是否开始迁移。 * @return */ @Override public boolean onStartContinuation() { //return false; return true; } /** * 如果onStartContinuation()返回true,则系统回调此方法, * 开发者在此回调中保存必须传递到另外设备上以便恢复Page状态的数据。 * @param intentParams 用于保存数据的一个结构类似于map的数据结构 * @return */ @Override public boolean onSaveData(IntentParams intentParams) { //return false; return true; } /** * 源侧设备上Page完成保存数据后,系统在目标侧设备上回调此方法, * 开发者在此回调中接受用于恢复Page状态的数据。注意,在目标侧设备上的Page会重新启动其生命周期, * 无论其启动模式如何配置。且系统回调此方法的时机在onStart()之前。 * @param intentParams 用于恢复数据的一个结构类似于map的数据结构 * @return */ @Override public boolean onRestoreData(IntentParams intentParams) { //return false; return true; } /** * 目标侧设备上恢复数据一旦完成,系统就会在源侧设备上回调Page的此方法, * 以便通知应用迁移流程已结束。开发者可以在此检查迁移结果是否成功, * 并在此处理迁移结束的动作,例如,应用可以在迁移完成后终止自身生命周期。 * @param i */ @Override public void onCompleteContinuation(int i) { terminateAbility(); } /** * 如果开发者使用continueAbilityReversibly()而不是continueAbility(), * 则此后可以在源侧设备上使用reverseContinueAbility()进行回迁。这种场景下, * 相当于同一个Page(的两个实例)同时在两个设备上运行,迁移完成后,如果目标侧设备上Page因任何原因终止, * 则源侧Page通过此回调接收终止通知。 */ @Override public void onRemoteTerminated() { terminateAbility(); } }
使MainAbilitySlice实现IAbilityContinuation
由于顺序是
a方 b方安装好app
a方打开app
a方app申请跨设备迁跃 答应
a方输入数据
a方触发开始跨设备迁跃
b方自动打开app
b方开始跨设备迁跃
a方继续跨设备迁跃
在本项目中,我们设计a方开始跨设备迁跃完毕后直接关闭项目(本项目在Ability中实现了,而非在AbilitySlice中实现),结束其生命周期,所以我们将内容重新显示位于b方app的Ability运行onStart时实现,用到b方 onRestoreData 获取到的参数,放入输入框中,在开始跨设备时保存数据
package com.example.harmonyos02cemigration.slice; import com.example.harmonyos02cemigration.ResourceTable; import ohos.aafwk.ability.AbilitySlice; import ohos.aafwk.ability.IAbilityContinuation; import ohos.aafwk.content.Intent; import ohos.aafwk.content.IntentParams; import ohos.agp.components.Component; import ohos.agp.components.TextField; import ohos.agp.window.dialog.ToastDialog; import ohos.hiviewdfx.HiLog; public class MainAbilitySlice extends AbilitySlice implements IAbilityContinuation { private static final String INPUT_CONTENT = "input content"; private TextField textField ; private String message; private boolean isContinued; @Override public void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); findComponentById(ResourceTable.Id_button_jump).setClickedListener(this::ceMigrate); textField = (TextField) findComponentById(ResourceTable.Id_message_textfield); if (isContinued && message != null) { textField.setText(message); } } private void ceMigrate(Component component){ String messageSend = textField.getText(); if (messageSend.isEmpty()) { new ToastDialog(this).setText("输入信息不为空").show(); return; } try { continueAbility(); } catch (IllegalStateException illegalStateException) { new ToastDialog(this).setText("发送失败").show(); return; } } @Override public void onActive() { super.onActive(); } @Override public void onForeground(Intent intent) { super.onForeground(intent); } @Override public boolean onStartContinuation() { return true; } @Override public boolean onSaveData(IntentParams intentParams) { intentParams.setParam(INPUT_CONTENT,textField.getText()); return true; } @Override public boolean onRestoreData(IntentParams intentParams) { if (intentParams.getParam(INPUT_CONTENT) instanceof String) { message = (String) intentParams.getParam(INPUT_CONTENT); isContinued = true; } return true; } @Override public void onCompleteContinuation(int i) { } @Override public void onRemoteTerminated() { } }
运行测试
b方手机成功打开,并且显示出a方的数据
很快a方手机自动终止该app生命周期
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。