#星光计划1.0# HarmonyOS 分布式之仿抖音视频(JS) 原创 精华

发布于 2021-11-9 14:44
浏览
4收藏

作者:陈建朋

简介

之前大家看过了Java版的【#星光计划1.0# HarmonyOS 分布式之仿抖音应用】,现在讲讲JS如何实现分布式仿抖音应用,通过JS方式开发视频播放,分布式设备迁移,评论,通过Java和js交互,获取设备信息,选择设备信息做分布式迁移。

  • 功能:分布式迁移到不同设备,视频进行评论,播放视频,可以像抖音一样切换视频,可以点赞,分享等操作。
  • 开发版本:sdk6,DevEco Studio3.0 Beta1

效果演示

#星光计划1.0# HarmonyOS 分布式之仿抖音视频(JS)-开源基础软件社区

主要代码

1.视频播放

鸿蒙js中有专门【video】的组件,并且非常完善,可以直接使用

    <video id='playerId{{ $idx }}' src='{{ $item.path }}' muted='false' autoplay='true'
           controls="false" onprepared='preparedCallback' onstart='startCallback' onpause='pauseCallback'
           onfinish='finishCallback' onerror='errorCallback' onseeking='seekingCallback'
           onseeked='seekedCallback'
           ontimeupdate='timeupdateCallback' style="object-fit : contain; width : 100%;"
           onlongpress='change_fullscreenchange' onclick="change_start_pause" loop='true'
           starttime="0"
            >
    </video>

js 代码中视频资源

  list: [{
         path: "/common/video/video1.mp4"
          },{
          path: "/common/video/video2.mp4"
          }, {
          path: "/common/video/video3.mp4"
          }, {
          path: "/common/video/video4.mp4"
          }, {
          path: "/common/video/video5.mp4"
           }, {
          path: "/common/video/video6.mp4"
         }]

2.仿抖音视频切换

有关视频切换的开发 js中也提供了对应的组件【swiper】,可以直接使用来进行视频切换

<!--index="{{ player_index }}"//设置当前的切换项 -->
<!--onchange="changeVideo"//切换监听 -->
<swiper class="swiper-content" id="swiper" index="{{ player_index }}"
           indicator="false" loop="true" digital="false" vertical="true" onchange="changeVideo">
       <stack class="stack-parent" for="list">
           <div class="videosource">
               <!--src='{{ $item.path }}'//设置视频资源路径-->
               <video id='playerId{{ $idx }}' src='{{ $item.path }}' muted='false' autoplay='true'
                      controls="false" onprepared='preparedCallback' onstart='startCallback' onpause='pauseCallback'
                      onfinish='finishCallback' onerror='errorCallback' onseeking='seekingCallback'
                      onseeked='seekedCallback'
                      ontimeupdate='timeupdateCallback' style="object-fit : contain; width : 100%;"
                      onlongpress='change_fullscreenchange' onclick="change_start_pause" loop='true'
                      starttime="0"
                       >
               </video>
           </div>
       </stack>
   </swiper>

3.评论功能添加

评论功能使用了鸿蒙js中了两个组件 【list】(负责列表展示) 和 【input】(负责信息发送),可参见有关文档。

评论功能有两部分 评论列表和评论发送输入框。

 <div class="pinglun" style="visibility : {{ ifhidden }};">
     <div style="height : 32%; background-color : transparent;" onclick="hideenbg">
         <text></text>
     </div>
     <!--列表展示-->
     <list class="todo-wrapper" divider="true" style="divider-color : darkgrey;">
         <!--list-item for="{{ todolist }}" class="todo-item" 循环遍历数据 显示item-->
         <list-item for="{{ todolist }}" class="todo-item">
             <div class="photo">
                 <image class="image-set" src="/common/images/science6.png"></image>
                 <text class="todo-title" style="font-size : 14fp; margin-left : 10px;">{{ $item.name }}
                 </text>
             </div>
             <text class="todo-title" style="font-size : 14fp; margin-top : 5px;">{{ $item.detail }}</text>
         </list-item>
     </list>
     <!--输入框信息发送-->
     <div class="butt">
         <!--onchange="change"//监听输入框信息变化-->
         <input id="input" class="input" type="text" value="{{ content }}" maxlength="20" enterkeytype="send"
                placeholder="{{ placecontent }}" onchange="change"
                onenterkeyclick="enterkeyClick">
         </input>
         <button class="last" onclick="sendmessage">发送</button>
     </div>
 </div>

功能实现逻辑:

   change(e) { // 监听输入框 信息变化 获取信息
       this.message = e.value;
       console.log("message===" + this.message)
   },

   sendmessage() { // 提交信息后组织数据 刷新界面
       this.todolist.push({
           name: '王者',
           detail: this.message,
       })
       this.content = "";
       this.message = "";
   },

4.js和Java交互获取设备信息

在实现分布式设备迁移的时候 查找js没有找到获取设备信息的有关接口,所以考虑通过js和Java相互调用实现。
通过jsFA 调用 Java PA机制,实现数据的获取和传递。

js端实现

重点:
Ability类型,对应PA端不同的实现方式:
0:Ability 1:Internal Ability

syncOption PA侧请求消息处理同步/异步选项
0:同步方式,默认方式。 1:异步方式。

 initAction(code) {
     var actionData = {};
     actionData.firstNum = 1024;
     actionData.secondNum = 2048;
     var action = {};
     action.bundleName = "com.corecode.video.videoplayer";//包名
     action.abilityName = "com.corecode.video.videoplayer.DeviceInternalAbility";// 包名+类名
     action.messageCode = code;// 消息编码
     action.data = actionData;// 传递数据
     action.abilityType = 1;//  ability类型 
     action.syncOption = 1;//同步还是异步类型
     return action;
 },
 getLevel: async function () {
     try {
         var action = this.initAction(1001);
         var result = await FeatureAbility.callAbility(action);
         console.info(" result = " + result);
         this.deviceId = JSON.parse(JSON.parse(result).result);
         console.log("deviceId==" + this.deviceId)
         this.devicelist = "visible";
     } catch (pluginError) {
         console.error("getBatteryLevel : Plugin Error = " + pluginError);
     }
 }

Java端实现
Java 端需要创建一个 ability 服务 继承 AceInternalAbility 具体是使用哪种类型 看上面的解释。
创建后需要注册,比如我创建的是 InternalAbility 这样注册: DeviceInternalAbility.register(this);

package com.corecode.video.videoplayer;
public class DeviceInternalAbility extends AceInternalAbility {
   private static final HiLogLabel TAG = new HiLogLabel(0, 0, "DeviceInternalAbility");
   private static final int CONNECT_ABILITY = 2000;
   private static final int DISCONNECT_ABILITY = 2001;
   private static final int SEND_MSG = 1001;
   private static final int SUCCESS_CODE = 0;
   private static final int FAIL_CODE = -1;
   private static DeviceInternalAbility INSTANCE;
   private String selectDeviceId;

   /**
    * default constructor
    *
    * @param context ability context
    */
   public DeviceInternalAbility(AbilityContext context) {
       super("com.corecode.video.videoplayer", "com.corecode.video.videoplayer.DeviceInternalAbility");
   }

   public DeviceInternalAbility(String bundleName, String abilityName) {
       super(bundleName, abilityName);
   }

   public DeviceInternalAbility(String abilityName) {
       super(abilityName);
   }

   /**
    * setInternalAbilityHandler for DistributeInternalAbility instance
    *
    * @param context ability context
    */
   static void register(AbilityContext context) {
       INSTANCE = new DeviceInternalAbility(context);
       INSTANCE.setInternalAbilityHandler((code, data, reply, option) ->
               INSTANCE.onRemoteRequest(code, data, reply, option));
   }

   /**
    * destroy DistributeInternalAbility instance
    */
   private static void unregister() {
       INSTANCE.destroy();
   }

   /**
    * default destructor
    */
   private void destroy() {
   }

   /**
    * hand click request from javascript
    *
    * @param code   ACTION_CODE
    * @param data   data sent from javascript
    * @param reply  reply for javascript
    * @param option currently excessive
    * @return whether javascript click event is correctly responded
    */
   private boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
       Map<String, Object> replyResult = new HashMap<>();
       switch (code) {
           // send message to remote device, message contains controller command from FA
           case SEND_MSG: {
               ZSONObject dataParsed = ZSONObject.stringToZSON(data.readString());
               int message = dataParsed.getIntValue("message");
//                // SYNC
//                if (option.getFlags() == MessageOption.TF_SYNC) {
//                    reply.writeString(ZSONObject.toZSONString(result));
//                }
               // ASYNC
               // 返回结果当前仅支持String,对于复杂结构可以序列化为ZSON字符串上报
               Map<String, Object> result = new HashMap<String, Object>();
               result.put("result", MainAbility.getList());
               MessageParcel responseData = MessageParcel.obtain();
               responseData.writeString(ZSONObject.toZSONString(result));
               IRemoteObject remoteReply = reply.readRemoteObject();
               try {
                   remoteReply.sendRequest(0, responseData, MessageParcel.obtain(), new MessageOption());
               } catch (RemoteException exception) {
                   return false;
               } finally {
                   responseData.reclaim();
               }
               break;
           }
           // to invoke remote device's newsShare ability and send news url we transfer
           case CONNECT_ABILITY: {
               ZSONObject dataParsed = ZSONObject.stringToZSON(data.readString());
               selectDeviceId = dataParsed.getString("deviceId");
               break;
           }
           // when controller FA went to destroy lifeCycle, disconnect with remote newsShare ability
           case DISCONNECT_ABILITY: {
               unregister();
               break;
           }
           default:
               HiLog.error(TAG, "messageCode not handle properly in phone distributeInternalAbility");
       }
       return true;
   }

   private void assembleReplyResult(int code, Map<String, Object> replyResult, Object content, MessageParcel reply) {
       replyResult.put("code", code);
       replyResult.put("content", content);
       reply.writeString(ZSONObject.toZSONString(replyResult));
   }
}

js调用后会进入 Java的 onRemoteRequest 函数进行数据解析和组织,然后通过接口:
remoteReply.sendRequest(0, responseData, MessageParcel.obtain(), new MessageOption());
将需要的结果回传给 js做界面展示和操作。

获取设备信息:
List<DeviceInfo> deviceInfos = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);

5.分布式迁移

鸿蒙中分布式迁移 真的是做到了强大,在js中只需要四个函数就能完成 分布式迁移。

  onSaveData(saveData) { // 数据保存到savedData中进行迁移。
       var data = {
           list: this.list,
           player_index: this.player_index,
       };
       Object.assign(saveData, data)
   },
   onRestoreData(restoreData) {  // 收到迁移数据,恢复。
       console.info('==== onRestoreData ' + JSON.stringify(restoreData))
       this.list = restoreData.list
       this.player_index = restoreData.player_index

       this.$element('swiper').swipeTo({
           index: this.player_index
       });

   },
   onCompleteContinuation(code) { //迁移完成
       console.log("onCompleteContinuation===" + code)
   },
   onStartContinuation() {//迁移开始
       return true;
   },

迁移:
上面的四个有关函数 设置好后 执行 下面的接口就能实现 分布式迁移了。
continueVideoAbility: async function () {
let conti = await FeatureAbility.continueAbility();
}

6.权限

需要加上 需要的权限:

 "reqPermissions": [
   {
     "reason": "",
     "usedScene": {
       "ability": [
         "MainAbility"
       ],
       "when": "inuse"
     },
     "name": "ohos.permission.DISTRIBUTED_DATASYNC"
   },
   {
     "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"
   },
   {
     "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
   },
   {
     "name": "ohos.permission.GRT_BUNDLE_INFO"
   },
   {
     "name": "ohos.permission.INTERNET"
   }
 ]

源码地址【 分布式仿抖音视频】

更多原创内容请关注:开鸿 HarmonyOS 学院

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

【本文正在参与51CTO HarmonyOS技术社区创作者激励-星光计划1.0】

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2021-11-10 17:18:45修改
5
收藏 4
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐