基于hybrid播放器同层渲染实践总结

1,717 阅读4分钟

业务背景介绍

在hybrid模式下,播放器由原生提供,h5通过jsbridge调用原生能力实现对播放器的生命周期(创建、拉流、销毁)、行为(横竖屏、录影、分辨率切换、camera设备通信)等业务交互。播放器由原生提供出现的问题是播放器不存在于h5的webview层级当中,相互独立,只通过webviewjavascriptBridge通信,原生播放器浮在h5层级之上,就必然存在播放器会遮挡h5层级的情况,播放器的同层渲染想要解决的问题是播放器在h5层级之中,让h5统一管理webview层级与原生播放器层级。

实践过程

方案一 :原生嵌入h5的某一个层级当中

ios方案介绍,借助ios托管webview的滚动区域生成特定的WKChildScroll特性。
1.h5设置宽度占满16:9的播放器区域,设置外两层盒子,使内层出现滚动区域。

2.ios通过视图调试可以获取到该滚动区域视图层WKChildScroll(如下图)。由于h5滚动区域长列表性能较差,IOS默认托管生成特定的WKChildScroll来优化表现,开发过程中常见问题。

3. IOS将原生组件挂载到该 WKChildScrollView 节点上作为其子 View。

4.原生处理事件响应,透传到原生组件上。

ios方案风险点
1.横竖屏切换 层级是否发生变化

2.原先的toast 位于view中的 可能被遮挡而无法看到。

3.一个界面中存在多个wkchildScrollView 情况

4.插入wkchildScrollView 带来的滑动特性

5.wkchildScrollView 和原生view 声明周期的不同步产生的内存泄漏,应该保证 wkchildScrollView先于原生view创建,晚于原生view 销毁。

6.事件响应不执行

7.创建播放器时,wkchildScrollView 节点并未渲染,创建之后 wkchildScrollView 才渲染,导致 无法找到 wkchildScrollView 层级。

8.前后台切换,元素是否被释放。

9.严重问题,多版本不兼容  ios12 版本 事件响应正常  iOS13.6 子视图事件不响应 ,需要排查原因
android方案介绍
1.WebView 侧创建一个 embed DOM 节点并指定组件类型;

2.chromium 内核会创建一个 WebPlugin 实例,并生成一个 RenderLayer;

3.Android 客户端初始化一个对应的原生组件;

4.Android 客户端将原生组件的画面绘制到步骤2创建的 RenderLayer 所绑定的 SurfaceTexture 上;

5.通知 chromium 内核渲染该 RenderLayer;

6.chromium 渲染该 embed 节点并上屏。
android方案风险分析
1.比较难实现,周期太长。

2.大厂一般自己开发andorid内核,专业人员迭代维护升级,小厂不推荐。

3.兼容性问题难以保证
h5风险分析分析

显然android和ios两种方案对h5而言需要两套代码去写兼容性,痛苦之处不言而喻。后期随着功能迭代维护成本简直不要太高啦。

方案二 :原生置于webview最底层。

1.原生端原生控件创建于h5的webview之下。

2.h5对播放器以上的所有webview层级添加透明来显示。

3.原生拦截h5在播放器上的事件行为下发到播放器上。

4.原生与播放器层级完全分离,原生无需处理太多细节。
h5代码介绍

1.抽取hooks,实现播放器页面路由切入动画时设置播放器占位区域透明度

/*
 * @Author: zhoufushan@leedarson.com
 * @Date: 2020-12-11 09:04:44
 * Copyright © Leedarson. All rights reserved.
 */
import { useEffect } from 'react';

const PLAYER_SIGN = 'player_sign';

const useSameLayerRender = ({ pathname = 'IPC/Live/', hideSelf = true, sign }) => {
  // 播放器置于最底部穿透解决同屏层渲染问题(播放器层级影响)
  useEffect(() => {
    if (Object.prototype.hasOwnProperty.call(window, '_routerTransiting')) {
      Object.defineProperty(window, '_routerTransiting', {
        set: o => {
          // 动画结束后设置播放器上层h5透明
          if (window.location.pathname.includes(pathname) && !o) {
            let whileRoot = false;
            let node = document.querySelector('.native-video-player');
            if (hideSelf) {
              node.style.background = 'transparent';
            }
            while (!whileRoot) {
              const { parentElement } = node;
              if (parentElement.id === 'root') {
                whileRoot = true;
                return;
              }
              parentElement.style.background = 'transparent';
              node = parentElement;
            }
          }
        },
      });
    }
  }, [hideSelf, pathname]);

  // 存储播放器标识sign,用于在播放器浮窗的h5页面使用
  useEffect(() => {
    if (sign) {
      sessionStorage.setItem(PLAYER_SIGN, sign);
    }
  }, [sign]);
};

export default useSameLayerRender;

2.hooks调用

	const { cloudLimitToast } = useSubscription();
    // 1.播放器穿透解决同层渲染问题
    useSameLayerRender({ pathname: 'IPC/CloudPlayback/', sign });

    // 2. playerBackData - 云回放数据
    const playerBackData = useCloudPlaybackState();

    // 3. Placeholder - 播放器占位元素
    const Placeholder = useMemo(
      () => styled.div`
        width: 100%;
        padding-top: 56.25%;
        background: transparent;
      `,
      [],
    );
优缺点分析

优点:

1.android和ios方案统一,h5不用兼容多套,维护成本低;

2.改动影响点小,改动量小,风险可控;

3.开发周期小;

4.兼容性好,基本没有兼容问题;

缺点:

1.不是真正的同层渲染,只是解决了当前遇到同层渲染问题。

2.原生事件拦截通过递归调用,当webview层级过多,堆栈开销大,再加上频繁点击的情况,会占用主线程过久,性能比较差。