PA定时拉起闹钟FA
本节前,我们分别开发完了闹钟设置entry FA和闹钟clock FA的功能开发,本节我们将完成最后的逻辑定时拉起。这块设计的思路是在闹钟设置entry FA的里再新增一个Service Ability的PA,里面通过定时任务触发闹钟,并跨设备拉起闹钟FA里的闹钟界面。
1.闹钟设置entry FA里新增Service Ability,触发定时拉起
1.1 在公共common har包中增加常量Constants,定义公共消息关闭闹钟代码。 Constants.java
public class Constants {
public static final int CLOSE_CLOCK = 1001;
}
1.2 闹钟设置entry FA里新增Service Ability为ScheduleServiceAbility。通过ScheduledExecutorService触发定时器,判断是否有闹钟触发。 ScheduleServiceAbility.java
@Override
public void onStart(Intent intent) {
LogUtil.info(TAG, "ScheduleServiceAbility::onStart");
super.onStart(intent);
// 按分钟定时检查
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
scheduledExecutorService.scheduleAtFixedRate(() -> {
doAlarm();
LogUtil.info(TAG, "ScheduleServiceAbility::onBackground");
}
, 0, 30, TimeUnit.SECONDS);
}
private void doAlarm() {
LogUtil.info(TAG, "doAlarm isInAlarm=" + isInAlarm);
if (isInAlarm) {
return;
}
// Clock clock = new Clock();
// clock.setBell(2);
// startClockAlarmAbility(clock);
Optional<Clock> optionClock = getAlarmClock();
if (optionClock.isPresent()) {
startClockAlarmAbility(optionClock.get());
}
}
private Optional<Clock> getAlarmClock() {
Calendar calendar = Calendar.getInstance();
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
List<Clock> clockList = ClockManager.getInstance(this.getApplicationContext()).getAllClocks();
for (Clock clock : clockList) {
if (clock.isEnable() && clock.getHour() == hour && clock.getMinute() == minute) {
return Optional.of(clock);
}
}
return Optional.empty();
}
1.3 ScheduleServiceAbility里使用startAbility方法,分别拉起本地和其他设备的闹钟FA. ScheduleServiceAbility.java
private void startClockAlarmAbility(Clock clock) {
isInAlarm = true;
startLocalClockAlarmAbility(clock);
startRemoteClockAlarmAbility(clock);
}
private void startRemoteClockAlarmAbility(Clock clock) {
Intent intent = new Intent();
intent.setParam("bell", DataConstants.getBell(clock.getBell()));
List<DeviceInfo> deviceInfos =
DeviceManager.getDeviceList(ohos.distributedschedule.interwork.DeviceInfo.FLAG_GET_ONLINE_DEVICE);
for (DeviceInfo deviceInfo : deviceInfos) {
Operation operation = new Intent.OperationBuilder()
.withDeviceId(deviceInfo.getDeviceId())
.withBundleName(getBundleName())
.withAbilityName("com.madixin.clock.clock.ClockAlarmAbility")
.withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
.build();
intent.setOperation(operation);
startAbility(intent);
}
}
private void startLocalClockAlarmAbility(Clock clock) {
Intent intent = new Intent();
intent.setParam("bell", DataConstants.getBell(clock.getBell()));
intent.setParam("deviceLocal", "local");
Operation operation = new Intent.OperationBuilder()
.withDeviceId("")
.withBundleName(getBundleName())
.withAbilityName("com.madixin.clock.clock.ClockAlarmAbility")
.build();
intent.setOperation(operation);
startAbility(intent);
}
1.4 ScheduleServiceAbility里增加RemoteObject,接收闹钟FA关闭的事件,把是否闹钟的状态置为false,后续可继续定时触发闹钟。 ScheduleServiceAbility.java
private boolean isInAlarm = false;
private ScheduleServiceAbility.MyRemote remote = new ScheduleServiceAbility.MyRemote();
@Override
public IRemoteObject onConnect(Intent intent) {
super.onConnect(intent);
return remote.asObject();
}
@Override
public void onDisconnect(Intent intent) {
}
class MyRemote extends RemoteObject implements IRemoteBroker {
MyRemote() {
super("MyService_MyRemote");
}
@Override
public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
LogUtil.info(TAG, "onRemoteRequest:" + code);
switch (code) {
case Constants.CLOSE_CLOCK:
isInAlarm = false;
return true;
default:
return true;
}
}
@Override
public IRemoteObject asObject() {
return this;
}
}
2.闹钟clock FA里新增Service Ability拉起闹钟界面
2.1 修改ClockAlarmAbilitySlice,在onstart启动时,连接ScheduleServiceAbility,并在关闭按钮触发时,发送消息给ScheduleServiceAbility重置闹钟状态。 ClockAlarmAbilitySlice.java
package com.madixin.clock.clock.slice;
import com.madixin.clock.clock.ResourceTable;
import com.madixin.clock.clock.util.PlayerManager;
import com.madixin.clock.clock.util.PlayerStateListener;
import com.madixin.clock.common.util.Constants;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.IAbilityConnection;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.agp.components.Button;
import ohos.bundle.ElementName;
import ohos.rpc.IRemoteObject;
import ohos.rpc.MessageOption;
import ohos.rpc.MessageParcel;
import ohos.rpc.RemoteException;
public class ClockAlarmAbilitySlice extends AbilitySlice {
private PlayerManager playerManager;
private IRemoteObject scheduleServiceAbility;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_clock_alarm);
Button btnClose = (Button) findComponentById(ResourceTable.Id_btn_close);
btnClose.setClickedListener((button) -> {
if (scheduleServiceAbility != null) {
MessageParcel data = MessageParcel.obtain();
MessageParcel reply = MessageParcel.obtain();
MessageOption option = new MessageOption(MessageOption.TF_SYNC);
try {
scheduleServiceAbility.sendRequest(Constants.CLOSE_CLOCK, data, reply, option);
} catch (RemoteException e) {
e.printStackTrace();
}
}
if (playerManager.isPlaying()) {
playerManager.releasePlayer();
}
this.terminate();//暂时直接关闭
});
String bell = "Canon";
if (intent != null) {
Object obj = intent.getStringParam("bell");
if (obj instanceof String) {
bell = (String) obj;
}
obj = intent.getStringParam("deviceLocal");
if (obj instanceof String) {
Intent connectPaIntent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withBundleName(getBundleName())
.withAbilityName("com.madixin.clock.setting.ability.ScheduleServiceAbility")
.build();
connectPaIntent.setOperation(operation);
IAbilityConnection conn = new IAbilityConnection() {
@Override
public void onAbilityConnectDone(ElementName elementName, IRemoteObject remote, int resultCode) {
scheduleServiceAbility = remote;
}
@Override
public void onAbilityDisconnectDone(ElementName elementName, int i) {
scheduleServiceAbility = null;
disconnectAbility(this);
}
};
this.connectAbility(connectPaIntent, conn);
}
}
initMedia(bell);
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
private void initMedia(String bell) {
playerManager = new PlayerManager(this, "resources/rawfile/" + bell + ".mp3");
playerManager.setPlayerStateListener(new PlayerStateListener() {
@Override
public void onPlaySuccess(int totalTime) {
}
@Override
public void onPauseSuccess() {
}
@Override
public void onPositionChange(int currentTime) {
}
@Override
public void onMusicFinished() {
}
@Override
public void onUriSet(String name) {
}
});
playerManager.init();
playerManager.play();
}
}
3.闹钟设置entry启动时拉起ScheduleServiceAbility
3.1 在MainAbilitySlice的onstart方法中启动ScheduleServiceAbility。
/**
* 启动后台PA触发定时
*/
private void initClockService() {
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId("")
.withBundleName(getBundleName())
.withAbilityName("com.madixin.clock.setting.ability.ScheduleServiceAbility")
.build();
intent.setOperation(operation);
startAbility(intent);
}
3.2 由于主机功能跨越两个FA,在DevEco Studio中选择Deploy multi hap packages后,部署到真机P40上验证,如果有多台,其他设备只需要单独部署clock闹钟这1个FA,但需要保证使用同一个华为账号登陆以及同一个网络。
4.总结
到本节为止,多设备的闹钟开发已经告一段落,该工程覆盖了如下知识点:
-
DevEco Studio创建多个har,hap包,最终打包成APP
-
java ui布局
-
基础控件的使用Button,Image,ListContainer,Tablist,Switch
-
使用Hilog输出日志
-
Data Ability关系型数据库的增删改查
-
分布式设备权限配置和设备发现
-
分布式连接ServiceAbility,分布式启动Page Ability
因这是本人第一个HarmonyOs应用,自己也感觉有很多不足之处,希望大家理解:
-
界面简单
-
闹钟时长未实现
-
需求未想清楚:关闭闹钟时,是否需要等每个闹钟都关闭才允许停止,是否要显示已关闭了多少个闹钟。 所有代码归档在github中,欢迎大家拍砖。
5.代码地址:
github,提交记录:9ed91f1e05ade0347a30e2ccbb735899c5bb9c1d