as

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

账户登录集成指南

账户登录集成指南

Vega媒体账户登录API为应用向系统报告其身份验证状态提供了一种方法。

应用需要在每次启动时刷新用户的登录状态,这通过账户登录API实现。有两种账户登录状态:登录和注销。通过保持最新的登录状态,应用可确保用户有权访问相应的内容和功能。借助此接口,无论是在应用启动时,还是在应用登录状态发生变化时,应用都可以传递表示用户是否已登录的布尔值。系统还可以根据需要查询此状态,从而实现个性化用户体验。

产品可以利用这些信息来展示或筛选已验证用户身份的服务中的内容,从而让用户即时访问内容。

账户登录功能提供两个用于管理身份验证状态的关键组件:updateStatus方法和handleReadStatus回调函数。updateStatus方法报告应用的当前登录状态,以便在身份验证状态发生变化时进行实时更新。handleReadStatus回调函数可以响应有关应用当前登录状态的系统查询。两种方法都使用StatusType来表示账户状态,可能有两个值: SIGNED_INSIGNED_OUT

Vega媒体账户登录API参考

有关此API的参考文档,请参阅账户登录API

Vega媒体账户登录常见用例

如果出现以下情况,应用应向系统更新其登录状态:

  • 在应用初次启动期间
  • 当用户的订阅状态发生变化时
  • 应其他服务的要求

安装和设置Vega媒体账户登录

步骤1: 更新应用清单

使用服务组件来处理账户登录交互的一部分。熟悉服务组件,也称为无头组件。让一项服务与交互式组件一起运行,使这种集成更易于执行和故障排除。请参阅服务组件文档

首先,更新您的应用清单以使用Vega媒体账户登录API。修改清单条目时,请将com.amazondeveloper.media.sample替换为应用的程序包ID,如下例所示。账户登录API通常作为一项服务来实现,以提供账户登录状态,尤其是在读取操作期间。不要求用户打开基于交互式界面的应用。媒体提供者应用通常以交互式应用的形式实现,以处理用户界面。您的交互式和服务应用应交换账户登录信息。您可以使用共享文件或其他进程间通信机制来执行此操作。

以下示例显示了内容启动器和账户登录API的示例清单。它定义了交互式组件和服务组件。

已复制到剪贴板。

schema-version = 1

[package]
title = "<应用标题>"
id = "com.amazondeveloper.media.sample"

[components]

[[components.interactive]]
id = "com.amazondeveloper.media.sample.main"
runtime-module = "/com.amazon.kepler.keplerscript.runtime.loader_2@IKeplerScript_2_0"
launch-type = "singleton"
# 类别“com.amazon.category.kepler.media”仅对主要组件是必需的,该组件在
# 清单的 [[extras]] 部分中使用“component-id”值进行标识。
categories = ["com.amazon.category.main", "com.amazon.category.kepler.media"]

[[components.service]]
id = "com.amazondeveloper.media.sample.interface.provider"
runtime-module = "/com.amazon.kepler.headless.runtime.loader_2@IKeplerScript_2_0"
launch-type = "singleton"

[processes]

[[processes.group]]
component-ids = ["com.amazondeveloper.media.sample.main"]

[[processes.group]]
component-ids = ["com.amazondeveloper.media.sample.interface.provider"]

[offers]

[[offers.interaction]]
id = "com.amazondeveloper.media.sample.main"

[[offers.service]]
id = "com.amazondeveloper.media.sample.interface.provider"
required-privileges = ["com.amazon.multimedia.privilege.session.manage"]

[[message]]
uri = "pkg://com.amazondeveloper.media.sample.main"
# 匹配 [[offers.interaction]] 中使用的权限。如果未添加权限,则使用“*”。
sender-privileges = ["*"]
receiver-privileges = ["self"]

[[offers.module]]
id = "/com.amazondeveloper.media.sample.module@ISomeUri1"
includes-messages = ["pkg://com.amazondeveloper.media.sample.main"]

[[extras]]
key = "interface.provider"
component-id = "com.amazondeveloper.media.sample.main"

[extras.value.application]
[[extras.value.application.interface]]
interface_name = "com.amazon.kepler.media.IContentLauncherServer"
attribute_options = ["partner-id"]
static-values = { "partner-id" = "<您的合作伙伴ID>" }

[[extras.value.application.interface]]
interface_name = "com.amazon.kepler.media.IAccountLoginServer"
attribute_options = ["Status"]
# 在这个清单示例中,我们定义了内容启动器和账户登录接口。
# 由于账户登录集群应该由服务组件而不是
# 交互式组件来处理,因此我们使用“override_attribute_component”将对
# “Status”属性的调用重定向到服务组件。
override_attribute_component = { Status = "com.amazondeveloper.media.sample.interface.provider" }

[needs]

[[needs.module]]
# 这种格式故意在“media”后面添加了一个句点 (.)。未来版本将会更改
# 这一表示法。
id = "/com.amazon.kepler.media.@IAccountLogin1"

[[needs.module]]
id = "/com.amazon.kepler.media@IContentLauncher1"

媒体应用通常需要与三个关键API集成:

  • 用于查找和启动内容的内容启动器集群API
  • Vega媒体控制API,用于控制启动的媒体
  • 账户登录API用于向系统传达账户登录状态

内容启动器和Vega媒体控制API通常作为交互式组件实现,用于处理Vega媒体查询。此组件在应用清单的附加部分使用interface.provider键指定,如以下TOML代码段和前面的示例所示。

已复制到剪贴板。

[[extras]]
key = "interface.provider"
component-id ="<主要交互式组件ID>"

账户登录API是作为服务应用实现的,让系统可以在不唤醒整个应用的情况下查询账户登录状态。账户登录API使用单个“状态”属性,该属性由服务组件处理。为确保正确路由登录状态属性交互,应用清单必须使用“override_attribute_component”设置指定服务组件,如本TOML示例以及上面的示例所示。

已复制到剪贴板。

override_attribute_component = { Status = "<service component-id implementing Account Login api>" }

使用该方法优化了系统性能,并明确区分媒体控制和账户管理功能。

步骤2: 纳入程序包依赖项

Vega媒体账户登录API通过系统Turbo模块amzn/kepler-media-account-login提供给TypeScript开发者。这个Turbo模块是SDK程序包的一部分。要使用该API,请更新package.json文件以将Turbo模块作为依赖项,然后添加amzn/headless-task-manager来创建无头服务应用。

已复制到剪贴板。

"dependencies": {
  "@amazon-devices/kepler-media-account-login": "^1.1.0",
  "@amazon-devices/headless-task-manager": "^1.1.0"
}

步骤3: 添加Vega媒体账户登录业务逻辑

创建一个名为AccountLoginWrapper.ts的文件,以包含Vega媒体账户登录API的核心业务逻辑。在此文件中,创建并导出一个名为AccountLoginWrapper的类。这个类只有一个成员变量:IAccountLoginServerAsync接口的实例,用于与账户登录服务器进行交互。该文件还将在全局创建一个AccountLoginServerComponent的实例,并将其存储在名为accountLoginServerComponent的变量中。

已复制到剪贴板。

import { IComponentInstance } from '@amazon-devices/react-native-kepler';
import {
  AccountLoginServerComponent,
  IAccountLoginHandlerAsync,
  StatusType,
  IAccountLoginServerAsync,
  IStatus,
} from '@amazon-devices/kepler-media-account-login';
import AsyncStorage from '@react-native-async-storage/async-storage';

const accountLoginServerComponent = new AccountLoginServerComponent();

export class AccountLoginWrapper {
  accountLoginServer?: IAccountLoginServerAsync;
}

步骤4: 异步返回账户登录状态

现在使用IAccountLoginHandlerAsync接口,实现不采用任何参数并返回Promise<IStatus>handleReadStatus() 方法。之所以采用这种方式设计方法,是因为我们需要确保服务应用可以在不屏蔽调用者的情况下异步返回账户登录状态。Promise<IStatus>返回类型表明,该方法最终会使用一个IStatus对象(其中可能包含有关当前登录状态的信息)进行解析。

稍后在步骤8中创建的无头服务调用handleReadStatus() 处理程序。由于交互式组件(用户界面应用)和无头服务在不同的进程中运行,因此它们不共享内存状态。依赖内存中的变量是不够的。为确保两个组件之间的可靠通信,请使用共享机制来保存和传输登录状态。必须从该永久存储器异步读取登录状态。目前,无头服务支持React的AsyncStorage和Vega文件系统。MMKV受支持。在此示例中,我们将使用AsyncStorage

以下是一些利用AsyncStorage的组件实现示例。

已复制到剪贴板。

// 交互式组件
// 在“com.amazondeveloper.media.sample.main”里面。
const onLoginChange = async (newStatus: boolean) => {
  await AsyncStorage.setItem('loginStatus', newStatus);
};

已复制到剪贴板。

// 服务组件
//在 “com.amazondeveloper.media.sample.interface.provider” 里面。
const getLoginStatus = async (): boolean => {
  return await AsyncStorage.getItem('loginStatus');
};

注册handleReadStatus() 处理程序以在系统调用它时返回登录状态。如前所示,getLoginStatus() 必须通过永久存储器读取状态。getLoginStatus() 是特定于应用的。

已复制到剪贴板。

export class AccountLoginWrapper {
  async getAccountLoginStatus(): IStatus {
    return accountLoginServerComponent.makeStatusBuilder()
      .status(
        // 从永久存储器中获取状态。
        (await getLoginStatus())
          ? StatusType.SIGNED_IN
          : StatusType.SIGNED_OUT)
      .build();
  }

  createAccountLoginHandler(): IAccountLoginHandlerAsync {
    return {
      handleReadStatus: async (): Promise<IStatus> => {
        console.log('handleReadStatus() 已调用。')
        return await this.getAccountLoginStatus();
      }
    };
  }
}

接下来,在AccountLoginWrapper类中引入setupAccountLoginServer() 方法。此方法可接受IComponentInstance对象作为参数。按以下方式实现它:

  1. 创建IAccountLoginServerAsync的单例实例并将其存储在成员变量accountLoginServer中。
  2. 接下来,调用setHandlerForComponent(),传入在步骤4中创建的handleReadStatus() 处理程序以及已提供的ComponentInstance

已复制到剪贴板。

export class AccountLoginWrapper {
  setupAccountLoginServer(componentInstance: IComponentInstance) {
    console.log('setupAccountLoginServer() 已调用。');
    try {
      this.accountLoginServer = accountLoginServerComponent.getOrMakeServer();
    } catch (error) {
      this.accountLoginServer = undefined;
      console.error(
        'setupAccountLoginServer() 创建账户登录服务器失败:',
        error,
      );
      return;
    }

    try {
      this.accountLoginServer?.setHandlerForComponent(this.createAccountLoginHandler(), componentInstance);
    } catch (error) {
      console.error(
        'setupAccountLoginServer() 设置处理程序失败:',
        error,
      );
    }
    console.log('setupAccountLoginServer() 已完成。');
  }
}

步骤6: 更新账户登录状态

要更新账户登录状态,请异步使用updateStatus() 方法。在调用此函数之前,请务必将登录状态存储在永久存储器中。

已复制到剪贴板。

export class AccountLoginWrapper {
  async updateStatus(loginStatus: boolean) {
    console.log('updateStatus() 已调用。');
    const status = accountLoginServerComponent.makeStatusBuilder()
      .status(loginStatus ? StatusType.SIGNED_IN : StatusType.SIGNED_OUT,)
      .build();
    try {
      this.accountLoginServer?.updateStatus(status);
    } catch (error) {
      console.error(
        'updateStatus() 更新登录状态失败:',
        error,
      );
    }
  }
}

步骤7: 将账户登录服务器与服务生命周期集成

为了进行这种集成,AccountLoginWrapper类公开了两种方法:onStart()onStop()

onStart()

onStart() 方法通过使用提供的IComponentInstance调用setupAccountLoginServer() 来初始化账户登录服务器。

onStop()

onStop() 方法添加了服务停止时所需的清理逻辑。

实现这些方法后,创建一个名为AccountLoginWrapperInstance的单例实例,并使用它来公开两个等效的顶级方法:onStartService()onStopService()。对服务和交互式组件使用这些方法来启动和停止账户登录服务器。

已复制到剪贴板。

export class AccountLoginWrapper {
  onStart(componentInstance: IComponentInstance): Promise<void> {
    this.setupAccountLoginServer(componentInstance);
    return Promise.resolve();
  }

  onStop(): Promise<void> {
    // 在此处添加清理代码。
    return Promise.resolve();
  }
}

export const AccountLoginWrapperInstance = new AccountLoginWrapper();

export const onStartService = (componentInstance: IComponentInstance): Promise<void> => {
  return AccountLoginWrapperInstance.onStart(componentInstance);
};

export const onStopService = (): Promise<void> => {
  return AccountLoginWrapperInstance.onStop();
};

步骤8: 实现无头服务

无头服务属于后台进程,可在没有用户界面的情况下利用自带的React Native运行时运行。此服务是响应登录状态请求的关键所在,可以通过下面的步骤进行创建:

  1. 在应用的根目录中创建一个名为service.js的新文件。
  2. 导入在前面的步骤中创建的AccountLoginWrapper,并导入headless-task-manager

    已复制到剪贴板。

     import { HeadlessEntryPointRegistry } from '@amazon-devices/headless-task-manager';
     import { onStartService, onStopService } from './src/AccountLoginWrapper';
    
  3. 定义起始和停止入口点。

    已复制到剪贴板。

     HeadlessEntryPointRegistry.registerHeadlessEntryPoint2(
       'com.amazondeveloper.media.sample.interface.provider::onStartService',
       () => onStartService
     );
    
     HeadlessEntryPointRegistry.registerHeadlessEntryPoint2(
       'com.amazondeveloper.media.sample.interface.provider::onStopService',
       () => onStopService
     );
    
    • com.amazondeveloper.media.sample替换为您应用的实际程序包ID。
    • 入口点命名惯例如下:<应用程序包ID>.interface.provider::onStartService::onStopService

Fire TV系统组件会自动调用onStartService(),将账户登录服务器初始化为无头服务。在此过程中,将为特定服务组件实例注册handleReadStatus() 函数。

当系统稍后调用此处理程序时,handleReadStatus() 返回应用报告的当前登录状态(登录或注销)。

步骤9: 初始化账户登录服务器以报告登录状态

交互式组件(用户界面应用)还必须初始化账户登录服务器,以使用updateStatus() 方法报告用户的登录状态。

已复制到剪贴板。

// 在“com.amazondeveloper.media.sample.main”里面。
useEffect(() => {
  AccountLoginWrapperInstance.onStart(componentInstance);

  return () => {
    AccountLoginWrapperInstance.onStop();
  };
}, []);

步骤10: 发送登录状态更新

在以下情况下,交互式组件(用户界面应用)必须使用当前登录状态调用updateStatus() 方法:

  1. 当应用启动时。
  2. 当用户的登录状态发生变化时。

每次,在调用updateStatus() 之前,必须将状态存储在永久存储器中,以便handleReadStatus() 会检索并返回正确的值。这样,系统就能始终如一地反映用户的身份验证状态,从而提供流畅的用户体验。

此设置允许您的应用管理登录状态请求,即使主应用并未主动运行。

您现在已经创建了IAccountLoginServerComponentAsync的单个实例。我们已在handleReadStatus() 方法和IComponentInstance之间建立联系,从而任何读取请求都会指向服务组件。这样做将处理账户登录状态查询。它还调用updateStatus() 方法,该方法具有双重用途。应该调用它来强制更新登录状态,或者在状态一旦发生变化时就调用它。

Vega媒体账户登录故障排除

与账户登录集成时可能会出现一些问题,但以下是一些更常见的问题。

确认服务组件启动

看看您的updateStatus() 调用是否被选取。这确认了服务组件可以在不崩溃的情况下启动。

为此,请在运行kepler device launch-app com.your.app.id.interface.provider之前开始捕获日志,您应该能够看到服务组件完成其启动序列。您应该看到与您的服务组件名称相对应的日志,并查看在此阶段是否发生任何崩溃。在到达registerHeadlessEntryPoint() (onStartService) 入口点之后可以找到日志。

如果您没有看到此日志,则可能会看到指示故障或崩溃的日志。在这种情况下,Vega崩溃调试文档是有用的参考。

确认处理程序已调用

确定服务已成功启动后,使用setHandlerForComponent() 确认您的服务正确注册了处理程序。您可以围绕此调用添加日志,以确认沿循的是此路径。您将看到来自handle_set_response_handler的针对您服务组件ID的等效日志,以便进行确认。

您还应该围绕handleReadStatus() 添加日志,以确认它能按预期运行。


Last updated: 2025年9月30日