as

Settings
Sign out
Notifications
Alexa
亚马逊应用商店
Ring
AWS
文档
Support
Contact Us
My Cases
新手入门
设计和开发
应用发布
参考
支持

网页应用开发者指南

网页应用开发者指南

使用WebView组件在Vega OS上集成、优化和交付标准网页应用。本指南涵盖了多个基本工作流,包括桥接网页内容与原生Fire TV功能、确保高性能媒体播放,以及调整网页应用以实现10英尺电视体验。

将初始焦点设置到WebView

要将焦点设置到WebView,请执行以下操作。在src/App.tsx文件中,通过向WebView组件添加hasTVPreferredFocus={true} 属性来设置初始焦点。

已复制到剪贴板。

  import { WebView } from "@amazon-devices/webview";
  import * as React from "react";
  import { useRef } from "react";
  import { View } from "react-native";

  export const App = () => {
  const webRef = useRef(null);
  return (
    <View >
        <WebView
        ref={webRef}
        hasTVPreferredFocus={true}
        source={{
          uri: "https://www.example.com",
        }}
        javaScriptEnabled={true}
        onLoadStart={(event) => {
          console.log("onLoadStart url: ", event.nativeEvent.url)
        }}
        onLoad={(event) => {
          console.log("onLoad url: ", event.nativeEvent.url)
        }}
        onError={(event) => {
          console.log("onError url: ", event.nativeEvent.url)
        }}
        />
    </View>
    );
  };

在WebView页面发送和接收消息

您可以使用适用于Vega的WebView发送和接收消息。要发送和接收消息,请使用以下选项:

  • 适用于Vega的React Native -> Web: injectJavaScript方法。
  • 网页 -> 适用于Vega的React Native: onMessage属性。

通过网页实现Vega设备的Fire TV功能

window.ReactNativeWebView.postMessage方法在适用于Vega的React Native中执行onMessage属性,该属性可用于触发Vega设备的Fire TV功能。window.ReactNativeWebView.postMessage只能接受一个参数,该参数必须是字符串。

在适用于Vega的WebView中检查HDR格式和编解码器支持

WebView在最新的Fire TV设备上支持HEVC Main10(HLG、HDR10、HDR10+)和VP9 Profile2(HLG、HDR10)。最新的Fire TV设备尚不支持AV1 HDR。请勿使用H264和VP8编解码器来处理HDR。

使用canPlayType()isTypeSupported() 来检查HDR的可用性。

在网页JavaScript中启用后退按钮遥控器按键事件

allowSystemKeyEvents属性控制网页应用是否会主动侦听特定的系统按键事件,例如后退按钮(进行标识时使用的是keyCode: 27)。要启用后退按钮遥控器按键事件,请执行以下操作。

  • src/App.tsx文件中,将allowSystemKeyEvents属性设置为true

启用并使用DevTools进行调试

当使用调试版本 (process.env.NODE_ENV = 'development') 构建应用时,默认情况下,Chrome DevTools处于启用状态。要构建DevTools调试应用,请执行以下操作。

  • 在命令提示符处,运行以下命令。

    已复制到剪贴板。

    vega build -b Debug
    

完成以下步骤以有效使用DevTools。要使用DevTools进行调试,请执行以下操作。

  1. 安装并启动调试应用,这样将打开WebView。
  2. 在命令提示符下,通过运行以下命令执行端口转发。

    已复制到剪贴板。

     vda forward tcp:9229 tcp:9229.
     DevTools在端口9229上运行。
    
  3. 打开Google Chrome并导航到chrome://inspect/#devices
  4. 在Remote Target(远程目标)部分已连接设备列表中,找到DevTools。
  5. 选择inspect(检查)打开DevTools,然后检查WebView。

有关更多信息,请参阅使用Vega Studio调试网页应用

支持的遥控器按键事件

下表显示了支持的遥控器按键事件及其关联的键代码。

事件键 键码 需要allowSystemKeyEvents属性
GoBack 27
Enter 13
ArrowLeft 37
ArrowRight 39
ArrowDown 40
ArrowUp 38
MediaFastForward 228
MediaPlayPause 179
MediaRewind 227

使用canPlayType() 检查HDR的示例

已复制到剪贴板。

>const video = document.querySelector('video');
>console.log(video.canPlayType('video/webm; codecs="vp09.02.10.10'));
probably

>console.log(video.canPlayType('video/mp4; codecs="hev1.2.4.L153.B0"'));
probably

使用isTypeSupported() 检查HDR的示例

已复制到剪贴板。

>MediaSource.isTypeSupported('video/webm; codecs="vp09.02.10.10"');
true

>MediaSource.isTypeSupported('video/mp4; codecs="hev1.2.4.L153.B0"');
true

由于存在已知问题,请勿使用MediaCapabilities.decodingInfo()。它会将VP9 Profile 2支持报告为false

使用HEVC的示例

已复制到剪贴板。

const hevcConfig = {
    type: 'media-source',
    video: {
        contentType: 'video/mp4; codecs="hvc1.2.4.L153.B0"',
        width: 3840,
        height: 2140,
        framerate: 60,
        bitrate: 20000000,
        transferFunction: 'pq',
        colorGamut: 'rec2020'
    }
};

navigator.mediaCapabilities.decodingInfo(hevcConfig)

如何覆盖默认媒体控制处理程序

有些应用需要对媒体播放进行精细控制,超出了默认WebView媒体控制的范围。以下是一些示例。

  • 在广告期间禁用搜索操作。
  • 在实时播放期间禁用所有媒体控制。

要实现这种级别的精细控制,开发者有两种选择:

  • 使用navigator.mediaSession的基于网页的实现。采用这种实现时,启用allowsDefaultMediaControl,原因如下。
    • 它支持对播放、暂停、向前跳过和向后跳过的传输控制。
    • 它直接在网页中实现,无需更改应用代码。
  • 在应用代码中直接与VegaMediaControl接口集成。采用这种集成时,禁用allowsDefaultMediaControl,原因如下。
    • 它提供了对设备媒体控制功能的完全访问权限。
    • 建议将它用于需要深度系统集成的应用。

使用navigator.mediaSession的基于网页的实现

navigator.mediaSession属性是一种网页标准,可在网页媒体内容和设备媒体控制系统之间提供强大的接口。它让网页应用能够为当前正在播放的媒体的媒体控制注册操作处理程序,以及自定义针对不同内容的媒体控制行为。

如果在WebView中将allowsDefaultMediaControl设置为true,则Alexa语音命令等系统级控件会传递给已注册的自定义处理程序(如果可用)。如果没有已注册的处理程序,WebView会恢复至其默认实现。然后,就可以根据播放上下文(例如在广告或直播期间)使用自定义处理程序。

例如,如果用户说“Alexa, fast forward”(Alexa,快进)且allowsDefaultMediaControl设置为true,则WebView会将命令传递给该网页的向前搜索处理程序(如果该处理程序已注册)。如果开发者未注册处理程序,则WebView会回退到其默认搜索实现。此功能让网页可以根据播放的内容控制搜索行为,例如在广告期间屏蔽搜索。

使用navigator.mediaSession的示例

已复制到剪贴板。

// 网页JavaScript代码(将包含在网页中)
// 获取对视频元素的引用
const video = document.querySelector('video');
if ('mediaSession' in navigator) {
    // 覆盖播放操作
    navigator.mediaSession.setActionHandler('play', () => {
        // 此处为自定义播放逻辑
        video.play();
    });
    // 覆盖暂停操作
    navigator.mediaSession.setActionHandler('pause', () => {
        // 此处为自定义暂停逻辑
        video.pause();
    });
    // 覆盖搜索操作
    navigator.mediaSession.setActionHandler('seekbackward', (details) => {
        // 此处为自定义搜索逻辑
        const skipTime = details.seekOffset || 10;
        video.currentTime = Math.max(video.currentTime - skipTime, 0);
    });
    navigator.mediaSession.setActionHandler('seekforward', (details) => {
        // 此处为自定义搜索逻辑
        const skipTime = details.seekOffset || 10;
        video.currentTime = Math.min(video.currentTime + skipTime, video.duration);
    });
}

// React Native应用代码(将在您的应用中实现)
return (
    <View style={styles.sectionContainer}>
      <WebView
        ref={webRef}
        hasTVPreferredFocus={true}
        allowsDefaultMediaControl={true}  // navigator.mediaSession正常工作所必需
        source={{
          uri: "https://example.com",  // 替换为您的URL
        }}
        javaScriptEnabled={true}
      />
    </View>
);

在应用中集成VegaMediaControl接口

VegaMediaControl为Vega应用中的媒体播放管理提供了接口。这种直接集成可实现对媒体功能(例如播放控制、搜索操作等)的精细控制。

要将您的Vega网页应用与VegaMediaControl集成,请执行以下关键步骤:

  1. 配置WebView属性。

    • 默认情况下,WebView中的默认媒体控制处于禁用状态。要让控制保持禁用状态,要么让属性保持未设置状态,要么明确地将allowsDefaultMediaControl设置为false。要使用VegaMediaControl集成,请让媒体控制保持禁用状态。
  2. 实现VegaMediaControl接口。

    • VegaMediaControl集成添加到您的应用
  3. JavaScript桥通信

    • 使用injectJavaScript来根据VegaMediaControl处理程序控制网页播放器。

在应用代码中集成VegaMediaControl的示例

以下提供了一个详细示例,演示如何处理暂停、播放、向前搜索和向后搜索命令。在Vega网页应用中,所有这些命令都使用VegaMediaControl

  1. manifest.toml中添加对VegaMediaControl的支持。更新VegaMediaControl的清单以及其他条目。

    已复制到剪贴板。

     [components]
     [[components.interactive]]
     categories = ["com.amazon.category.kepler.media"]
    
     [[extras]]
     key = "interface.provider"
     component-id = "com.amazondeveloper.keplerwebapp.main"
    
     [extras.value.application]
     [[extras.value.application.interface]]
     interface_name = "com.amazon.kepler.media.IMediaPlaybackServer"
     command_options = [
         "Play",
         "Pause",
         "SkipForward",
         "SkipBackward",
     ]
    
  2. 在package.json中,添加以下依赖项。

    已复制到剪贴板。

     "@amazon-devices/kepler-media-controls": "~1.0.0",
     "@amazon-devices/kepler-media-types": "~1.0.0",
    
  3. 以下应用代码示例创建了VegaMediaControl服务器和关联的处理程序。这些处理程序可通过自定义行为将JavaScript注入网页。

    已复制到剪贴板。

     import { WebView } from "@amazon-devices/webview";
     import { useEffect, useRef, useMemo } from "react";
     import { StyleSheet, View } from "react-native";
     import {
       Action,
       IMediaControlHandlerAsync,
       IMediaMetadata,
       IMediaSessionId,
       ITimeValue,
       ITrack,
       MediaControlServerComponentAsync,
       MediaSessionState,
       RepeatMode,
     } from "@amazon-devices/kepler-media-controls";
     import { IComponentInstance, useComponentInstance } from "@amazon-devices/react-native-kepler";
        
     export const App = () => {
       const webRef = useRef(null);
       const componentInstance: IComponentInstance = useComponentInstance();
        
       const mediaControlsHandler: IMediaControlHandlerAsync = useMemo(
         (): IMediaControlHandlerAsync => ({
           handlePlay: function (sessionId?: IMediaSessionId): Promise<void> {
             console.log("媒体控制"["handlePlay"]);
             // 根据网页实现更新JS脚本。
             webRef.current?.injectJavaScript("document.querySelector('video')?.play();");
             return Promise.resolve();
           },
           handlePause: function (sessionId?: IMediaSessionId): Promise<void> {
             console.log("媒体控制"["handlePause"]);
             // 根据网页实现更新JS脚本。
             webRef.current?.injectJavaScript("document.querySelector('video')?.pause();");
             return Promise.resolve();
           },
           handleTogglePlayPause: function (sessionId?: IMediaSessionId): Promise<void> {
             console.log("媒体控制"["handleTogglePlayPause"]);
             // 根据网页实现注入javascript
             return Promise.resolve();
           },
           handleStop: function (sessionId?: IMediaSessionId): Promise<void> {
             console.log("媒体控制"["handleStop"]);
             // 根据网页实现注入javascript
             return Promise.resolve();
           },
           handleStartOver: function (sessionId?: IMediaSessionId): Promise<void> {
             console.log("媒体控制"["handleStartOver"]);
             // 根据网页实现注入javascript
             return Promise.resolve();
           },
           handleFastForward: function (sessionId?: IMediaSessionId): Promise<void> {
             console.log("媒体控制"["handleFastForward"]);
             // 根据网页实现注入javascript
             return Promise.resolve();
           },
           handleRewind: function (sessionId?: IMediaSessionId): Promise<void> {
             console.log("媒体控制"["handleRewind"]);
             // 根据网页实现注入javascript
             return Promise.resolve();
           },
           handleGetMetadataInfo: function (id: MediaId): Promise<IMediaMetadata> {
             console.log("媒体控制"["handleGetMetadataInfo"id]);
             return Promise.resolve({
               mediaId: id.contentId,
               title: 'Sample Title',
               date: '2024-01-01',
               artwork: [
                 {
                   url: 'https://example.com/artwork1.jpg',
                   sizeTag: 'medium',
                   id: 'sample artwork id1',
                   tag: 'fantastic',
                 },
                 {
                   url: 'https://example.com/artwork2.jpg',
                   sizeTag: 'large',
                   id: 'sample artwork id2',
                   tag: 'wonderful',
                 },
               ],
             });
           },
           handleGetSessionState: function (sessionId?: IMediaSessionId): Promise<MediaSessionState[]> {
             console.log("Media Controls", ["handleGetSessionState"]);
             // 根据网页实现注入javascript
             return Promise.resolve([]);
           },
         }),
         []
       );
        
       useEffect(() => {
         const mediaControlServer = MediaControlServerComponentAsync.getOrMakeServer();
         mediaControlServer.setHandlerForComponent(mediaControlsHandler, componentInstance);
       }, [componentInstance, mediaControlsHandler]);
        
       const styles = StyleSheet.create({
         sectionContainer: {
           flex: 1,
         },
       });
        
       return (
         <View style={styles.sectionContainer}>
           <WebView
             ref={webRef}
             hasTVPreferredFocus={true}
             source=
             javaScriptEnabled={true}
           />
         </View>
       );
     };
    

将存储在设备上的本地文件URL加载到WebView中

默认情况下,WebView从/pkg/assets目录加载应用。但是,如果要从其他目录加载应用文件,则必须将属性设置为true。要加载存储在设备上的本地应用文件URL,请执行以下操作。

  • src/App.tsx中,将allowFileAccess属性设置为true

如果将属性设置为true,会让WebView能够访问并显示所选目录中的应用文件。如果未将allowFileAccess设置为true,尝试从其他目录加载应用文件时会导致“拒绝访问”错误。文件应用在沙盒环境中运行,限制了其对设备文件系统的访问。下表介绍了文件应用可以访问的目录。

路径 可写入 描述
/pkg 应用程序包的根目录
/pkg/assets 由程序包安装的资产
/pkg/lib 由程序包安装的应用入口点组件和任何其他库
/data 应用数据的可写入位置。在设备重启和程序包升级期间保持不变。应用或服务之间不会共享。
/tmp 临时存储。其他应用或服务不会共享/tmp。非持久性存储,重启会将其删除。
/proc 应用只可使用/proc/self

示例: 使用WebView从assets目录加载HTML文件

已复制到剪贴板。

<View>
    <WebView
        source={{
            uri: "file:///pkg/assets/sample.html",
        }}
    />
</View>

示例: 使用WebView从应用数据目录加载HTML文件

已复制到剪贴板。

<View>
    <WebView
        source={{
            uri: "file:///data/sample.html",
        }}
        allowFileAccess={true}
    />
</View>

WebView中的方向键导航

WebView默认启用了空间导航,以提供基本的方向键导航。

阻止WebView中的空间导航

对于实现自定义焦点管理而非默认空间导航焦点的应用,应用在捕获按键按下事件时可以使用Event::preventDefault。使用Event::preventDefault可以避免自定义焦点逻辑和内置的WebView空间导航之间发生冲突。

要管理序列化的Cookie或清除应用的Cookie,请参阅Vega网页应用Cookie管理器。要查看其他WebView API,请参阅Vega网页应用组件参考

如何防止页面加载前出现白屏闪烁

在加载网页之前,视图会使用组件的背景颜色。如果未设置背景颜色,则WebView会选择白色背景。要更改背景颜色,请更新WebView组件的backgroundColor样式。

已复制到剪贴板。


import { WebView } from "@amazon-devices/webview";
import * as React from "react";
import { useRef } from "react";
import { View, StyleSheet } from "react-native";

export const App = () => {
  const webRef = useRef(null);
  return (
    <View style={styles.container}>
      <WebView
        style={styles.webview}
        ref={webRef}
        hasTVPreferredFocus={true}
        source={{
          uri: "https://www.example.com",
        }}
        javaScriptEnabled={true}
        onLoadStart={(event) => {
          console.log("onLoadStart", event.nativeEvent.description)
        }}
        onLoad={(event) => {
          console.log("onLoad", event.nativeEvent.description)
        }}
        onError={(event) => {
          console.log("onError", event.nativeEvent.description)
        }}
      />
    </View>
  );
};

// 布局和背景颜色的样式
const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  webview: {
    backgroundColor: "#000000"
  }
});

根据需要修改WebView样式的背景颜色。

如何移除视频播放屏幕顶部的远程播放图标

如果WebView在屏幕顶部显示图标,这可能是disableRemotePlayback属性造成的。可以通过将该属性设置为true来移除该图标。该属性为true表示它已被禁用,false表示它保持启用状态。

示例如下:

已复制到剪贴板。

const obj = document.createElement("audio");
obj.disableRemotePlayback = true;

Last updated: 2026年4月27日