0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

鸿蒙HarmonyOS开发实战:【分布式音乐播放】

jf_46214456 来源:jf_46214456 作者:jf_46214456 2024-04-10 17:51 次阅读

分布式音乐播放

介绍

本示例使用fileIo获取指定音频文件,并通过AudioPlayer完成了音乐的播放完成了基本的音乐播放、暂停、上一曲、下一曲功能;并使用DeviceManager完成了分布式设备列表的显示和分布式能力完成了音乐播放状态的跨设备分享。

本示例用到了与用户进行交互的Ability的能力接口[@ohos.ability.featureAbility]

文件存储管理能力接口[@ohos.fileio]

屏幕属性接口[@ohos.display]

媒体查询接口[@ohos.mediaquery]

分布式数据管理接口[@ohos.data.distributedData]

音视频相关媒体业务能力接口[@ohos.multimedia.media]

分布式设备管理能力接口(设备管理),实现设备之间的kvStore对象的数据传输交互[@ohos.distributedDeviceManager]

效果预览

image.png

使用说明

  1. 音乐播放 ,点击 播放暂停 、上 一曲 、下一曲按钮可以对音乐进行操作。
  2. 跨设备分享 ,组网并且双端均已授权条件下,点击分享按钮,选择设备,拉起对端设备上的音乐,并将本端的播放状态同步到对端上。
  3. 跨设备停止分享 ,分享成功前提条件下,点击停止分享按钮,将对端设备拉起的音乐应用停止退出。

相关概念

音频播放:媒体子系统包含了音视频相关媒体业务,通过AudioPlayer实现音频播放的能力。

数据分享:分布式数据管理为应用程序提供不同设备间数据库的分布式协同能力。通过调用分布式数据各个接口,应用程序可将数据保存到分布式数据库中,并可对分布式数据库中的数据进行增/删/改/查等各项操作。

资料文档参考

搜狗高速浏览器截图20240326151450.png

具体实现

在分布式音乐播放器中,分布式设备管理包含了分布式设备搜索、分布式设备列表弹窗、远端设备拉起三部分。
首先在分布式组网内搜索设备,然后把设备展示到分布式设备列表弹窗中,最后根据用户的选择拉起远端设备。

分布式设备搜索

通过SUBSCRIBE_ID搜索分布式组网内的远端设备,详见registerDeviceListCallback(callback) {}模块[源码参考]。

/*

* Copyright (c) 2022 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 deviceManager from '@ohos.distributedDeviceManager';

import Logger from '../model/Logger';



let SUBSCRIBE_ID: number = 100;

const RANDOM: number = 65536;

const TAG: string = 'RemoteDeviceModel';



export class RemoteDeviceModel {

  public deviceLists: Array< deviceManager.DeviceBasicInfo > = [];

  public discoverLists: Array< deviceManager.DeviceBasicInfo > = [];

  private callback: () = > void = null;

  private authCallback: () = > void = null;

  private deviceManager: deviceManager.DeviceManager = undefined;



  registerDeviceListCallback(callback) {

    if (typeof (this.deviceManager) === 'undefined') {

      Logger.info(TAG, 'deviceManager.createDeviceManager begin');

      try {

        this.deviceManager = deviceManager.createDeviceManager('ohos.samples.distributedmusicplayer');

        this.registerDeviceList(callback);

        Logger.info(TAG, `createDeviceManager callback returned, value= ${JSON.stringify(this.deviceManager)}`);

      } catch (error) {

        Logger.info(TAG, `createDeviceManager throw error, error=${error} message=${error.message}`);

      }

      Logger.info(TAG, 'deviceManager.createDeviceManager end');

    } else {

      this.registerDeviceList(callback);

    };

  };



  registerDeviceList(callback) {

    Logger.info(TAG, 'registerDeviceListCallback');

    this.callback = callback;

    if (this.deviceManager === undefined) {

      Logger.error(TAG, 'deviceManager has not initialized');

      this.callback();

      return;

    };



    Logger.info(TAG, 'getTrustedDeviceListSync begin');

    let list: deviceManager.DeviceBasicInfo[] = [];

    try {

      list = this.deviceManager.getAvailableDeviceListSync();

    } catch (error) {

      Logger.info(TAG, `getTrustedDeviceListSync throw error, error=${error} message=${error.message}`);

    };

    Logger.info(TAG, `getTrustedDeviceListSync end, deviceLists= ${JSON.stringify(list)}`);

    if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') {

      this.deviceLists = list;

    };

    this.callback();

    Logger.info(TAG, 'callback finished');



    try {

      this.deviceManager.on('deviceStateChange', (data) = > {

        Logger.info(TAG, `deviceStateChange data= ${JSON.stringify(data)}`);

        switch (data.action) {

          case deviceManager.DeviceStateChange.AVAILABLE:

            this.discoverLists = [];

            this.deviceLists.push(data.device);

            Logger.info(TAG, `reday, updated device list= ${JSON.stringify(this.deviceLists)} `);

            let list: deviceManager.DeviceBasicInfo[] = [];

            try {

              list = this.deviceManager.getAvailableDeviceListSync();

            } catch (err) {

              Logger.info(TAG, `this err is ${JSON.stringify(err)}`);

            }

            Logger.info(TAG, `getTrustedDeviceListSync end, deviceList= ${JSON.stringify(list)}`);

            if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') {

              this.deviceLists = list;

            }

            this.callback();

            break;

          case deviceManager.DeviceStateChange.UNAVAILABLE:

            if (this.deviceLists.length > 0) {

              let list = [];

              for (let i = 0; i < this.deviceLists.length; i++) {

                if (this.deviceLists[i].deviceId !== data.device.deviceId) {

                  list[i] = data.device;

                };

              };

              this.deviceLists = list;

            };

            Logger.info(TAG, `offline, updated device list= ${JSON.stringify(this.deviceLists)}`);

            this.callback();

            break;

          default:

            break;

        };

      });

      this.deviceManager.on('discoverSuccess', (data) = > {

        Logger.info(TAG, `discoverSuccess data= ${JSON.stringify(data)}`);

        Logger.info(TAG, `discoverSuccess this.deviceLists= ${this.deviceLists}, this.deviceLists.length= ${this.deviceLists.length}`);

        for (let i = 0;i < this.discoverLists.length; i++) {

          if (this.discoverLists[i].deviceId === data.device.deviceId) {

            Logger.info(TAG, 'device founded, ignored');

            return;

          };

        };

        this.discoverLists[this.discoverLists.length] = data.device;

        this.callback();

      });

      this.deviceManager.on('discoverFailure', (data) = > {

        Logger.info(TAG, `discoverFailure data= ${JSON.stringify(data)}`);

      });

      this.deviceManager.on('serviceDie', () = > {

        Logger.error(TAG, 'serviceDie');

      });

    } catch (error) {

      Logger.info(TAG, `on throw error, error=${error} message=${error.message}`);

    }



    let discoverParam = {

      'discoverTargetType': 1

    };

    let filterOptions = {

      'availableStatus': 0

    };

    Logger.info(TAG, `startDiscovering ${SUBSCRIBE_ID}`);

    try {

      if (this.deviceManager !== null) {

        this.deviceManager.startDiscovering(discoverParam, filterOptions);

      };

    } catch (error) {

      Logger.error(TAG, `startDiscovering throw error, error=${error} message=${error.message}`);

    };

  };



  authDevice(device, callback) {

    Logger.info(TAG, `authDevice ${device}`);

    if (device !== undefined) {

      for (let i = 0; i < this.discoverLists.length; i++) {

        if (this.discoverLists[i].deviceId === device.deviceId) {

          Logger.info(TAG, 'device founded, ignored');

          let bindParam = {

            bindType: 1,

            targetPkgName: 'ohos.samples.distributedmusicplayer',

            appName: 'Music',

          };

          Logger.info(TAG, `authenticateDevice ${JSON.stringify(this.discoverLists[i])}`);

          try {

            this.deviceManager.bindTarget(device.deviceId, bindParam, (err, data) = > {

              if (err) {

                Logger.error(TAG, `authenticateDevice error: ${JSON.stringify(err)}`);

                this.authCallback = () = > {

                };

                return;

              };

              Logger.info(TAG, `authenticateDevice succeed, data= ${JSON.stringify(data)}`);

              this.authCallback = callback;

            });

          } catch (error) {

            Logger.error(TAG, `authenticateDevice throw error, error=${JSON.stringify(error)} message=${error.message}`);

          }

        }

      }

    }

  };



  unregisterDeviceListCallback() {

    Logger.info(TAG, `stopDiscovering ${SUBSCRIBE_ID}`);

    if (this.deviceManager === undefined) {

      return;

    };

    try {

      this.deviceManager.stopDiscovering();

      this.deviceManager.off('deviceStateChange');

      this.deviceManager.off('discoverSuccess');

      this.deviceManager.off('discoverFailure');

      this.deviceManager.off('serviceDie');

    } catch (error) {

      Logger.info(TAG, `stopDeviceDiscovery throw error, error=${error} message=${error.message}`);

    }

    this.deviceLists = [];

  };

}

分布式设备列表弹窗

使用@CustomDialog弹出分布式设备列表弹窗,参考首页。[源码参考]。

/*

 * Copyright (c) 2022 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 deviceManager from '@ohos.distributedDeviceManager';

import Logger from '../model/Logger';



const TAG: string = 'DeviceDialog';



@CustomDialog

export struct DeviceDialog {

  controller?: CustomDialogController;

  private deviceLists: Array< deviceManager.DeviceBasicInfo > = [];

  private selectedIndex: number = 0;

  private selectedIndexChange: (selectedIndex: number) = > void = () = > {

  };



  build() {

    Column() {

      Text($r('app.string.choiceDevice'))

        .fontSize('32px')

        .width('434px')

        .fontColor(Color.Black)

        .textAlign(TextAlign.Start)

        .fontWeight(600)

      List() {

        ForEach(this.deviceLists, (item: deviceManager.DeviceBasicInfo, index: number | undefined) = > {

          ListItem() {

            Flex({

              direction: FlexDirection.Row,

              justifyContent: FlexAlign.SpaceBetween,

              alignItems: ItemAlign.Center

            }) {

              Text(item.deviceName)

                .fontSize(16)

                .width('86%')

                .fontColor(Color.Black)

                .textAlign(TextAlign.Start)

              Radio({ value: '', group: 'radioGroup' })

                .radioStyle({

                  checkedBackgroundColor: '#ff0d64fb'

                })

                .width('7%')

                .checked(index === this.selectedIndex ? true : false)

            }

            .height(55)

            .onClick(() = > {

              Logger.info(TAG, `select device: ${item.deviceId}`)

              if (index === this.selectedIndex) {

                Logger.info(TAG, 'index === this.selectedIndex')

                return

              }

              this.selectedIndex = index !== undefined ? index : 0

              if (this.controller !== undefined) {

                this.controller.close()

              }

              this.selectedIndexChange(this.selectedIndex)

            })

          }

          .width('434px')

          .height('80px')

        })

      }

      .margin({ top: 12 })

      .width('434px')

      .height('18%')



      Button() {

        Text($r('app.string.cancel'))

          .width('90%')

          .fontSize(21)

          .fontColor('#ff0d64fb')

          .textAlign(TextAlign.Center)

      }

      .margin({ bottom: 16 })

      .type(ButtonType.Capsule)

      .backgroundColor(Color.White)

      .onClick(() = > {

        if (this.controller !== undefined) {

          this.controller.close()

        }

      })

    }

    .margin({ bottom: 36 })

    .width('500px')

    .padding(10)

    .backgroundColor(Color.White)

    .border({ color: Color.White, radius: 20 })

  }

}

远端设备拉起

通过startAbility(deviceId)方法拉起远端设备的包,[源码参考]。

/*

 * Copyright (c) 2022-2023 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 abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';

import display from '@ohos.display';

import common from '@ohos.app.ability.common';

import mediaQuery from '@ohos.mediaquery';

import rpc from '@ohos.rpc';

import Want from '@ohos.app.ability.Want';

import PermissionRequestResult from 'security/PermissionRequestResult';

import KvStoreModel from '../model/KvStoreModel';

import Logger from '../model/Logger';

import PlayerModel from '../model/PlayerModel';

import deviceManager from '@ohos.distributedDeviceManager';

import ability from '@ohos.ability.ability';

import { RemoteDeviceModel } from '../model/RemoteDeviceModel';

import { DeviceDialog } from '../common/DeviceDialog';

import {

  APPLICATION_BUNDLE_NAME,

  APPLICATION_SERVICE_NAME,

  MusicSharedEventCode,

  MusicSharedStatus,

  MusicConnectEvent

} from '../common/MusicSharedDefinition';



const TAG: string = 'Index';

const DESIGN_WIDTH: number = 720.0;

const SYSTEM_UI_HEIGHT: number = 134;

const DESIGN_RATIO: number = 16 / 9;

const ONE_HUNDRED: number = 100;

const ONE_THOUSAND: number = 1000;

const SIXTY: number = 60;

const REMOTE_ABILITY_STARTED: string = 'remoteAbilityStarted';

const ABILITY_SHARED_BUTTON = 0;

const DEFAULT_NUM = -1;

const PREVIOUS_CLICK = 2;



interface Params {

  uri: string,

  seekTo: number,

  isPlaying: boolean

};



@Entry

@Component

 struct Index {

  private listener = mediaQuery.matchMediaSync('screen and (min-aspect-ratio: 1.5) or (orientation: landscape)');

  @State isLand: boolean = false;

  @State currentTimeText: string = '';

  @State currentProgress: number = 0;

  @State totalMs: number = 0;

  @State riscale: number = 1;

  @State risw: number = 720;

  @State rish: number = 1280;

  @State isSwitching: boolean = false;

  @State deviceLists: Array< deviceManager.DeviceBasicInfo > = [];

  @State isDialogShowing: boolean = false;

  @State isDistributed: boolean = false;

  @State title: string = '';

  @State totalTimeText: string = '00:00';

  @State albumSrc: Resource = $r('app.media.album');

  @State selectedIndex: number = 0;

  @State imageArrays: Array< Resource > = [$r('app.media.ic_hop'), $r('app.media.ic_play_previous'), $r('app.media.ic_play'), $r('app.media.ic_play_next')];

  private dialogController: CustomDialogController | null = null;

  @StorageLink('exitMusicApp') @Watch('exitMusicApp') isExitMusicApp: boolean = false;

  @StorageLink('remoteServiceExtensionConnectEvent') @Watch('remoteServiceExtensionConnectEvent') isRemoteServiceExtensionConnectEvent: boolean = false;

  @StorageLink('musicPlay') @Watch('musicPlay') isMusicPlay: boolean = false;

  @StorageLink('musicPause') @Watch('musicPause') isMusicPause: boolean = false;

  private remoteDeviceModel: RemoteDeviceModel = new RemoteDeviceModel();

  private context: common.UIAbilityContext | null = null;

  private deviceId: string | null = null;

  private clickFlag = MusicSharedStatus.MUSIC_SHARED;

  private localExtensionRemote: rpc.IRemoteObject | null = null;

  onLand = (mediaQueryResult: mediaQuery.MediaQueryResult) = > {

    Logger.info(TAG, `onLand: mediaQueryResult.matches= ${mediaQueryResult.matches}`);

    if (mediaQueryResult.matches) {

      this.isLand = true;

    } else {

      this.isLand = false;

    };

  };



  showDialog() {

    this.remoteDeviceModel.registerDeviceListCallback(() = > {

      Logger.info(TAG, 'registerDeviceListCallback, callback entered');

      this.deviceLists = [];

      this.deviceLists.push({

        deviceId: '0',

        deviceName: 'local device',

        deviceType: '0',

        networkId: ''

      });

      let deviceTempList = this.remoteDeviceModel.discoverLists.length > 0 ? this.remoteDeviceModel.discoverLists : this.remoteDeviceModel.deviceLists;

      for (let i = 0; i < deviceTempList.length; i++) {

        Logger.info(TAG, `device ${i}/${deviceTempList.length} deviceId= ${deviceTempList[i].deviceId},

         deviceName= ${deviceTempList[i].deviceName}, deviceType= ${deviceTempList[i].deviceType}`);

        this.deviceLists.push(deviceTempList[i]);

        Logger.info(TAG, 'deviceLists push end');

      };

      Logger.info(TAG, 'CustomDialogController start');

      if (this.dialogController !== null) {

        this.dialogController.close();

        this.dialogController = null;

      }

      this.dialogController = new CustomDialogController({

        builder: DeviceDialog({

          deviceLists: this.deviceLists,

          selectedIndex: this.selectedIndex,

          selectedIndexChange: this.selectedIndexChange

        }),

        autoCancel: true,

        customStyle: true

      });

      this.dialogController.open();

      Logger.info(TAG, 'CustomDialogController end');

    })

  };



  showPromptDialog(title: ResourceStr, str: ResourceStr) {

    AlertDialog.show({

      title: title,

      message: str,

      confirm: {

        value: $r('app.string.cancel'),

        action: () = > {

          Logger.info(TAG, `Button-clicking callback`);

        }

      },

      cancel: () = > {

        Logger.info(TAG, `Closed callbacks`);

      }

    });

  };



  remoteServiceExtensionConnectEvent(event: string) {

    if (typeof (event) === 'string') {

      let viewThis = AppStorage.get< Index >('viewThis');

      if (viewThis !== undefined) {

        if (event === MusicConnectEvent.EVENT_CONNECT) {

          viewThis.clickFlag = MusicSharedStatus.MUSIC_STOP_SHARED;

          viewThis.imageArrays[ABILITY_SHARED_BUTTON] = $r('app.media.ic_hop');

          Logger.info(TAG, 'remote service on connect callbacked');

        } else if (event === MusicConnectEvent.EVENT_DISCONNECT) {

          viewThis.imageArrays[ABILITY_SHARED_BUTTON] = $r('app.media.ic_hop');

          viewThis.clickFlag = MusicSharedStatus.MUSIC_SHARED;

          viewThis.showPromptDialog($r('app.string.ConnectRemoteDevices'), $r('app.string.onDisconnectService'));

        } else if (event === MusicConnectEvent.EVENT_FAILED) {

          viewThis.imageArrays[ABILITY_SHARED_BUTTON] = $r('app.media.ic_hop');

          viewThis.clickFlag = MusicSharedStatus.MUSIC_SHARED;

          viewThis.showPromptDialog($r('app.string.ConnectRemoteDevices'), $r('app.string.onFailedService'));

        } else if (event === MusicConnectEvent.EVENT_TIMEOUT) {

          this.imageArrays[ABILITY_SHARED_BUTTON] = $r('app.media.ic_hop');

          viewThis.clickFlag = MusicSharedStatus.MUSIC_SHARED;

          viewThis.showPromptDialog($r('app.string.ConnectRemoteDevices'), $r('app.string.ConnectionTimeout'));

        }

      }

    } else {

      Logger.info(TAG, 'event is not a string');

    };

  };



  musicPause() {

    Logger.info(TAG, 'music pause recv');

    PlayerModel.pause();

    let viewThis = AppStorage.get< Index >('viewThis');

    viewThis!.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_play');

  };



  musicPlay() {

    Logger.info(TAG, 'music play recv');

    PlayerModel.play(DEFAULT_NUM, true);

    let viewThis = AppStorage.get< Index >('viewThis');

    viewThis!.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_pause');

  };



  exitMusicApp() {

    Logger.info(TAG, `exit music app called`);

    if (this.localExtensionRemote !== null && typeof (this.localExtensionRemote) === 'object') {

      let option = new rpc.MessageOption();

      let data = new rpc.MessageParcel();

      let reply = new rpc.MessageParcel();

      this.localExtensionRemote.sendRequest(

        MusicSharedEventCode.STOP_LOCAL_SERIVCE,

        data,

        reply,

        option);

    } else {

      Logger.info(TAG, `Remote start type is error or deviceID is empty, typeof= ${typeof (this.localExtensionRemote)}`);

    };

  };



  connectLocalExtension() {

    let localServiceWant: Want = {

      bundleName: APPLICATION_BUNDLE_NAME,

      abilityName: APPLICATION_SERVICE_NAME,

    };

    let connectOptions: ability.ConnectOptions = {

      onConnect: (elementName, remote) = > {

        this.localExtensionRemote = remote;

        Logger.info(TAG, `onConnect called elementName is ${JSON.stringify(elementName)}`);

      },

      onDisconnect: (elementName) = > {

        if (this.context !== null) {

          this.context.terminateSelf();

          Logger.info(TAG, `OnDisconnect called elementName is ${JSON.stringify(elementName)}`);

        };

      },

      onFailed: (code) = > {

        if (this.context !== null) {

          this.context.terminateSelf();

          Logger.info(TAG, `OnFailed called code is ${JSON.stringify(code)}`);

        }

      }

    };

    if (this.context !== null) {

      this.context.connectServiceExtensionAbility(localServiceWant, connectOptions);

    };

  };



  startRemoteExtension(deviceId: string, params: object) {

    if (this.localExtensionRemote !== null && typeof (this.localExtensionRemote) === 'object' && typeof (deviceId) === 'string' && deviceId !== '') {

      let option = new rpc.MessageOption();

      let data = new rpc.MessageParcel();

      let reply = new rpc.MessageParcel();

      data.writeString(deviceId);

      data.writeString(JSON.stringify(params));

      this.localExtensionRemote.sendRequest(MusicSharedEventCode.START_DISTRIBUTED_MUSIC_SERVICE, data, reply, option);

      this.deviceId = deviceId;

      this.imageArrays[ABILITY_SHARED_BUTTON] = $r('app.media.ic_hop');

    } else {

      Logger.info(TAG, `Remote start type is error or deviceID is empty, typeof= ${typeof (this.localExtensionRemote)}`);

    };

  };



  stopRemoteExtension() {

    if (this.localExtensionRemote !== null && typeof (this.localExtensionRemote) === 'object' && typeof (this.deviceId) === 'string' && this.deviceId !== '') {

      let option = new rpc.MessageOption();

      let data = new rpc.MessageParcel();

      let reply = new rpc.MessageParcel();

      data.writeString(this.deviceId);

      this.localExtensionRemote.sendRequest(MusicSharedEventCode.STOP_DISTRIBUTED_MUSIC_SERVICE, data, reply, option);

      this.deviceId = '';

    } else {

      Logger.info(TAG, `Remote stopped type is wrong or deviceID is empty, typeof= ${typeof (this.localExtensionRemote)}`);

    };

  };



  sendMessagePlay() {

    if (this.localExtensionRemote !== null) {

      let option = new rpc.MessageOption();

      let data = new rpc.MessageParcel();

      let reply = new rpc.MessageParcel();

      this.localExtensionRemote.sendRequest(MusicSharedEventCode.PLAY_MUSIC_SERVICE, data, reply, option);

      Logger.info(TAG, `onPlayClick send mssage success`);

    } else {

      Logger.info(TAG, `can not get proxy`);

      return;

    };

  };



  sendMessagePause() {

    if (this.localExtensionRemote === null) {

      Logger.info(TAG, `can not get proxy`);

      return;

    };

    let option = new rpc.MessageOption();

    let data = new rpc.MessageParcel();

    let reply = new rpc.MessageParcel();



    this.localExtensionRemote.sendRequest(MusicSharedEventCode.PAUSE_MUSIC_SERVICE, data, reply, option);

    Logger.info(TAG, `onPauseClick send mssage success`);

  };



  onBackPress() {

    if (this.isDialogShowing === true) {

      this.dismissDialog();

      return true;

    };

    return false;

  };



  onPageHide() {

    if (this.isDialogShowing === true) {

      this.dismissDialog();

      return true;

    };

    return false;

  };



  dismissDialog() {

    if (this.dialogController !== null) {

      this.dialogController.close();

    }

    this.remoteDeviceModel.unregisterDeviceListCallback();

    this.isDialogShowing = false;

  };



  startAbilityContinuation(deviceId: string) {

    let params: Params = {

      uri: '',

      seekTo: 0,

      isPlaying: false

    };

    Logger.info(TAG, `startAbilityContinuation PlayerModel.index= ${PlayerModel.index}/${PlayerModel.playlist.audioFiles.length}`);

    if (PlayerModel.index >= 0 && PlayerModel.index <= PlayerModel.playlist.audioFiles.length) {

      params = {

        uri: PlayerModel.playlist.audioFiles[PlayerModel.index].fileUri,

        seekTo: PlayerModel.getCurrentMs(),

        isPlaying: PlayerModel.isPlaying

      };

    };

    Logger.info(TAG, `context.startAbility deviceId= ${deviceId}`);

    if (this.context !== null) {

      KvStoreModel.setOnMessageReceivedListener(this.context, REMOTE_ABILITY_STARTED, () = > {

        Logger.info(TAG, 'OnMessageReceived, terminateSelf');

      });

    };

    Logger.info(TAG, `context.startAbility start`);

    this.clickFlag = MusicSharedStatus.MUSIC_REMOTING;

    this.startRemoteExtension(deviceId, params);

    this.clearSelectState();

    Logger.info(TAG, 'context.startAbility end');

  };



  selectedIndexChange = (selectedIndex: number) = > {

    if (this.context !== null && selectedIndex === 0) {

      this.context.startAbility({ bundleName: 'ohos.samples.distributedmusicplayer',

        abilityName: 'ohos.samples.distributedmusicplayer.MainAbility',

        deviceId: this.deviceLists[selectedIndex].deviceId,

        parameters: {

          isFA: 'EXIT'

        }

      }).then(() = > {

        Logger.info(TAG, `startAbility finished`);

      }).catch((err: Error) = > {

        Logger.info(TAG, `startAbility filed error = ${JSON.stringify(err)}`);

      });

      this.isDistributed = false;

      this.selectedIndex = 0;

      if (this.dialogController !== null) {

        this.dialogController.close();

      }

      this.deviceLists = [];

      return;

    };

    this.selectedIndex = selectedIndex;

    this.selectDevice();

  };



  selectDevice() {

    Logger.info(TAG, 'start ability ......');

    if (this.selectedIndex !== undefined && (this.remoteDeviceModel === null || this.remoteDeviceModel.discoverLists.length <= 0)) {

      Logger.info(TAG, `start ability device:${JSON.stringify(this.deviceLists)}`);

      this.startAbilityContinuation(this.deviceLists[this.selectedIndex].networkId as string);

      this.clearSelectState();

      return;

    };

    Logger.info(TAG, 'start ability, needAuth');

    if (this.selectedIndex !== undefined){

      this.remoteDeviceModel.authDevice(this.deviceLists[this.selectedIndex], (device: deviceManager.DeviceBasicInfo) = > {

        Logger.info(TAG, 'auth and online finished');

        this.startAbilityContinuation(device.networkId);

      });

    }

    Logger.info(TAG, 'start ability2 ......');

    this.clearSelectState();

  };



  clearSelectState() {

    this.deviceLists = [];

    if (this.dialogController) {

      this.dialogController.close();

      this.dialogController = null;

    };

  };



  getShownTimer(ms: number) {

    let minStr: string;

    let secStr: string;

    let seconds = Math.floor(ms / ONE_THOUSAND);

    let sec = seconds % SIXTY;

    Logger.info(TAG, `getShownTimer sec = ${sec}`);

    let min = (seconds - sec) / SIXTY;

    Logger.info(TAG, `getShownTimer min = ${min}`);

    if (sec < 10) {

      secStr = '0' + sec;

    } else {

      secStr = sec.toString(10);

    };

    if (min < 10) {

      minStr = '0' + min;

    } else {

      minStr = min.toString(10);

    };

    Logger.warn(TAG, `getShownTimer = ${minStr}:${secStr}`);

    return minStr + ':' + secStr;

  };



  refreshSongInfo(index: number) {

    Logger.info(TAG, `refreshSongInfo ${index}/${PlayerModel.playlist.audioFiles.length}`);

    if (index >= PlayerModel.playlist.audioFiles.length) {

      Logger.warn(TAG, 'refreshSongInfo ignored');

      return;

    };

    // update song title

    this.title = PlayerModel.playlist.audioFiles[index].name;

    this.albumSrc = (index % 2 === 0) ? $r('app.media.album') : $r('app.media.album2');



    // update duration

    this.totalMs = PlayerModel.getDuration();

    this.totalTimeText = this.getShownTimer(this.totalMs);

    this.currentTimeText = this.getShownTimer(PlayerModel.getCurrentMs());

    Logger.info(TAG, `refreshSongInfo this.title= ${this.title}, this.totalMs= ${this.totalMs}, this.totalTimeText= ${this.totalTimeText},this.currentTimeText= ${this.currentTimeText}`);

  };



  onAppSharedClick() {

    if (this.clickFlag === MusicSharedStatus.MUSIC_SHARED) {

      Logger.info(TAG, `1start button is ${JSON.stringify(this.imageArrays[ABILITY_SHARED_BUTTON])}`);

      this.showDialog();

    } else if (this.clickFlag === MusicSharedStatus.MUSIC_STOP_SHARED) {

      Logger.info(TAG, `2start button is ${JSON.stringify(this.imageArrays[ABILITY_SHARED_BUTTON])}`);

      this.stopRemoteExtension();

      this.imageArrays[ABILITY_SHARED_BUTTON] = $r('app.media.ic_hop');

    };

  };



  onPreviousClick() {

    if (this.isSwitching) {

      Logger.info(TAG, 'onPreviousClick ignored, isSwitching');

      return;

    };

    Logger.info(TAG, 'onPreviousClick');

    PlayerModel.index--;

    if (PlayerModel.index < 0 && PlayerModel.playlist.audioFiles.length >= 1) {

      PlayerModel.index = PlayerModel.playlist.audioFiles.length - 1;

    };

    this.currentProgress = 0;

    this.isSwitching = true;



    PlayerModel.preLoad(PlayerModel.index, () = > {

      this.refreshSongInfo(PlayerModel.index);

      PlayerModel.play(0, true);

      if (PlayerModel.isPlaying) {

        this.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_pause');

      };

      this.isSwitching = false;

    });

  };



  onNextClick() {

    if (this.isSwitching) {

      Logger.info(TAG, 'onNextClick ignored, isSwitching');

      return;

    };

    Logger.info(TAG, 'onNextClick');

    PlayerModel.index++;

    if (PlayerModel.index >= PlayerModel.playlist.audioFiles.length) {

      PlayerModel.index = 0;

    };

    this.currentProgress = 0;

    this.isSwitching = true;

    PlayerModel.preLoad(PlayerModel.index, () = > {

      this.refreshSongInfo(PlayerModel.index);

      PlayerModel.play(0, true);

      if (PlayerModel.isPlaying) {

        this.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_pause');

      };

      this.isSwitching = false;

    });

  };



  onPlayClick() {

    if (this.isSwitching) {

      Logger.info(TAG, 'onPlayClick ignored, isSwitching');

      return;

    };

    Logger.info(TAG, `onPlayClick isPlaying= ${PlayerModel.isPlaying}`);

    if (PlayerModel.isPlaying) {

      PlayerModel.pause();

      this.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_play');

      this.sendMessagePause();

    } else {

      PlayerModel.preLoad(PlayerModel.index, () = > {

        PlayerModel.play(DEFAULT_NUM, true);

        this.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_pause');

        this.sendMessagePlay();

      })

    };

  };



  restoreFromWant() {

    Logger.info(TAG, 'restoreFromWant');

    let status: Record< string, Object > | undefined = AppStorage.get('status');

    if (status !== undefined && status !== null && status.uri !== null) {

      KvStoreModel.broadcastMessage(this.context, REMOTE_ABILITY_STARTED);

      Logger.info(TAG, 'restorePlayingStatus');

      PlayerModel.restorePlayingStatus(status, (index: number) = > {

        Logger.info(TAG, `restorePlayingStatus finished, index= ${index}`);

        if (index >= 0) {

          this.refreshSongInfo(index);

        } else {

          PlayerModel.preLoad(0, () = > {

            this.refreshSongInfo(0);

          })

        }

        if (status !== undefined) {

          Logger.info(TAG, `Index PlayerModel.restorePlayingStatus this.totalMs = ${this.totalMs}, status.seekTo = ${status.seekTo}`);

          this.currentProgress = Math.floor(Number(status.seekTo) / this.totalMs * ONE_HUNDRED);

        }

      })

    } else {

      PlayerModel.preLoad(0, () = > {

        this.refreshSongInfo(0);

      });

    }

  };



  aboutToAppear() {

    Logger.info(TAG, `begin`);

    Logger.info(TAG, 'grantPermission');

    this.context = getContext(this) as common.UIAbilityContext;

    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();

    let permission: Array< Permissions > = ['ohos.permission.DISTRIBUTED_DATASYNC'];

    try {

      atManager.requestPermissionsFromUser(this.context, permission).then((data: PermissionRequestResult) = > {

        Logger.info(TAG, `data: ${JSON.stringify(data)}`);

      }).catch((err: object) = > {

        Logger.info(TAG, `err: ${JSON.stringify(err)}`);

      })

    } catch (err) {

      Logger.info(TAG, `catch err- >${JSON.stringify(err)}`);

    }

    display.getDefaultDisplay().then((dis: display.Display) = > {

      Logger.info(TAG, `getDefaultDisplay dis= ${JSON.stringify(dis)}`);

      let proportion = DESIGN_WIDTH / dis.width;

      let screenWidth = DESIGN_WIDTH;

      let screenHeight = (dis.height - SYSTEM_UI_HEIGHT) * proportion;

      this.riscale = (screenHeight / screenWidth) / DESIGN_RATIO;

      if (this.riscale < 1) {

        // The screen ratio is shorter than design ratio

        this.risw = screenWidth * this.riscale;

        this.rish = screenHeight;

      } else {

        // The screen ratio is longer than design ratio

        this.risw = screenWidth;

        this.rish = screenHeight / this.riscale;

      }

      Logger.info(TAG, `proportion=${proportion} , screenWidth= ${screenWidth},

      screenHeight= ${screenHeight} , riscale= ${this.riscale} , risw= ${this.risw} , rish= ${this.rish}`);

    })

    Logger.info(TAG, 'getDefaultDisplay end');

    this.currentTimeText = this.getShownTimer(0);

    PlayerModel.setOnStatusChangedListener((isPlaying: string) = > {

      Logger.info(TAG, `on player status changed, isPlaying= ${isPlaying} refresh ui`);

      PlayerModel.setOnPlayingProgressListener((currentTimeMs: number) = > {

        this.currentTimeText = this.getShownTimer(currentTimeMs);

        this.currentProgress = Math.floor(currentTimeMs / this.totalMs * ONE_HUNDRED);

      });

      if (isPlaying) {

        this.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_pause');

      } else {

        this.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_play');

      }

    });



    PlayerModel.getPlaylist(() = > {

      Logger.info(TAG, 'on playlist generated, refresh ui');

      this.restoreFromWant();

    });



    AppStorage.setOrCreate('viewThis', this);



    this.connectLocalExtension();

  };



  aboutToDisappear() {

    Logger.info(TAG, `aboutToDisappear begin`)

    if (PlayerModel === undefined) {

      return

    }

    PlayerModel.release()

    this.remoteDeviceModel.unregisterDeviceListCallback()

    this.dialogController = null

    KvStoreModel.deleteKvStore()

    Logger.info(TAG, `aboutToDisappear end`)

  };



  build() {

    Column() {

      Blank()

        .width('100%')

        .height(72)

      Text(this.title)

        .width('100%')

        .fontSize(28)

        .margin({ top: '10%' })

        .fontColor(Color.White)

        .textAlign(TextAlign.Center)

      Image(this.albumSrc)

        .width(this.isLand ? '60%' : '89%')

        .objectFit(ImageFit.Contain)

        .margin({ top: 50, left: 40, right: 40 })

      Row() {

        Text(this.currentTimeText)

          .fontSize(20)

          .fontColor(Color.White)

        Blank()

        Text(this.totalTimeText)

          .fontSize(20)

          .fontColor(Color.White)

      }

      .width('90%')

      .margin({ top: '12%' })



      Slider({ value: typeof (this.currentProgress) === 'number' ? this.currentProgress : 0 })

        .trackColor('#64CCE7FF')

        .width('90%')

        .selectedColor('#ff0c4ae7')

        .onChange((value: number, mode: SliderChangeMode) = > {

          this.currentProgress = value;

          if (typeof (this.totalMs) !== 'number') {

            this.currentProgress = 0;

            Logger.info(TAG, `setProgress ignored, totalMs= ${this.totalMs}`);

            return;

          };

          let currentMs = this.currentProgress / ONE_HUNDRED * this.totalMs;

          this.currentTimeText = this.getShownTimer(currentMs);

          if (mode === SliderChangeMode.End || mode === 3) {

            Logger.info(TAG, `player.seek= ${currentMs}`);

            PlayerModel.seek(currentMs);

          };

        })



      Row() {

        ForEach(this.imageArrays, (item: Resource, index: number | undefined) = > {

          Column() {

            Image(item)

              .size({ width: 74, height: 74 })

              .objectFit(ImageFit.Contain)

              .onClick(() = > {

                switch (index) {

                  case 0:

                    this.onAppSharedClick();

                    break;

                  case 1:

                    this.onPreviousClick();

                    break;

                  case 2:

                    this.onPlayClick();

                    break;

                  case 3:

                    this.onNextClick();

                    break;

                  default:

                    break;

                }

              })

          }

          .id('image' + (index !== undefined ? (index + 1) : 0))

          .width(100)

          .height(100)

          .alignItems(HorizontalAlign.Center)

          .justifyContent(FlexAlign.Center)

        })



      }

      .width('100%')

      .margin({ top: '4%' })

      .justifyContent(FlexAlign.SpaceEvenly)

    }

    .width('100%')

    .height('100%')

    .backgroundImage($r('app.media.bg_blurry'))

    .backgroundImageSize({ width: '100%', height: '100%' })

  }

}

分布式数据管理

(1) 管理分布式数据库
创建一个KVManager对象实例,用于管理分布式数据库对象。通过distributedData.createKVManager(config),并通过指定Options和storeId,创建并获取KVStore数据库,并通过Promise方式返回,此方法为异步方法,例如this.kvManager.getKVStore(STORE_ID, options).then((store) => {})
(2) 订阅分布式数据变化
通过订阅分布式数据库所有(本地及远端)数据变化实现数据协同[源码参考]。

/*

 * Copyright (c) 2022-2023 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 abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';

import display from '@ohos.display';

import common from '@ohos.app.ability.common';

import mediaQuery from '@ohos.mediaquery';

import rpc from '@ohos.rpc';

import Want from '@ohos.app.ability.Want';

import PermissionRequestResult from 'security/PermissionRequestResult';

import KvStoreModel from '../model/KvStoreModel';

import Logger from '../model/Logger';

import PlayerModel from '../model/PlayerModel';

import deviceManager from '@ohos.distributedDeviceManager';

import ability from '@ohos.ability.ability';

import { RemoteDeviceModel } from '../model/RemoteDeviceModel';

import { DeviceDialog } from '../common/DeviceDialog';

import {

  APPLICATION_BUNDLE_NAME,

  APPLICATION_SERVICE_NAME,

  MusicSharedEventCode,

  MusicSharedStatus,

  MusicConnectEvent

} from '../common/MusicSharedDefinition';



const TAG: string = 'Index';

const DESIGN_WIDTH: number = 720.0;

const SYSTEM_UI_HEIGHT: number = 134;

const DESIGN_RATIO: number = 16 / 9;

const ONE_HUNDRED: number = 100;

const ONE_THOUSAND: number = 1000;

const SIXTY: number = 60;

const REMOTE_ABILITY_STARTED: string = 'remoteAbilityStarted';

const ABILITY_SHARED_BUTTON = 0;

const DEFAULT_NUM = -1;

const PREVIOUS_CLICK = 2;



interface Params {

  uri: string,

  seekTo: number,

  isPlaying: boolean

};



@Entry

@Component

 struct Index {

  private listener = mediaQuery.matchMediaSync('screen and (min-aspect-ratio: 1.5) or (orientation: landscape)');

  @State isLand: boolean = false;

  @State currentTimeText: string = '';

  @State currentProgress: number = 0;

  @State totalMs: number = 0;

  @State riscale: number = 1;

  @State risw: number = 720;

  @State rish: number = 1280;

  @State isSwitching: boolean = false;

  @State deviceLists: Array< deviceManager.DeviceBasicInfo > = [];

  @State isDialogShowing: boolean = false;

  @State isDistributed: boolean = false;

  @State title: string = '';

  @State totalTimeText: string = '00:00';

  @State albumSrc: Resource = $r('app.media.album');

  @State selectedIndex: number = 0;

  @State imageArrays: Array< Resource > = [$r('app.media.ic_hop'), $r('app.media.ic_play_previous'), $r('app.media.ic_play'), $r('app.media.ic_play_next')];

  private dialogController: CustomDialogController | null = null;

  @StorageLink('exitMusicApp') @Watch('exitMusicApp') isExitMusicApp: boolean = false;

  @StorageLink('remoteServiceExtensionConnectEvent') @Watch('remoteServiceExtensionConnectEvent') isRemoteServiceExtensionConnectEvent: boolean = false;

  @StorageLink('musicPlay') @Watch('musicPlay') isMusicPlay: boolean = false;

  @StorageLink('musicPause') @Watch('musicPause') isMusicPause: boolean = false;

  private remoteDeviceModel: RemoteDeviceModel = new RemoteDeviceModel();

  private context: common.UIAbilityContext | null = null;

  private deviceId: string | null = null;

  private clickFlag = MusicSharedStatus.MUSIC_SHARED;

  private localExtensionRemote: rpc.IRemoteObject | null = null;

  onLand = (mediaQueryResult: mediaQuery.MediaQueryResult) = > {

    Logger.info(TAG, `onLand: mediaQueryResult.matches= ${mediaQueryResult.matches}`);

    if (mediaQueryResult.matches) {

      this.isLand = true;

    } else {

      this.isLand = false;

    };

  };



  showDialog() {

    this.remoteDeviceModel.registerDeviceListCallback(() = > {

      Logger.info(TAG, 'registerDeviceListCallback, callback entered');

      this.deviceLists = [];

      this.deviceLists.push({

        deviceId: '0',

        deviceName: 'local device',

        deviceType: '0',

        networkId: ''

      });

      let deviceTempList = this.remoteDeviceModel.discoverLists.length > 0 ? this.remoteDeviceModel.discoverLists : this.remoteDeviceModel.deviceLists;

      for (let i = 0; i < deviceTempList.length; i++) {

        Logger.info(TAG, `device ${i}/${deviceTempList.length} deviceId= ${deviceTempList[i].deviceId},

         deviceName= ${deviceTempList[i].deviceName}, deviceType= ${deviceTempList[i].deviceType}`);

        this.deviceLists.push(deviceTempList[i]);

        Logger.info(TAG, 'deviceLists push end');

      };

      Logger.info(TAG, 'CustomDialogController start');

      if (this.dialogController !== null) {

        this.dialogController.close();

        this.dialogController = null;

      }

      this.dialogController = new CustomDialogController({

        builder: DeviceDialog({

          deviceLists: this.deviceLists,

          selectedIndex: this.selectedIndex,

          selectedIndexChange: this.selectedIndexChange

        }),

        autoCancel: true,

        customStyle: true

      });

      this.dialogController.open();

      Logger.info(TAG, 'CustomDialogController end');

    })

  };



  showPromptDialog(title: ResourceStr, str: ResourceStr) {

    AlertDialog.show({

      title: title,

      message: str,

      confirm: {

        value: $r('app.string.cancel'),

        action: () = > {

          Logger.info(TAG, `Button-clicking callback`);

        }

      },

      cancel: () = > {

        Logger.info(TAG, `Closed callbacks`);

      }

    });

  };



  remoteServiceExtensionConnectEvent(event: string) {

    if (typeof (event) === 'string') {

      let viewThis = AppStorage.get< Index >('viewThis');

      if (viewThis !== undefined) {

        if (event === MusicConnectEvent.EVENT_CONNECT) {

          viewThis.clickFlag = MusicSharedStatus.MUSIC_STOP_SHARED;

          viewThis.imageArrays[ABILITY_SHARED_BUTTON] = $r('app.media.ic_hop');

          Logger.info(TAG, 'remote service on connect callbacked');

        } else if (event === MusicConnectEvent.EVENT_DISCONNECT) {

          viewThis.imageArrays[ABILITY_SHARED_BUTTON] = $r('app.media.ic_hop');

          viewThis.clickFlag = MusicSharedStatus.MUSIC_SHARED;

          viewThis.showPromptDialog($r('app.string.ConnectRemoteDevices'), $r('app.string.onDisconnectService'));

        } else if (event === MusicConnectEvent.EVENT_FAILED) {

          viewThis.imageArrays[ABILITY_SHARED_BUTTON] = $r('app.media.ic_hop');

          viewThis.clickFlag = MusicSharedStatus.MUSIC_SHARED;

          viewThis.showPromptDialog($r('app.string.ConnectRemoteDevices'), $r('app.string.onFailedService'));

        } else if (event === MusicConnectEvent.EVENT_TIMEOUT) {

          this.imageArrays[ABILITY_SHARED_BUTTON] = $r('app.media.ic_hop');

          viewThis.clickFlag = MusicSharedStatus.MUSIC_SHARED;

          viewThis.showPromptDialog($r('app.string.ConnectRemoteDevices'), $r('app.string.ConnectionTimeout'));

        }

      }

    } else {

      Logger.info(TAG, 'event is not a string');

    };

  };



  musicPause() {

    Logger.info(TAG, 'music pause recv');

    PlayerModel.pause();

    let viewThis = AppStorage.get< Index >('viewThis');

    viewThis!.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_play');

  };



  musicPlay() {

    Logger.info(TAG, 'music play recv');

    PlayerModel.play(DEFAULT_NUM, true);

    let viewThis = AppStorage.get< Index >('viewThis');

    viewThis!.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_pause');

  };



  exitMusicApp() {

    Logger.info(TAG, `exit music app called`);

    if (this.localExtensionRemote !== null && typeof (this.localExtensionRemote) === 'object') {

      let option = new rpc.MessageOption();

      let data = new rpc.MessageParcel();

      let reply = new rpc.MessageParcel();

      this.localExtensionRemote.sendRequest(

        MusicSharedEventCode.STOP_LOCAL_SERIVCE,

        data,

        reply,

        option);

    } else {

      Logger.info(TAG, `Remote start type is error or deviceID is empty, typeof= ${typeof (this.localExtensionRemote)}`);

    };

  };



  connectLocalExtension() {

    let localServiceWant: Want = {

      bundleName: APPLICATION_BUNDLE_NAME,

      abilityName: APPLICATION_SERVICE_NAME,

    };

    let connectOptions: ability.ConnectOptions = {

      onConnect: (elementName, remote) = > {

        this.localExtensionRemote = remote;

        Logger.info(TAG, `onConnect called elementName is ${JSON.stringify(elementName)}`);

      },

      onDisconnect: (elementName) = > {

        if (this.context !== null) {

          this.context.terminateSelf();

          Logger.info(TAG, `OnDisconnect called elementName is ${JSON.stringify(elementName)}`);

        };

      },

      onFailed: (code) = > {

        if (this.context !== null) {

          this.context.terminateSelf();

          Logger.info(TAG, `OnFailed called code is ${JSON.stringify(code)}`);

        }

      }

    };

    if (this.context !== null) {

      this.context.connectServiceExtensionAbility(localServiceWant, connectOptions);

    };

  };



  startRemoteExtension(deviceId: string, params: object) {

    if (this.localExtensionRemote !== null && typeof (this.localExtensionRemote) === 'object' && typeof (deviceId) === 'string' && deviceId !== '') {

      let option = new rpc.MessageOption();

      let data = new rpc.MessageParcel();

      let reply = new rpc.MessageParcel();

      data.writeString(deviceId);

      data.writeString(JSON.stringify(params));

      this.localExtensionRemote.sendRequest(MusicSharedEventCode.START_DISTRIBUTED_MUSIC_SERVICE, data, reply, option);

      this.deviceId = deviceId;

      this.imageArrays[ABILITY_SHARED_BUTTON] = $r('app.media.ic_hop');

    } else {

      Logger.info(TAG, `Remote start type is error or deviceID is empty, typeof= ${typeof (this.localExtensionRemote)}`);

    };

  };



  stopRemoteExtension() {

    if (this.localExtensionRemote !== null && typeof (this.localExtensionRemote) === 'object' && typeof (this.deviceId) === 'string' && this.deviceId !== '') {

      let option = new rpc.MessageOption();

      let data = new rpc.MessageParcel();

      let reply = new rpc.MessageParcel();

      data.writeString(this.deviceId);

      this.localExtensionRemote.sendRequest(MusicSharedEventCode.STOP_DISTRIBUTED_MUSIC_SERVICE, data, reply, option);

      this.deviceId = '';

    } else {

      Logger.info(TAG, `Remote stopped type is wrong or deviceID is empty, typeof= ${typeof (this.localExtensionRemote)}`);

    };

  };



  sendMessagePlay() {

    if (this.localExtensionRemote !== null) {

      let option = new rpc.MessageOption();

      let data = new rpc.MessageParcel();

      let reply = new rpc.MessageParcel();

      this.localExtensionRemote.sendRequest(MusicSharedEventCode.PLAY_MUSIC_SERVICE, data, reply, option);

      Logger.info(TAG, `onPlayClick send mssage success`);

    } else {

      Logger.info(TAG, `can not get proxy`);

      return;

    };

  };



  sendMessagePause() {

    if (this.localExtensionRemote === null) {

      Logger.info(TAG, `can not get proxy`);

      return;

    };

    let option = new rpc.MessageOption();

    let data = new rpc.MessageParcel();

    let reply = new rpc.MessageParcel();



    this.localExtensionRemote.sendRequest(MusicSharedEventCode.PAUSE_MUSIC_SERVICE, data, reply, option);

    Logger.info(TAG, `onPauseClick send mssage success`);

  };



  onBackPress() {

    if (this.isDialogShowing === true) {

      this.dismissDialog();

      return true;

    };

    return false;

  };



  onPageHide() {

    if (this.isDialogShowing === true) {

      this.dismissDialog();

      return true;

    };

    return false;

  };



  dismissDialog() {

    if (this.dialogController !== null) {

      this.dialogController.close();

    }

    this.remoteDeviceModel.unregisterDeviceListCallback();

    this.isDialogShowing = false;

  };



  startAbilityContinuation(deviceId: string) {

    let params: Params = {

      uri: '',

      seekTo: 0,

      isPlaying: false

    };

    Logger.info(TAG, `startAbilityContinuation PlayerModel.index= ${PlayerModel.index}/${PlayerModel.playlist.audioFiles.length}`);

    if (PlayerModel.index >= 0 && PlayerModel.index <= PlayerModel.playlist.audioFiles.length) {

      params = {

        uri: PlayerModel.playlist.audioFiles[PlayerModel.index].fileUri,

        seekTo: PlayerModel.getCurrentMs(),

        isPlaying: PlayerModel.isPlaying

      };

    };

    Logger.info(TAG, `context.startAbility deviceId= ${deviceId}`);

    if (this.context !== null) {

      KvStoreModel.setOnMessageReceivedListener(this.context, REMOTE_ABILITY_STARTED, () = > {

        Logger.info(TAG, 'OnMessageReceived, terminateSelf');

      });

    };

    Logger.info(TAG, `context.startAbility start`);

    this.clickFlag = MusicSharedStatus.MUSIC_REMOTING;

    this.startRemoteExtension(deviceId, params);

    this.clearSelectState();

    Logger.info(TAG, 'context.startAbility end');

  };



  selectedIndexChange = (selectedIndex: number) = > {

    if (this.context !== null && selectedIndex === 0) {

      this.context.startAbility({ bundleName: 'ohos.samples.distributedmusicplayer',

        abilityName: 'ohos.samples.distributedmusicplayer.MainAbility',

        deviceId: this.deviceLists[selectedIndex].deviceId,

        parameters: {

          isFA: 'EXIT'

        }

      }).then(() = > {

        Logger.info(TAG, `startAbility finished`);

      }).catch((err: Error) = > {

        Logger.info(TAG, `startAbility filed error = ${JSON.stringify(err)}`);

      });

      this.isDistributed = false;

      this.selectedIndex = 0;

      if (this.dialogController !== null) {

        this.dialogController.close();

      }

      this.deviceLists = [];

      return;

    };

    this.selectedIndex = selectedIndex;

    this.selectDevice();

  };



  selectDevice() {

    Logger.info(TAG, 'start ability ......');

    if (this.selectedIndex !== undefined && (this.remoteDeviceModel === null || this.remoteDeviceModel.discoverLists.length <= 0)) {

      Logger.info(TAG, `start ability device:${JSON.stringify(this.deviceLists)}`);

      this.startAbilityContinuation(this.deviceLists[this.selectedIndex].networkId as string);

      this.clearSelectState();

      return;

    };

    Logger.info(TAG, 'start ability, needAuth');

    if (this.selectedIndex !== undefined){

      this.remoteDeviceModel.authDevice(this.deviceLists[this.selectedIndex], (device: deviceManager.DeviceBasicInfo) = > {

        Logger.info(TAG, 'auth and online finished');

        this.startAbilityContinuation(device.networkId);

      });

    }

    Logger.info(TAG, 'start ability2 ......');

    this.clearSelectState();

  };



  clearSelectState() {

    this.deviceLists = [];

    if (this.dialogController) {

      this.dialogController.close();

      this.dialogController = null;

    };

  };



  getShownTimer(ms: number) {

    let minStr: string;

    let secStr: string;

    let seconds = Math.floor(ms / ONE_THOUSAND);

    let sec = seconds % SIXTY;

    Logger.info(TAG, `getShownTimer sec = ${sec}`);

    let min = (seconds - sec) / SIXTY;

    Logger.info(TAG, `getShownTimer min = ${min}`);

    if (sec < 10) {

      secStr = '0' + sec;

    } else {

      secStr = sec.toString(10);

    };

    if (min < 10) {

      minStr = '0' + min;

    } else {

      minStr = min.toString(10);

    };

    Logger.warn(TAG, `getShownTimer = ${minStr}:${secStr}`);

    return minStr + ':' + secStr;

  };



  refreshSongInfo(index: number) {

    Logger.info(TAG, `refreshSongInfo ${index}/${PlayerModel.playlist.audioFiles.length}`);

    if (index >= PlayerModel.playlist.audioFiles.length) {

      Logger.warn(TAG, 'refreshSongInfo ignored');

      return;

    };

    // update song title

    this.title = PlayerModel.playlist.audioFiles[index].name;

    this.albumSrc = (index % 2 === 0) ? $r('app.media.album') : $r('app.media.album2');



    // update duration

    this.totalMs = PlayerModel.getDuration();

    this.totalTimeText = this.getShownTimer(this.totalMs);

    this.currentTimeText = this.getShownTimer(PlayerModel.getCurrentMs());

    Logger.info(TAG, `refreshSongInfo this.title= ${this.title}, this.totalMs= ${this.totalMs}, this.totalTimeText= ${this.totalTimeText},this.currentTimeText= ${this.currentTimeText}`);

  };



  onAppSharedClick() {

    if (this.clickFlag === MusicSharedStatus.MUSIC_SHARED) {

      Logger.info(TAG, `1start button is ${JSON.stringify(this.imageArrays[ABILITY_SHARED_BUTTON])}`);

      this.showDialog();

    } else if (this.clickFlag === MusicSharedStatus.MUSIC_STOP_SHARED) {

      Logger.info(TAG, `2start button is ${JSON.stringify(this.imageArrays[ABILITY_SHARED_BUTTON])}`);

      this.stopRemoteExtension();

      this.imageArrays[ABILITY_SHARED_BUTTON] = $r('app.media.ic_hop');

    };

  };



  onPreviousClick() {

    if (this.isSwitching) {

      Logger.info(TAG, 'onPreviousClick ignored, isSwitching');

      return;

    };

    Logger.info(TAG, 'onPreviousClick');

    PlayerModel.index--;

    if (PlayerModel.index < 0 && PlayerModel.playlist.audioFiles.length >= 1) {

      PlayerModel.index = PlayerModel.playlist.audioFiles.length - 1;

    };

    this.currentProgress = 0;

    this.isSwitching = true;



    PlayerModel.preLoad(PlayerModel.index, () = > {

      this.refreshSongInfo(PlayerModel.index);

      PlayerModel.play(0, true);

      if (PlayerModel.isPlaying) {

        this.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_pause');

      };

      this.isSwitching = false;

    });

  };



  onNextClick() {

    if (this.isSwitching) {

      Logger.info(TAG, 'onNextClick ignored, isSwitching');

      return;

    };

    Logger.info(TAG, 'onNextClick');

    PlayerModel.index++;

    if (PlayerModel.index >= PlayerModel.playlist.audioFiles.length) {

      PlayerModel.index = 0;

    };

    this.currentProgress = 0;

    this.isSwitching = true;

    PlayerModel.preLoad(PlayerModel.index, () = > {

      this.refreshSongInfo(PlayerModel.index);

      PlayerModel.play(0, true);

      if (PlayerModel.isPlaying) {

        this.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_pause');

      };

      this.isSwitching = false;

    });

  };



  onPlayClick() {

    if (this.isSwitching) {

      Logger.info(TAG, 'onPlayClick ignored, isSwitching');

      return;

    };

    Logger.info(TAG, `onPlayClick isPlaying= ${PlayerModel.isPlaying}`);

    if (PlayerModel.isPlaying) {

      PlayerModel.pause();

      this.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_play');

      this.sendMessagePause();

    } else {

      PlayerModel.preLoad(PlayerModel.index, () = > {

        PlayerModel.play(DEFAULT_NUM, true);

        this.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_pause');

        this.sendMessagePlay();

      })

    };

  };



  restoreFromWant() {

    Logger.info(TAG, 'restoreFromWant');

    let status: Record< string, Object > | undefined = AppStorage.get('status');

    if (status !== undefined && status !== null && status.uri !== null) {

      KvStoreModel.broadcastMessage(this.context, REMOTE_ABILITY_STARTED);

      Logger.info(TAG, 'restorePlayingStatus');

      PlayerModel.restorePlayingStatus(status, (index: number) = > {

        Logger.info(TAG, `restorePlayingStatus finished, index= ${index}`);

        if (index >= 0) {

          this.refreshSongInfo(index);

        } else {

          PlayerModel.preLoad(0, () = > {

            this.refreshSongInfo(0);

          })

        }

        if (status !== undefined) {

          Logger.info(TAG, `Index PlayerModel.restorePlayingStatus this.totalMs = ${this.totalMs}, status.seekTo = ${status.seekTo}`);

          this.currentProgress = Math.floor(Number(status.seekTo) / this.totalMs * ONE_HUNDRED);

        }

      })

    } else {

      PlayerModel.preLoad(0, () = > {

        this.refreshSongInfo(0);

      });

    }

  };



  aboutToAppear() {

    Logger.info(TAG, `begin`);

    Logger.info(TAG, 'grantPermission');

    this.context = getContext(this) as common.UIAbilityContext;

    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();

    let permission: Array< Permissions > = ['ohos.permission.DISTRIBUTED_DATASYNC'];

    try {

      atManager.requestPermissionsFromUser(this.context, permission).then((data: PermissionRequestResult) = > {

        Logger.info(TAG, `data: ${JSON.stringify(data)}`);

      }).catch((err: object) = > {

        Logger.info(TAG, `err: ${JSON.stringify(err)}`);

      })

    } catch (err) {

      Logger.info(TAG, `catch err- >${JSON.stringify(err)}`);

    }

    display.getDefaultDisplay().then((dis: display.Display) = > {

      Logger.info(TAG, `getDefaultDisplay dis= ${JSON.stringify(dis)}`);

      let proportion = DESIGN_WIDTH / dis.width;

      let screenWidth = DESIGN_WIDTH;

      let screenHeight = (dis.height - SYSTEM_UI_HEIGHT) * proportion;

      this.riscale = (screenHeight / screenWidth) / DESIGN_RATIO;

      if (this.riscale < 1) {

        // The screen ratio is shorter than design ratio

        this.risw = screenWidth * this.riscale;

        this.rish = screenHeight;

      } else {

        // The screen ratio is longer than design ratio

        this.risw = screenWidth;

        this.rish = screenHeight / this.riscale;

      }

      Logger.info(TAG, `proportion=${proportion} , screenWidth= ${screenWidth},

      screenHeight= ${screenHeight} , riscale= ${this.riscale} , risw= ${this.risw} , rish= ${this.rish}`);

    })

    Logger.info(TAG, 'getDefaultDisplay end');

    this.currentTimeText = this.getShownTimer(0);

    PlayerModel.setOnStatusChangedListener((isPlaying: string) = > {

      Logger.info(TAG, `on player status changed, isPlaying= ${isPlaying} refresh ui`);

      PlayerModel.setOnPlayingProgressListener((currentTimeMs: number) = > {

        this.currentTimeText = this.getShownTimer(currentTimeMs);

        this.currentProgress = Math.floor(currentTimeMs / this.totalMs * ONE_HUNDRED);

      });

      if (isPlaying) {

        this.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_pause');

      } else {

        this.imageArrays[PREVIOUS_CLICK] = $r('app.media.ic_play');

      }

    });



    PlayerModel.getPlaylist(() = > {

      Logger.info(TAG, 'on playlist generated, refresh ui');

      this.restoreFromWant();

    });



    AppStorage.setOrCreate('viewThis', this);



    this.connectLocalExtension();

  };



  aboutToDisappear() {

    Logger.info(TAG, `aboutToDisappear begin`)

    if (PlayerModel === undefined) {

      return

    }

    PlayerModel.release()

    this.remoteDeviceModel.unregisterDeviceListCallback()

    this.dialogController = null

    KvStoreModel.deleteKvStore()

    Logger.info(TAG, `aboutToDisappear end`)

  };



  build() {

    Column() {

      Blank()

        .width('100%')

        .height(72)

      Text(this.title)

        .width('100%')

        .fontSize(28)

        .margin({ top: '10%' })

        .fontColor(Color.White)

        .textAlign(TextAlign.Center)

      Image(this.albumSrc)

        .width(this.isLand ? '60%' : '89%')

        .objectFit(ImageFit.Contain)

        .margin({ top: 50, left: 40, right: 40 })

      Row() {

        Text(this.currentTimeText)

          .fontSize(20)

          .fontColor(Color.White)

        Blank()

        Text(this.totalTimeText)

          .fontSize(20)

          .fontColor(Color.White)

      }

      .width('90%')

      .margin({ top: '12%' })



      Slider({ value: typeof (this.currentProgress) === 'number' ? this.currentProgress : 0 })

        .trackColor('#64CCE7FF')

        .width('90%')

        .selectedColor('#ff0c4ae7')

        .onChange((value: number, mode: SliderChangeMode) = > {

          this.currentProgress = value;

          if (typeof (this.totalMs) !== 'number') {

            this.currentProgress = 0;

            Logger.info(TAG, `setProgress ignored, totalMs= ${this.totalMs}`);

            return;

          };

          let currentMs = this.currentProgress / ONE_HUNDRED * this.totalMs;

          this.currentTimeText = this.getShownTimer(currentMs);

          if (mode === SliderChangeMode.End || mode === 3) {

            Logger.info(TAG, `player.seek= ${currentMs}`);

            PlayerModel.seek(currentMs);

          };

        })



      Row() {

        ForEach(this.imageArrays, (item: Resource, index: number | undefined) = > {

          Column() {

            Image(item)

              .size({ width: 74, height: 74 })

              .objectFit(ImageFit.Contain)

              .onClick(() = > {

                switch (index) {

                  case 0:

                    this.onAppSharedClick();

                    break;

                  case 1:

                    this.onPreviousClick();

                    break;

                  case 2:

                    this.onPlayClick();

                    break;

                  case 3:

                    this.onNextClick();

                    break;

                  default:

                    break;

                }

              })

          }

          .id('image' + (index !== undefined ? (index + 1) : 0))

          .width(100)

          .height(100)

          .alignItems(HorizontalAlign.Center)

          .justifyContent(FlexAlign.Center)

        })



      }

      .width('100%')

      .margin({ top: '4%' })

      .justifyContent(FlexAlign.SpaceEvenly)

    }

    .width('100%')

    .height('100%')

    .backgroundImage($r('app.media.bg_blurry'))

    .backgroundImageSize({ width: '100%', height: '100%' })

  }

}

跨设备播放操作

(1)分布式设备管理器绑定应用包 deviceManager.createDeviceManager('ohos.samples.distributedmusicplayer') [源码参考]。

/*

* Copyright (c) 2022 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 deviceManager from '@ohos.distributedDeviceManager';

import Logger from '../model/Logger';



let SUBSCRIBE_ID: number = 100;

const RANDOM: number = 65536;

const TAG: string = 'RemoteDeviceModel';



export class RemoteDeviceModel {

  public deviceLists: Array< deviceManager.DeviceBasicInfo > = [];

  public discoverLists: Array< deviceManager.DeviceBasicInfo > = [];

  private callback: () = > void = null;

  private authCallback: () = > void = null;

  private deviceManager: deviceManager.DeviceManager = undefined;



  registerDeviceListCallback(callback) {

    if (typeof (this.deviceManager) === 'undefined') {

      Logger.info(TAG, 'deviceManager.createDeviceManager begin');

      try {

        this.deviceManager = deviceManager.createDeviceManager('ohos.samples.distributedmusicplayer');

        this.registerDeviceList(callback);

        Logger.info(TAG, `createDeviceManager callback returned, value= ${JSON.stringify(this.deviceManager)}`);

      } catch (error) {

        Logger.info(TAG, `createDeviceManager throw error, error=${error} message=${error.message}`);

      }

      Logger.info(TAG, 'deviceManager.createDeviceManager end');

    } else {

      this.registerDeviceList(callback);

    };

  };



  registerDeviceList(callback) {

    Logger.info(TAG, 'registerDeviceListCallback');

    this.callback = callback;

    if (this.deviceManager === undefined) {

      Logger.error(TAG, 'deviceManager has not initialized');

      this.callback();

      return;

    };



    Logger.info(TAG, 'getTrustedDeviceListSync begin');

    let list: deviceManager.DeviceBasicInfo[] = [];

    try {

      list = this.deviceManager.getAvailableDeviceListSync();

    } catch (error) {

      Logger.info(TAG, `getTrustedDeviceListSync throw error, error=${error} message=${error.message}`);

    };

    Logger.info(TAG, `getTrustedDeviceListSync end, deviceLists= ${JSON.stringify(list)}`);

    if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') {

      this.deviceLists = list;

    };

    this.callback();

    Logger.info(TAG, 'callback finished');



    try {

      this.deviceManager.on('deviceStateChange', (data) = > {

        Logger.info(TAG, `deviceStateChange data= ${JSON.stringify(data)}`);

        switch (data.action) {

          case deviceManager.DeviceStateChange.AVAILABLE:

            this.discoverLists = [];

            this.deviceLists.push(data.device);

            Logger.info(TAG, `reday, updated device list= ${JSON.stringify(this.deviceLists)} `);

            let list: deviceManager.DeviceBasicInfo[] = [];

            try {

              list = this.deviceManager.getAvailableDeviceListSync();

            } catch (err) {

              Logger.info(TAG, `this err is ${JSON.stringify(err)}`);

            }

            Logger.info(TAG, `getTrustedDeviceListSync end, deviceList= ${JSON.stringify(list)}`);

            if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') {

              this.deviceLists = list;

            }

            this.callback();

            break;

          case deviceManager.DeviceStateChange.UNAVAILABLE:

            if (this.deviceLists.length > 0) {

              let list = [];

              for (let i = 0; i < this.deviceLists.length; i++) {

                if (this.deviceLists[i].deviceId !== data.device.deviceId) {

                  list[i] = data.device;

                };

              };

              this.deviceLists = list;

            };

            Logger.info(TAG, `offline, updated device list= ${JSON.stringify(this.deviceLists)}`);

            this.callback();

            break;

          default:

            break;

        };

      });

      this.deviceManager.on('discoverSuccess', (data) = > {

        Logger.info(TAG, `discoverSuccess data= ${JSON.stringify(data)}`);

        Logger.info(TAG, `discoverSuccess this.deviceLists= ${this.deviceLists}, this.deviceLists.length= ${this.deviceLists.length}`);

        for (let i = 0;i < this.discoverLists.length; i++) {

          if (this.discoverLists[i].deviceId === data.device.deviceId) {

            Logger.info(TAG, 'device founded, ignored');

            return;

          };

        };

        this.discoverLists[this.discoverLists.length] = data.device;

        this.callback();

      });

      this.deviceManager.on('discoverFailure', (data) = > {

        Logger.info(TAG, `discoverFailure data= ${JSON.stringify(data)}`);

      });

      this.deviceManager.on('serviceDie', () = > {

        Logger.error(TAG, 'serviceDie');

      });

    } catch (error) {

      Logger.info(TAG, `on throw error, error=${error} message=${error.message}`);

    }



    let discoverParam = {

      'discoverTargetType': 1

    };

    let filterOptions = {

      'availableStatus': 0

    };

    Logger.info(TAG, `startDiscovering ${SUBSCRIBE_ID}`);

    try {

      if (this.deviceManager !== null) {

        this.deviceManager.startDiscovering(discoverParam, filterOptions);

      };

    } catch (error) {

      Logger.error(TAG, `startDiscovering throw error, error=${error} message=${error.message}`);

    };

  };



  authDevice(device, callback) {

    Logger.info(TAG, `authDevice ${device}`);

    if (device !== undefined) {

      for (let i = 0; i < this.discoverLists.length; i++) {

        if (this.discoverLists[i].deviceId === device.deviceId) {

          Logger.info(TAG, 'device founded, ignored');

          let bindParam = {

            bindType: 1,

            targetPkgName: 'ohos.samples.distributedmusicplayer',

            appName: 'Music',

          };

          Logger.info(TAG, `authenticateDevice ${JSON.stringify(this.discoverLists[i])}`);

          try {

            this.deviceManager.bindTarget(device.deviceId, bindParam, (err, data) = > {

              if (err) {

                Logger.error(TAG, `authenticateDevice error: ${JSON.stringify(err)}`);

                this.authCallback = () = > {

                };

                return;

              };

              Logger.info(TAG, `authenticateDevice succeed, data= ${JSON.stringify(data)}`);

              this.authCallback = callback;

            });

          } catch (error) {

            Logger.error(TAG, `authenticateDevice throw error, error=${JSON.stringify(error)} message=${error.message}`);

          }

        }

      }

    }

  };



  unregisterDeviceListCallback() {

    Logger.info(TAG, `stopDiscovering ${SUBSCRIBE_ID}`);

    if (this.deviceManager === undefined) {

      return;

    };

    try {

      this.deviceManager.stopDiscovering();

      this.deviceManager.off('deviceStateChange');

      this.deviceManager.off('discoverSuccess');

      this.deviceManager.off('discoverFailure');

      this.deviceManager.off('serviceDie');

    } catch (error) {

      Logger.info(TAG, `stopDeviceDiscovery throw error, error=${error} message=${error.message}`);

    }

    this.deviceLists = [];

  };

}

(2) 初始化播放器 构造函数中通过'@ohos.multimedia.media'组件对播放器进行实例化,并调用播放器初始化函数,通过播放器的on函数,监听error、finish、timeUpdate
(3) 同步当前播放数据 播放器通过调用selectedIndexChange(),将当前播放的资源、时间、以及播放状态同步给选中的设备。
(4) 接收当前播放数据 播放器通过在aboutToAppear()时调用this.restoreFromWant(), KvStoreModel组件获取播放列表,playerModel组件重新加载播放器状态和资源。

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 分布式
    +关注

    关注

    1

    文章

    850

    浏览量

    74430
  • 鸿蒙
    +关注

    关注

    57

    文章

    2301

    浏览量

    42666
  • HarmonyOS
    +关注

    关注

    79

    文章

    1964

    浏览量

    29942
收藏 人收藏

    评论

    相关推荐

    HarmonyOS应用开发-分布式任务调度

    1. 介绍本篇CodeLab将实现的内容HarmonyOS是面向全场景多终端的分布式操作系统,使得应用程序的开发打破了智能终端互通的性能和数据壁垒,业务逻辑原子化开发,适配多端。通过一
    发表于 09-18 09:21

    HarmonyOS应用开发-分布式设计

    设计理念HarmonyOS 是面向未来全场景智慧生活方式的分布式操作系统。对消费者而言,HarmonyOS 将生活场景中的各类终端进行能力整合,形成“One Super Device”,以实现
    发表于 09-22 17:11

    HarmonyOS分布式应用框架深入解读

    设备、分布式的能力及应用,二者具有无限能力。从开发者角度看,HarmonyOS上基本的组件分为3+1,其中3代表三个Ability,分别是:PageAbility:负责用户界面的显示
    发表于 11-22 15:15

    如何高效完成HarmonyOS分布式应用测试?

    作者:liuxun,HarmonyOS测试架构师HarmonyOS是新一代的智能终端操作系统,给开发者提供了设备发现、设备连接、跨设备调用等丰富的分布式API。随着越来越多的
    发表于 12-13 18:07

    基于润和DAYU200开发套件的OpenHarmony分布式音乐播放

    RJ45以太网口,可满足NVR、工业网关等多网口产品需求。分布式音乐播放器这里给大家分享一个样例,分布式音乐
    发表于 03-14 09:07

    Hello HarmonyOS学习笔记:分布式新闻客户端实战(JS、eTS)

    源代码下载地址:Codelabs: 分享知识与见解,一起探索HarmonyOS的独特魅力。 - Gitee.com代码讲解视频:华为开发者学堂-【Hello系列直播课】第5期:分布式新闻客户端
    发表于 06-23 20:08

    求一种基于DAYU200开发套件的分布式音乐播放器设计方案

    、音频、视频和摄像头等功能,拥有丰富的扩展接口,支持多种视频输入输出接口;配置双千兆自适应RJ45以太网口,可满足NVR、工业网关等多网口产品需求。分布式音乐播放器这里给大家分享一个样例,分布
    发表于 09-08 17:22

    HarmonyOS测试技术与实战-HarmonyOS分布式应用特征与挑战

     HDC 2021华为开发者大会HarmonyOS测试技术与实战-HarmonyOS分布式应用特征与挑战
    的头像 发表于 10-23 14:41 1632次阅读
    <b class='flag-5'>HarmonyOS</b>测试技术与<b class='flag-5'>实战</b>-<b class='flag-5'>HarmonyOS</b><b class='flag-5'>分布式</b>应用特征与挑战

    HarmonyOS测试技术与实战-分布式应用测试解决方案

    HDC 2021华为开发者大会HarmonyOS测试技术与实战-HarmonyOS分布式应用测试解决方案
    的头像 发表于 10-23 14:48 1551次阅读
    <b class='flag-5'>HarmonyOS</b>测试技术与<b class='flag-5'>实战</b>-<b class='flag-5'>分布式</b>应用测试解决方案

    HarmonyOS测试技术与实战-分布式UI测试框架

    HDC 2021华为开发者大会 HarmonyOS测试技术与实战-分布式UI测试框架演示
    的头像 发表于 10-23 14:49 1341次阅读
    <b class='flag-5'>HarmonyOS</b>测试技术与<b class='flag-5'>实战</b>-<b class='flag-5'>分布式</b>UI测试框架

    HarmonyOS测试技术与实战-分布式业务音视频测试场景分类

     HDC 2021华为开发者大会 HarmonyOS测试技术与实战-分布式业务音视频测试场景分类
    的头像 发表于 10-23 15:54 1649次阅读
    <b class='flag-5'>HarmonyOS</b>测试技术与<b class='flag-5'>实战</b>-<b class='flag-5'>分布式</b>业务音视频测试场景分类

    HarmonyOS测试技术与实战-分布式业务音频体验关注点

    HDC 2021华为开发者大会 HarmonyOS测试技术与实战-分布式业务音频体验关注点
    的头像 发表于 10-23 15:59 1160次阅读
    <b class='flag-5'>HarmonyOS</b>测试技术与<b class='flag-5'>实战</b>-<b class='flag-5'>分布式</b>业务音频体验关注点

    HarmonyOS测试技术与实战-分布式业务音视频体验测试全景

    HDC 2021华为开发者大会 HarmonyOS测试技术与实战-分布式业务音视频体验测试全景
    的头像 发表于 10-23 16:03 1691次阅读
    <b class='flag-5'>HarmonyOS</b>测试技术与<b class='flag-5'>实战</b>-<b class='flag-5'>分布式</b>业务音视频体验测试全景

    鸿蒙版JS如何实现分布式仿抖音应用

       之前大家看过了 Java 版的《 HarmonyOS 分布式之仿抖音应用 》,现在讲讲 JS 如何实现分布式仿抖音应用,通过 JS 方式开发视频
    的头像 发表于 11-15 09:44 2322次阅读

    HarmonyOS分布式应用上架问题分析

    HarmonyOS是新一代的智能终端操作系统,给开发者提供了设备发现、设备连接、跨设备调用等丰富的分布式API。随着越来越多的开发者投入到Harmo
    的头像 发表于 12-24 17:56 1862次阅读
    <b class='flag-5'>HarmonyOS</b><b class='flag-5'>分布式</b>应用上架问题分析