From aa45bb2cdd0993a4404b0aca8aada6ae106a23a6 Mon Sep 17 00:00:00 2001 From: beauty-bag Date: Thu, 13 Nov 2025 10:54:54 +0800 Subject: [PATCH] =?UTF-8?q?=E9=95=BF=E6=97=B6=E4=BB=BB=E5=8A=A1sample?= =?UTF-8?q?=E5=90=8C=E6=BA=90=20Signed-off-by:=20=E8=83=A1=E4=BC=9F=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/ets/MainAbility/BgTaskAbility.ets | 144 +++++++++ .../entry/src/main/ets/pages/Index.ets | 66 ++--- .../src/main/ets/pages/IndexAsyncAndAwait.ets | 278 ++++++++++++++++++ 3 files changed, 454 insertions(+), 34 deletions(-) create mode 100644 BackGroundTasksKit/ContinuousTask/entry/src/main/ets/MainAbility/BgTaskAbility.ets create mode 100644 BackGroundTasksKit/ContinuousTask/entry/src/main/ets/pages/IndexAsyncAndAwait.ets diff --git a/BackGroundTasksKit/ContinuousTask/entry/src/main/ets/MainAbility/BgTaskAbility.ets b/BackGroundTasksKit/ContinuousTask/entry/src/main/ets/MainAbility/BgTaskAbility.ets new file mode 100644 index 00000000000..e6e22b9d6af --- /dev/null +++ b/BackGroundTasksKit/ContinuousTask/entry/src/main/ets/MainAbility/BgTaskAbility.ets @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { backgroundTaskManager } from '@kit.BackgroundTasksKit'; +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { window } from '@kit.ArkUI'; +import { rpc } from '@kit.IPCKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { wantAgent, WantAgent } from '@kit.AbilityKit'; + +// [Start continuous_task_call] +const MSG_SEND_METHOD: string = 'CallSendMsg'; + +let mContext: Context; + +function startContinuousTask() { + let wantAgentInfo : wantAgent.WantAgentInfo = { + // 点击通知后,将要执行的动作列表 + wants: [ + { + bundleName: 'ohos.samples.continuoustask', + abilityName: 'MainAbility' + } + ], + // 点击通知后,动作类型 + actionType: wantAgent.OperationType.START_ABILITY, + // 使用者自定义的一个私有值 + requestCode: 0, + // 点击通知后,动作执行属性 + actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG] + }; + + // 通过wantAgent模块的getWantAgent方法获取WantAgent对象 + // 在原子化服务中,使用wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: object) => {替换下面一行代码 + wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj : WantAgent) => { + backgroundTaskManager.startBackgroundRunning(mContext, + backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, wantAgentObj).then(() => { + console.info(`Succeeded in operationing startBackgroundRunning.`); + }).catch((err: BusinessError) => { + console.error(`Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`); + }); + }); +} + +function stopContinuousTask() { + backgroundTaskManager.stopBackgroundRunning(mContext).then(() => { + console.info(`Succeeded in operationing stopBackgroundRunning.`); + }).catch((err: BusinessError) => { + console.error(`Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`); + }); +} + +class MyParcelable implements rpc.Parcelable { + private num: number = 0; + private str: string = ''; + + constructor(num: number, str: string) { + this.num = num; + this.str = str; + } + + marshalling(messageSequence: rpc.MessageSequence) { + messageSequence.writeInt(this.num); + messageSequence.writeString(this.str); + return true; + } + + unmarshalling(messageSequence: rpc.MessageSequence) { + this.num = messageSequence.readInt(); + this.str = messageSequence.readString(); + return true; + } +} + +function sendMsgCallback(data: rpc.MessageSequence) { + console.info('BgTaskAbility funcCallBack is called ' + data); + let receivedData: MyParcelable = new MyParcelable(0, ''); + data.readParcelable(receivedData); + console.info(`receiveData[${receivedData.num}, ${receivedData.str}]`); + // 可以根据Caller端发送的序列化数据的str值,执行不同的方法。 + if (receivedData.str === 'start_bgtask') { + // 申请长时 + startContinuousTask(); + } else if (receivedData.str === 'stop_bgtask') { + // 取消长时 + stopContinuousTask(); + } + return new MyParcelable(10, 'Callee test'); +} + +export default class BgTaskAbility extends UIAbility { + // Ability创建 + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + console.info('[Demo] BgTaskAbility onCreate'); + try { + this.callee.on(MSG_SEND_METHOD, sendMsgCallback); + } catch (error) { + console.error(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`); + } + mContext = this.context; + } + + // Ability销毁 + onDestroy() { + console.info('[Demo] BgTaskAbility onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + console.info('[Demo] BgTaskAbility onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (error, data) => { + if (error.code) { + console.error(`load content failed with error ${JSON.stringify(error)}`); + return; + } + console.info(`load content succeed with data ${JSON.stringify(data)}`); + }); + } + + onWindowStageDestroy() { + console.info('[Demo] BgTaskAbility onWindowStageDestroy'); + } + + onForeground() { + console.info('[Demo] BgTaskAbility onForeground'); + } + + onBackground() { + console.info('[Demo] BgTaskAbility onBackground'); + } +}; +// [End continuous_task_call] \ No newline at end of file diff --git a/BackGroundTasksKit/ContinuousTask/entry/src/main/ets/pages/Index.ets b/BackGroundTasksKit/ContinuousTask/entry/src/main/ets/pages/Index.ets index e1355d0f512..e3f35134680 100644 --- a/BackGroundTasksKit/ContinuousTask/entry/src/main/ets/pages/Index.ets +++ b/BackGroundTasksKit/ContinuousTask/entry/src/main/ets/pages/Index.ets @@ -17,9 +17,9 @@ import { backgroundTaskManager } from '@kit.BackgroundTasksKit'; import { common } from '@kit.AbilityKit'; import { BusinessError } from '@kit.BasicServicesKit'; import { wantAgent, WantAgent } from '@kit.AbilityKit'; -import PlayerModel from '../feature/BackgroundPlayerFeature' +import PlayerModel from '../feature/BackgroundPlayerFeature'; -// [Start quick_start] +// [Start continuous_task] function callback(info: backgroundTaskManager.ContinuousTaskCancelInfo) { // 长时任务id console.info('OnContinuousTaskCancel callback id ' + info.id); @@ -31,20 +31,20 @@ function callback(info: backgroundTaskManager.ContinuousTaskCancelInfo) { @Component struct Index { @State message: string = 'ContinuousTask'; - @State isSwitching: boolean = false // 通过getUIContext().getHostContext()方法,来获取page所在的UIAbility上下文 private context: Context | undefined = this.getUIContext().getHostContext(); - // [StartExclude quick_start] + // [StartExclude play_music] + @State isSwitching: boolean = false; // 播放或暂停 onPlayClick(): void { if (this.isSwitching) { - console.info(`onPlayClick ignored, isSwitching`) + console.info(`onPlayClick ignored, isSwitching`); return } - console.info(`onPlayClick isPlaying= ${PlayerModel.isPlaying}`) + console.info(`onPlayClick isPlaying= ${PlayerModel.isPlaying}`); if (PlayerModel.isPlaying) { - PlayerModel.pauseMusic() + PlayerModel.pauseMusic(); // cancel continuous task this.stopContinuousTask(); PlayerModel.isPlaying = false; @@ -63,18 +63,18 @@ struct Index { // 上一首 onPreviousClick(): void { if (this.isSwitching) { - console.info(`onPreviousClick ignored, isSwitching`) - return + console.info(`onPreviousClick ignored, isSwitching`); + return; } - console.info(`onPreviousClick`) - PlayerModel.playerIndex-- + console.info(`onPreviousClick`); + PlayerModel.playerIndex--; if (PlayerModel.playerIndex < 0 && PlayerModel.playlist.audioFiles.length >= 1) { - PlayerModel.playerIndex = PlayerModel.playlist.audioFiles.length - 1 + PlayerModel.playerIndex = PlayerModel.playlist.audioFiles.length - 1; } - this.isSwitching = true + this.isSwitching = true; PlayerModel.preLoad(PlayerModel.playerIndex, () => { PlayerModel.playMusic(0, true, this.context as common.UIAbilityContext); - this.isSwitching = false + this.isSwitching = false; PlayerModel.isPlaying = true; if (!this.checkHaveAuidioPlaybackTask()) { this.startContinuousTask(); @@ -85,18 +85,18 @@ struct Index { // 下一首 onNextClick(): void { if (this.isSwitching) { - console.info(`onNextClick ignored, isSwitching`) - return + console.info(`onNextClick ignored, isSwitching`); + return; } - console.info(`onNextClick`) - PlayerModel.playerIndex++ + console.info(`onNextClick`); + PlayerModel.playerIndex++; if (PlayerModel.playerIndex >= PlayerModel.playlist.audioFiles.length) { - PlayerModel.playerIndex = 0 + PlayerModel.playerIndex = 0; } - this.isSwitching = true + this.isSwitching = true; PlayerModel.preLoad(PlayerModel.playerIndex, () => { PlayerModel.playMusic(0, true, this.context as common.UIAbilityContext); - this.isSwitching = false + this.isSwitching = false; PlayerModel.isPlaying = true; if (!this.checkHaveAuidioPlaybackTask()) { this.startContinuousTask(); @@ -132,10 +132,10 @@ struct Index { aboutToAppear() { PlayerModel.getPlaylist(() => { - console.info(`on playlist generated`) + console.info(`on playlist generated`); }) } - // [EndExclude quick_start] + // [EndExclude play_music] OnContinuousTaskCancel() { try { @@ -191,11 +191,11 @@ struct Index { then((res: backgroundTaskManager.ContinuousTaskNotification) => { console.info('Operation startBackgroundRunning succeeded'); // 此处执行具体的长时任务逻辑,如播音等。 - // [StartExclude quick_start] + // [StartExclude apply_play_music] if (!PlayerModel.isPlaying) { - this.onPlayClick() + this.onPlayClick(); } - // [EndExclude quick_start] + // [EndExclude apply_play_music] }).catch((error: BusinessError) => { console.error(`Failed to Operation startBackgroundRunning. code is ${error.code} message is ${error.message}`); }); @@ -212,11 +212,11 @@ struct Index { stopContinuousTask() { backgroundTaskManager.stopBackgroundRunning(this.context).then(() => { console.info(`Succeeded in operationing stopBackgroundRunning.`); - // [StartExclude quick_start] + // [StartExclude stop_play_music] if (PlayerModel.isPlaying) { - this.onPlayClick() + this.onPlayClick(); } - // [EndExclude quick_start] + // [EndExclude stop_play_music] }).catch((err: BusinessError) => { console.error(`Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`); }); @@ -284,11 +284,10 @@ struct Index { // 通过按钮取消注册长时任务取消回调 this.OffContinuousTaskCancel(); }) - + // [StartExclude play_music_ux] Text('播音业务功能') .fontSize(40) .fontWeight(FontWeight.Bold) - Button() { Text('播放/暂停音乐').fontSize(25).fontWeight(FontWeight.Bold) } @@ -301,7 +300,6 @@ struct Index { .onClick(() => { this.onPlayClick(); }) - Button() { Text('上一首').fontSize(25).fontWeight(FontWeight.Bold) } @@ -313,7 +311,6 @@ struct Index { .onClick(() => { this.onPreviousClick(); }) - Button() { Text('下一首').fontSize(25).fontWeight(FontWeight.Bold) } @@ -325,10 +322,11 @@ struct Index { .onClick(() => { this.onNextClick(); }) + // [EndExclude play_music_ux] } .width('100%') } .height('100%') } - // [End quick_start] + // [End continuous_task] } \ No newline at end of file diff --git a/BackGroundTasksKit/ContinuousTask/entry/src/main/ets/pages/IndexAsyncAndAwait.ets b/BackGroundTasksKit/ContinuousTask/entry/src/main/ets/pages/IndexAsyncAndAwait.ets new file mode 100644 index 00000000000..555c3154830 --- /dev/null +++ b/BackGroundTasksKit/ContinuousTask/entry/src/main/ets/pages/IndexAsyncAndAwait.ets @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { backgroundTaskManager } from '@kit.BackgroundTasksKit'; +import { common } from '@kit.AbilityKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { wantAgent, WantAgent } from '@kit.AbilityKit'; +import PlayerModel from '../feature/BackgroundPlayerFeature'; + +// [Start continuous_task_await] +@Entry +@Component +struct IndexAsyncAndAwait { + @State message: string = 'ContinuousTask'; + // 通过getUIContext().getHostContext()方法,来获取page所在的UIAbility上下文 + private context: Context | undefined = this.getUIContext().getHostContext(); + + // [StartExclude play_music] + @State isSwitching: boolean = false; + // 播放或暂停 + onPlayClick(): void { + if (this.isSwitching) { + console.info(`onPlayClick ignored, isSwitching`); + return; + } + console.info(`onPlayClick isPlaying= ${PlayerModel.isPlaying}`); + if (PlayerModel.isPlaying) { + PlayerModel.pauseMusic(); + // cancel continuous task + this.stopContinuousTask(); + PlayerModel.isPlaying = false; + } else { + PlayerModel.preLoad(PlayerModel.playerIndex, () => { + PlayerModel.playMusic(-1, true, this.context as common.UIAbilityContext); + // start continuous task + PlayerModel.isPlaying = true; + if (!this.checkHaveAuidioPlaybackTask()) { + this.startContinuousTask(); + } + }) + } + } + + // 上一首 + onPreviousClick(): void { + if (this.isSwitching) { + console.info(`onPreviousClick ignored, isSwitching`); + return; + } + console.info(`onPreviousClick`); + PlayerModel.playerIndex--; + if (PlayerModel.playerIndex < 0 && PlayerModel.playlist.audioFiles.length >= 1) { + PlayerModel.playerIndex = PlayerModel.playlist.audioFiles.length - 1; + } + this.isSwitching = true; + PlayerModel.preLoad(PlayerModel.playerIndex, () => { + PlayerModel.playMusic(0, true, this.context as common.UIAbilityContext); + this.isSwitching = false; + PlayerModel.isPlaying = true; + if (!this.checkHaveAuidioPlaybackTask()) { + this.startContinuousTask(); + } + }) + } + + // 下一首 + onNextClick(): void { + if (this.isSwitching) { + console.info(`onNextClick ignored, isSwitching`); + return; + } + console.info(`onNextClick`); + PlayerModel.playerIndex++; + if (PlayerModel.playerIndex >= PlayerModel.playlist.audioFiles.length) { + PlayerModel.playerIndex = 0; + } + this.isSwitching = true; + PlayerModel.preLoad(PlayerModel.playerIndex, () => { + PlayerModel.playMusic(0, true, this.context as common.UIAbilityContext); + this.isSwitching = false; + PlayerModel.isPlaying = true; + if (!this.checkHaveAuidioPlaybackTask()) { + this.startContinuousTask(); + } + }) + } + + // 查询当前UIability是否已经申请过特定类型的长时任务 + checkHaveAuidioPlaybackTask(): boolean { + try { + backgroundTaskManager.getAllContinuousTasks(this.context, false). + then((info: backgroundTaskManager.ContinuousTaskInfo[]) => { + console.info(`Operation getAllContinuousTasks succeeded. data: ` + JSON.stringify(info)); + let index: number = 0; + for (index = 0; index < info.length; index++) { + if (info[index].backgroundModes.indexOf('audioPlayback') != 0) { + return true; + } else { + console.info(`current have audioplyback continuous task.`); + } + } + return false; + }).catch((error: BusinessError) => { + console.error(`Operation getAllContinuousTasks failed. code is ${error.code} message is ${error.message}`); + return false; + }); + } catch (error) { + console.error(`Operation getAllContinuousTasks failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`); + return false; + } + return false; + } + + aboutToAppear() { + PlayerModel.getPlaylist(() => { + console.info(`on playlist generated`); + }) + } + // [EndExclude play_music] + + // 申请长时任务async/await写法 + async startContinuousTask() { + let wantAgentInfo: wantAgent.WantAgentInfo = { + // 点击通知后,将要执行的动作列表 + // 添加需要被拉起应用的bundleName和abilityName + wants: [ + { + bundleName: 'ohos.samples.continuoustask', + abilityName: 'MainAbility' + } + ], + // 指定点击通知栏消息后的动作是拉起ability + actionType: wantAgent.OperationType.START_ABILITY, + // 使用者自定义的一个私有值 + requestCode: 0, + // 点击通知后,动作执行属性 + actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG], + // 车钥匙长时任务子类型,从API version 16开始支持。只有申请bluetoothInteraction类型的长时任务,车钥匙子类型才能生效。 + // 确保extraInfo参数中的Key值为backgroundTaskManager.BackgroundModeType.SUB_MODE,否则子类型不生效。 + // extraInfo: { + // [backgroundTaskManager.BackgroundModeType.SUB_MODE] :backgroundTaskManager.BackgroundSubMode.CAR_KEY + // } + }; + + try { + // 通过wantAgent模块下getWantAgent方法获取WantAgent对象 + // 在原子化服务中,使用const wantAgentObj: object = await wantAgent.getWantAgent(wantAgentInfo);替换下面一行代码 + const wantAgentObj: WantAgent = await wantAgent.getWantAgent(wantAgentInfo); + try { + let list: string[] = ['audioPlayback']; + // let list: string[] = ['bluetoothInteraction']; 长时任务类型包含bluetoothInteraction,CAR_KEY子类型合法 + // 在原子化服务中,let list: Array = ["audioPlayback"]; + const res: backgroundTaskManager.ContinuousTaskNotification = + await backgroundTaskManager.startBackgroundRunning(this.context as Context, list, wantAgentObj); + console.info(`Operation startBackgroundRunning succeeded, notificationId: ${res.notificationId}`); + // 此处执行具体的长时任务逻辑,如播音等。 + // [StartExclude apply_play_music] + if (!PlayerModel.isPlaying) { + this.onPlayClick(); + } + // [EndExclude apply_play_music] + } catch (error) { + console.error(`Failed to Operation startBackgroundRunning. Code is ${(error as BusinessError).code}, message is ${(error as BusinessError).message}`); + } + } catch (error) { + console.error(`Failed to Operation getWantAgent. Code is ${(error as BusinessError).code}, message is ${(error as BusinessError).message}`); + } + } + + // 取消长时任务async/await写法 + async stopContinuousTask() { + try { + await backgroundTaskManager.stopBackgroundRunning(this.context); + console.info(`Succeeded in operationing stopBackgroundRunning.`); + // [StartExclude stop_play_music] + if (PlayerModel.isPlaying) { + this.onPlayClick(); + } + // [EndExclude stop_play_music] + } catch (error) { + console.error(`Failed to operation stopBackgroundRunning. Code is ${(error as BusinessError).code}, message is ${(error as BusinessError).message}`) + } + } + + build() { + Row() { + Column() { + Text('Index') + .fontSize(50) + .fontWeight(FontWeight.Bold) + + Button() { + Text('申请长时任务').fontSize(25).fontWeight(FontWeight.Bold) + } + .type(ButtonType.Capsule) + .margin({ top: 10 }) + .backgroundColor('#0D9FFB') + .width(250) + .height(40) + .id('applyContinuousTask') + .onClick(() => { + // 通过按钮申请长时任务 + this.startContinuousTask(); + }) + + Button() { + Text('取消长时任务').fontSize(25).fontWeight(FontWeight.Bold) + } + .type(ButtonType.Capsule) + .margin({ top: 10 }) + .backgroundColor('#0D9FFB') + .width(250) + .height(40) + .id('resetContinuousTask') + .onClick(() => { + // 此处结束具体的长时任务的执行 + + // 通过按钮取消长时任务 + this.stopContinuousTask(); + }) + // [StartExclude play_music_ux] + Text('播音业务功能') + .fontSize(40) + .fontWeight(FontWeight.Bold) + Button() { + Text('播放/暂停音乐').fontSize(25).fontWeight(FontWeight.Bold) + } + .type(ButtonType.Capsule) + .margin({ top: 10 }) + .backgroundColor('#0D9FFB') + .width(250) + .height(40) + .id('playAndPause') + .onClick(() => { + this.onPlayClick(); + }) + Button() { + Text('上一首').fontSize(25).fontWeight(FontWeight.Bold) + } + .type(ButtonType.Capsule) + .margin({ top: 10 }) + .backgroundColor('#0D9FFB') + .width(250) + .height(40) + .onClick(() => { + this.onPreviousClick(); + }) + Button() { + Text('下一首').fontSize(25).fontWeight(FontWeight.Bold) + } + .type(ButtonType.Capsule) + .margin({ top: 10 }) + .backgroundColor('#0D9FFB') + .width(250) + .height(40) + .onClick(() => { + this.onNextClick(); + }) + // [EndExclude play_music_ux] + } + .width('100%') + } + .height('100%') + } + // [End continuous_task_await] +} \ No newline at end of file -- Gitee