Todos los productos
Search
Centro de documentos

Alibaba Cloud Model Studio:Referencia de la API del SDK de Android de HappyOyster

Última actualización: Oct 12, 2026

El punto de entrada para el SDK de Android de HappyOyster es el objeto singleton HappyOyster. Excepto initialize, updateToken, attachVideo y sendCommand, todos los métodos de negocio son funciones suspendidas que lanzan SDKError en caso de fallo. Cubre los tres modos aventura / dirección / actuación, la configuración de modelo requerida y aspectRatio.

Si la corrutina invocadora se cancela, el SDK cancela la solicitud HTTP en tránsito de esa llamada, si la hay, y propaga CancellationException sin cambios en lugar de convertirla a SDKError. La cancelación no revierte una solicitud que el servidor ya haya aceptado.

Para el flujo de integración, instalación y mejores prácticas, consulte Guía de integración del SDK de Happy Oyster para Android.

Terminología principal

Término

Significado

token

API Key de la puerta de enlace de Bailian: inyectada como token Bearer por su aplicación mediante updateToken(token). El SDK conserva únicamente la más reciente; no la persiste ni la renueva. Cuando la Key cambia, debe inyectarla nuevamente. La puerta de enlace requiere que todas las solicitudes del SDK, incluida startTravel, utilicen este Bearer. Cuando la Key Bearer ha caducado o es inválida, el SDK lanza SDKError(101002); en ese caso, debe obtener e inyectar nuevamente la Key Bearer (updateToken), en lugar de solicitar un nuevo ticket. Recomendación de seguridad: el Bearer del lado del cliente debe utilizar un token de corta duración generado por su servidor, en lugar de incluir una API Key de larga duración en la aplicación o confirmarla en un repositorio de código.

ticket

Credencial de experiencia de un solo uso: intercambiada por su servidor a través de la plataforma abierta y entregada al cliente, utilizada únicamente para una sola ejecución de startTravel. Se vuelve inválida una vez que finaliza la experiencia (de manera normal o anómala) y no puede reutilizarse. Los errores de credenciales a nivel de ticket se identifican mediante códigos de servidor de seis dígitos (por ejemplo, 401010 = ticket inválido/vencido, 401011 = ticket ya utilizado), los cuales el SDK transmite textualmente.

Requisitos del entorno

Elemento

Requisito

minSdk

24 (Android 7.0) y superior

compileSdk

36

Lenguaje

Kotlin (APIs de corrutina suspend)

ABI

arm64-v8a / armeabi-v7a (el motor de comunicación en tiempo real incluye bibliotecas nativas)

Red

Se requiere acceso a Internet público

Conceptos principales

Concepto

Descripción

Mundo

Un mundo de IA, que contiene personajes y escenas. Creado y gestionado por su servidor.

Travel

Una única experiencia en tiempo real. Ciclo de vida básico: init → pending → running → completed (el fallo es failed). En los modos de dirección y Actuación, si la experiencia admite pausas, running puede entrar en paused mediante pauseTravel y luego volver a running mediante resumeTravel (la dirección también puede usar rewindTravel, tras lo cual el servidor reanuda la experiencia automáticamente); running ⇄ paused puede repetirse múltiples veces.

Modo

adventure (interacción dispositivo/comando), directing (narrativa impulsada por texto) o acting (Actuación: narrativa impulsada por texto cuya relación de aspecto de reproducción es fijada por el servidor en la creación del mundo). El SDK expone uniformemente estos tres valores.

Video en tiempo real

Establecida y mantenida automáticamente por el SDK después de que startTravel tenga éxito; no necesita conectar/desconectar manualmente. Se libera automáticamente en endTravel.

Visión general

Métodos de HappyOyster

Método

Descripción

initialize(context, config)

Inicializa el SDK. Se admite la reinicialización en inactividad; la reinicialización mientras un Travel está iniciando, activo o finalizando se rechaza con 103004.

updateToken(token)

Inyecta/actualiza la clave de API de la puerta de enlace de Bailian (token Bearer).

startTravel(ticket)

Inicia una experiencia con una credencial de un solo uso y establece automáticamente la conexión de video en tiempo real.

pauseTravel()

Pausa la experiencia de forma asíncrona (dirección y Actuación, y solo si la experiencia admite la pausa); tras la aceptación, la pausa real se determina mediante onStatusChanged(Paused).

resumeTravel()

Reanuda una experiencia pausada (dirección y Actuación); incluye retroceso de reintento de 3× internamente.

rewindTravel(rewindToSec)

Rebobina al número especificado de segundos (solo dirección, estado paused). El valor en segundos debe ser múltiplo de 4. La Actuación no admite rebobinado.

sendInstruct(content)

Envía una instrucción de texto para impulsar la narrativa (dirección y Actuación, running o paused).

sendCommand(command)

Envía comandos de control de dirección/vista/acción (modo aventura, solo running; hilo principal). No disponible para Actuación.

attachVideo()

Devuelve un SurfaceView para reproducción; agréguelo a su diseño para renderizar (hilo principal).

endTravel()

Finaliza la experiencia, desconectando automáticamente la conexión en tiempo real y liberando todos los recursos de la sesión.

VERSION

Cadena de versión del SDK (SemVer), una constante en tiempo de compilación.

Eventos

Evento

Descripción

onStatusChanged(status)

Cambio de estado de la experiencia (incluido el ciclo de vida del video en tiempo real); los valores se definen en TravelStatusValue.

onError(error)

Devolución de llamada cuando falla un flujo automático interno; un error fatal también termina la experiencia actual.

Tipos de datos clave

Tipo

Descripción

SDKConfig

Configuración de inicialización del SDK (apiHost y model son obligatorios, además de logLevel, logcatEnabled, logHandler y callbackTimeoutMs).

TravelStatusValue

Estado de la experiencia: Init / Pending / Running / Paused / Failed / Completed.

ModeValue

Modo de experiencia: adventure / directing / acting.

CreationModelValue

Modelo de creación de mundo: Simple (predeterminado, impulsado por instrucciones) / ScriptList (script estructurado, sendInstruct no admitido).

StartTravelData

Devuelto por startTravel; contiene metadatos como mode, version, creationModel, aspectRatio y maxExperienceTimeSec.

AdventureCommand

Parámetro para sendCommand; contiene los tres campos translation / rotation / interaction.

SDKError

Error del SDK; contiene code: Int y raw: Any? opcional.

HappyOyster.initialize

Inicializa el SDK; llámelo antes que cualquier otra API (preferiblemente en Application.onCreate). config es obligatorio y debe contener el apiHost de Bailian de su cuenta y un model de happyoyster-1.0-directing / happyoyster-1.0-acting / happyoyster-1.0-adventure (coincidiendo con la entrada de Open API). El SDK construye las URL de solicitud de la siguiente manera:

https://{apiHost}/api/v2/apps/{model}/openapi/v1/{endpoint}

model no tiene valor predeterminado: omitirlo al construir SDKConfig genera un error en tiempo de compilación. Pasar un valor en blanco hace que initialize lance sincrónicamente SDKError(100002); no se crea ni reemplaza ningún entorno de ejecución y no se realiza ninguna solicitud de red. El SDK conserva únicamente el contexto de la aplicación, no una Activity. La reinicialización solo se admite mientras el entorno de ejecución anterior está inactivo. Para cambiar de modelo, reinicialice con el nuevo model mientras esté inactivo; si un Travel se está iniciando, está activo o está finalizando, espere primero a endTravel().

Nota

Aviso de cumplimiento regional:
  • Para respaldar el cumplimiento de los requisitos legales y de datos aplicables, si sus usuarios previstos incluyen usuarios de EE. UU., debe configurar un API Host de la región de EE. UU. durante la inicialización del SDK para los servicios proporcionados a dichos usuarios.
  • Los desarrolladores son responsables de la configuración correcta y asumen la responsabilidad correspondiente bajo la ley aplicable por el incumplimiento de este requisito.

Firma

fun initialize(context: Context, config: SDKConfig)

Parámetros

Campo

Tipo

Obligatorio

Descripción

context

Context

Sí

Se recomienda pasar applicationContext.

config

SDKConfig

Sí

Configuración del SDK; debe llevar los obligatorios y sin valor predeterminado apiHost y model.

Devuelve

Sin valor de retorno.

Errores

code

Descripción

100002

model está vacío. La inicialización falla sincrónicamente con SDKError.raw establecido en SDKConfig.model must not be blank; no se crea ni reemplaza ningún entorno de ejecución y no se realiza ninguna solicitud de red.

103004

Reinicialización rechazada porque un Travel está iniciando, activo o finalizando. Espere a endTravel() y reintente.

HappyOyster.updateToken

Inyecta/actualiza la API Key de la puerta de enlace de Bailian. Es seguro para subprocesos y se puede llamar después de initialize; el SDK utiliza el token más reciente para startTravel y solicitudes posteriores de control de experiencia. El ticket es una credencial de experiencia de un solo uso y no debe confundirse con el token Bearer.

Firma

fun updateToken(token: String)

Parámetros

Campo

Tipo

Obligatorio

Descripción

token

String

Sí

La clave de API de la puerta de enlace de Bailian, utilizada como token Bearer.

Devuelve

Sin valor de retorno.

Errores

code

Descripción

100001

El SDK no ha sido inicializado.

HappyOyster.startTravel

Inicia una experiencia con un ticket de un solo uso. En caso de éxito, el SDK establece automáticamente la conexión de video en tiempo real e inicia el sondeo interno de estado, y el estado se expone mediante onStatusChanged.

  • ticket es una credencial de un solo uso y se considera consumida una vez realizada la llamada.
  • Solo se permite un Travel concurrente por aplicación en cualquier momento dado; llamar nuevamente mientras una experiencia está en curso lanza SDKError(103004), con SDKError.raw conteniendo solo un resumen redactado del ticket actualmente activo para diagnóstico, nunca el ticket completo.
  • Opcionalmente pase maxExperienceTimeSec para limitar la duración máxima de esta experiencia (solo exploración de mundo / modo aventura); consulte Parámetros.

StartTravelData.creationModel (CreationModelValue, valor predeterminado CreationModelValue.Simple): indica el modelo de creación del mundo, es decir, cómo se gestiona el contenido del guion. simple (predeterminado) es un mundo ordinario basado en instrucciones; los mundos de exploración también se normalizan a este valor. scriptlist es un mundo estructurado ScriptList — no se accede a su contenido de guion a través de este SDK. En el modo ScriptList (creationModel == CreationModelValue.ScriptList), la llamada a sendInstruct se rechaza y lanza SDKError(103002). Este campo tiene como valor predeterminado simple.

StartTravelData.aspectRatio (String?): la relación de aspecto de reproducción que el servidor asignó a esta sesión, como una cadena width:height. No es null solo para Actuación—"9:16" (vertical, el valor predeterminado del servidor al crear) o "16:9" (horizontal); null para los modos de exploración de mundo y dirección, y null cuando el servidor no reporta el valor. El SDK conserva los valores desconocidos textualmente (no los reduce a null), por lo que los anfitriones deben tratar cualquier valor no reconocido como null y recurrir a su propia orientación predeterminada; el SDK en sí no consume este campo.

El valor se entrega con el valor de retorno de startTravel(): dimensione el contenedor de reproducción basándose en él después de que startTravel() retorne y antes de llamar a attachVideo() y añadir la vista devuelta a su diseño — el SDK solo comienza a vincular el flujo remoto para renderizado una vez que el anfitrión adjunta la vista, por lo que decidir la orientación en ese momento todavía precede al primer fotograma renderizado (sin embargo, no está garantizado que ocurra antes de que el SDK se una a la sala en tiempo real). La vista remota se vincula con un modo de renderizado recortar para llenar, por lo que un contenedor cuya orientación no coincida con este valor recortará la imagen en lugar de aplicar letterboxing.

Firma

suspend fun startTravel(ticket: String): StartTravelData
suspend fun startTravel(ticket: String, maxExperienceTimeSec: Int?): StartTravelData

Parámetros

Campo

Tipo

Obligatorio

Descripción

ticket

String

Sí

La credencial de experiencia de un solo uso, proporcionada por su servidor.

maxExperienceTimeSec

Int?

No

Duración máxima de esta experiencia en segundos; el mundo finaliza la sesión automáticamente una vez alcanzada. Aplica únicamente al modo de exploración de mundo (aventura); se ignora en los modos de dirección (dirección en tiempo real) y Actuación. Los valores permitidos están configurados en el servidor, actualmente 60 / 90 / 120; pasar null (o usar la sobrecarga sin este parámetro) aplica el valor predeterminado del servidor (actualmente 60). Pasar un valor no admitido provoca que el servidor rechace startTravel con 400000, lanzando SDKError(400000) sin crear ningún Travel activo. El SDK no valida el valor localmente — el conjunto permitido es autoritativo en el servidor.

Devuelve

Devuelve StartTravelData, que contiene metadatos de la experiencia (mode, version, creationModel, aspectRatio, etc.). Inspeccione los metadatos devueltos antes de decidir cómo interactuar (p. ej., travel.mode, travel.creationModel, travel.version); para Actuación, también dimensione el contenedor de reproducción desde travel.aspectRatio.

Errores

code

Descripción

401010

ticket inválido o caducado

401011

ticket ya utilizado

400000

Parámetro inválido (p. ej., maxExperienceTimeSec no está dentro del conjunto permitido por el servidor); este startTravel falla al iniciar

403002

El Mundo no está en estado listo

500001

Asignación de recursos / fallo del servicio interno

103004

Un Travel ya se está iniciando, está activo o finalizando, o startTravel se llama concurrentemente (solo se permite un Travel a la vez; SDKError.raw contiene únicamente un resumen redactado del ticket activo)

HappyOyster.pauseTravel / HappyOyster.resumeTravel

Pausar / reanudar la experiencia. El SDK gestiona solo un Travel a la vez; encryptedTravelId se obtiene internamente por el SDK y no necesita ser pasado por el llamador.

Condiciones previas:

  • pauseTravel: llamable solo en dirección o Actuación y cuando el estado es running.
  • resumeTravel: llamable solo en dirección o Actuación y cuando el estado es paused.

No todas las experiencias admiten pausa/reanudación: la experiencia debe reportar el identificador de versión que requiere su modo (StartTravelData.version—storyV2 para dirección, actingV2 para Actuación; comparado ignorando espacios en blanco circundantes y mayúsculas/minúsculas), de lo contrario tanto pauseTravel como resumeTravel devuelven 103002. La Actuación admite pausa/reanudación pero no admite rewindTravel.

La pausa es asíncrona (importante): pauseTravel es una operación "pesada" — una llamada exitosa (retorno del método) solo significa que la pausa ha sido aceptada; en ese momento la experiencia aún no está realmente pausada. La pausa solo es efectiva una vez que la devolución de llamada onStatusChanged reporta paused. Por lo tanto, controle la máquina de estados del anfitrión y la habilitación de llamadas desde esa devolución de llamada: entre "llamar a pauseTravel" y "recibir la devolución de llamada paused" puede marcar el estado local como pausing; solo después de recibir paused debe permitir iniciar resumeTravel o rewindTravel. No considere que el retorno de pauseTravel implica que ya está pausado.

Manejo de la conexión en tiempo real: después de una pausa real, el SDK desconecta la conexión en tiempo real; al reanudar, el SDK se vuelve a unir automáticamente y restaura la imagen utilizando la credencial entregada durante el mismo startTravel, sin requerir intervención del host.

Barrera ordenada de desmantelamiento por pausa: después de que se emite Paused, el desmantelamiento de la sala en tiempo real en el lado del servidor aún puede retrasarse brevemente. El SDK establece una ventana de estabilización de 3 segundos a partir de la confirmación de la pausa. Si se llama a resumeTravel o rewindTravel dentro de esa ventana, la llamada de suspensión espera primero de forma no bloqueante durante el tiempo restante y luego envía la solicitud de API para reapertura de la sala. No se añade ningún retraso si ya han transcurrido naturalmente 3 segundos. Esto evita que un desmantelamiento tardío por pausa cierre la sala recién abierta y genere 105001.

Reintento de API de reanudación: resumeTravel reintenta internamente hasta 3 veces en caso de fallo (retroceso de 1 s / 2 s / 3 s) para manejar indisponibilidad breve del servicio tras una pausa; solo si fallan los 3 intentos se propaga el error hacia arriba.

Nota⚠️ Nota: Se recomienda que el host espere hasta 3 s después de que resumeTravel retorne exitosamente antes de iniciar pauseTravel nuevamente, para evitar cambios demasiado frecuentes. Esta es una recomendación de enfriamiento de llamadas del lado del host y no es impuesta por el propio SDK. Consulte la Guía de integración del SDK de Android de Happy Oyster para más detalles.

Firma

suspend fun pauseTravel(): TravelStateData
suspend fun resumeTravel(): TravelStateData

Parámetros

Sin parámetros.

Devuelve

Devuelve TravelStateData (contiene encryptedTravelId, status).

Errores

code

Descripción

103001

Sin experiencia activa

103002

Estado no permitido, o esta experiencia no admite pausa / reanudación

103003

Llamado en modo aventura; solo dirección y Actuación admiten pausa / reanudación

HappyOyster.rewindTravel

Rebobina al número especificado de segundos. encryptedTravelId se obtiene internamente por el SDK.

Precondiciones: solo se puede invocar en modo de dirección y cuando el estado es paused (no se permite rebobinar en el estado running). No todas las experiencias de dirección admiten el rebobinado, y las que no lo admiten devuelven 103002. Después de un rebobinado exitoso, la experiencia se reanuda automáticamente y el SDK reconecta RTC de forma automática, sin necesidad de intervención por parte del anfitrión.

La Actuación no tiene capacidad de rebobinado en absoluto: llamarla en Actuación es rechazado localmente por el SDK con 103003, y no se emite ninguna solicitud. En Actuación, los hosts deben ocultar el punto de entrada de rebobinado en lugar de simplemente deshabilitar el botón.

Firma

suspend fun rewindTravel(rewindToSec: Double): RewindTravelData

Parámetros

Campo

Tipo

Obligatorio

Descripción

rewindToSec

Double

Sí

El número objetivo de segundos al que rebobinar, que debe ser un múltiplo de 4 (p. ej., 4, 8, 12). Un valor no múltiplo de 4 es redondeado hacia abajo por el servidor al múltiplo menor más cercano de 4 (p. ej., pasar 7 produce 4).

Devuelve

Devuelve RewindTravelData (contiene encryptedTravelId, status, resumedAtSec), donde resumedAtSec es el número de segundos al que el servidor realmente rebobinó (ya redondeado hacia abajo al múltiplo más cercano de 4).

Errores

code

Descripción

103001

Sin experiencia activa

103002

El estado no es paused, o esta experiencia no admite rebobinado

103003

Llamado en modo aventura o Actuación; solo el modo dirección admite rebobinado

HappyOyster.sendInstruct

Envía una instrucción de texto para impulsar la narrativa. Válido en dirección o Actuación y cuando el estado de la experiencia es running o paused. encryptedTravelId se obtiene internamente por el SDK.

Comportamiento en estado pausado: el SDK no reanuda automáticamente mientras está pausado; la instrucción se envía directamente. Corresponde al host decidir si llamar primero a resumeTravel antes de enviar la instrucción.

Firma

suspend fun sendInstruct(content: String): SendInstructData

Parámetros

Campo

Tipo

Obligatorio

Descripción

content

String

Sí

El contenido de la instrucción de texto a enviar.

Devuelve

Devuelve SendInstructData (contiene encryptedTravelId, content, accepted).

Errores

code

Descripción

103001

Sin experiencia activa (nunca se llamó a startTravel, o ha finalizado)

103003

El modo actual no es ni dirección ni Actuación (llamado en modo aventura)

103002

El estado de la experiencia no es ni running ni paused (p. ej., todavía en la fase init/pending); o el mundo actual está en modo ScriptList (StartTravelData.creationModel == CreationModelValue.ScriptList)—no se permite enviar instrucciones en el modo ScriptList, donde los guiones son gestionados por la API de la plataforma Bailian

403004

Bloqueo por moderación de contenido

404000

El Travel no existe

HappyOyster.sendCommand

Envía comandos de control de dirección/vista/acción. Válido solo en modo aventura y cuando running.

  • Llame en el hilo principal; una llamada fuera del hilo principal lanza sincrónicamente IllegalStateException.
  • Si no hay experiencia activa, se reporta 103001.
  • Llamar fuera del modo aventura (dirección / Actuación) reporta 103003.
  • Un estado no permitido reporta 103002.
  • El enlace ascendente se establece mediante un flujo de audio silencioso (el SDK no graba ni carga audio real); RECORD_AUDIO no es un requisito estricto para el DataChannel, pero declararlo y otorgarlo es recomendable para garantizar la compatibilidad entre dispositivos (consulte Guía de integración del SDK de Android de Happy Oyster § Instalación · Permisos). 105004 significa que el canal en tiempo real no está listo o que falló un envío (por ejemplo, una interrupción de la conexión del DataChannel o una anomalía en el canal en tiempo real); no se activa directamente por la falta de un permiso.

Limitación de frecuencia integrada de 42 ms (24 fps, prevalece el último): sendCommand limita internamente las escrituras en el DataChannel en intervalos de 42 ms (aproximadamente 24 fps). La validación de estado se ejecuta sincrónica e inmediatamente, lanzando un error al instante ante una llamada ilegal; la escritura real en el DataChannel se limita de forma asíncrona. Si han transcurrido al menos 42 ms desde el envío anterior, el comando se despacha inmediatamente (incluida la primera llamada). De lo contrario, el comando pendiente se reemplaza con el valor más reciente y se despacha una sola vez al final de la ventana actual de 42 ms. Por lo tanto, múltiples llamadas en una misma ventana producen una única escritura en la red que contiene el último valor. El anfitrión puede llamar a sendCommand a la tasa de fotogramas del juego sin implementar su propio limitador de frecuencia.

Fallos de envío durante la limitación: el vaciado limitado es asíncrono, y un fallo de envío no se puede lanzar al invocador; el error se expone mediante el callback onError (no fatal, 105004). Los comandos pendientes se descartan cuando finaliza la sesión (no se enviarán después de que termine la sesión).

Campos y valores de AdventureCommand:

Campo

Semántica

Valores

translation

Movimiento: adelante/izquierda/atrás/derecha/diagonal/inactivo

W / A / S / D / W_A / W_D / S_A / S_D / None

rotation

Vista: arriba/abajo/izquierda/derecha/diagonal/none

Mouse_Up / Mouse_Down / Mouse_Left / Mouse_Right / Mouse_Up_Left / Mouse_Up_Right / Mouse_Down_Left / Mouse_Down_Right / None

interaction

Interacción: saltar/atacar/agacharse/correr/none

Jump / Attack / Squat / Sprint / None

Los tres campos son grupos de comandos mutuamente excluyentes e independientes. El movimiento/vista diagonal utiliza un único valor combinado (por ejemplo, adelante+izquierda es W_A, no W y A concurrentes en el mismo campo). Cada llamada debe llevar el estado actual completo.

Mejores prácticas: acciones instantáneas frente a acciones mantenidas

Utilice el intervalo de 42 ms como modelo mental:

  • Acción instantánea (por ejemplo, tocar saltar/atacar o dar un paso): llamar una vez. El SDK la envía en el intervalo más cercano; no se requieren llamadas repetidas ni un comando de seguimiento None.

    // Jump once
    HappyOyster.sendCommand(AdventureCommand("None", "None", "Jump"))
    
  • Acción mantenida (por ejemplo, seguir moviéndose o rotando): llamar cada fotograma mientras se mantiene. El SDK emite aproximadamente un comando cada 42 ms. Al soltar, envíe explícitamente un comando que contenga None para restablecer el estado. El SDK nunca genera un comando de restablecimiento automáticamente.

    // While held: call from the host's frame loop
    HappyOyster.sendCommand(AdventureCommand("W", "None", "None"))
    // On release: explicitly reset once
    HappyOyster.sendCommand(AdventureCommand("None", "None", "None"))
    

Firma

fun sendCommand(command: AdventureCommand)

Parámetros

Campo

Tipo

Obligatorio

Descripción

command

AdventureCommand

Sí

Un objeto de comando que contiene los tres campos translation, rotation, interaction.

Devuelve

Sin valor de retorno (sincrónico). La validación de estado se ejecuta sincrónica e inmediatamente; la escritura en DataChannel se limita asincrónicamente.

Errores

code

Descripción

103001

Sin experiencia activa

103002

Estado no permitido

103003

Llamado fuera del modo aventura (dirección / Actuación)

105004 (a través de onError)

Fallo de envío de vaciado limitado (no fatal, devolución de llamada asíncrona)

HappyOyster.attachVideo

Devuelve un SurfaceView para reproducción, que usted agrega a su diseño; el SDK completa internamente el enlace de renderizado con el flujo remoto.

  • Llame en el hilo principal; una llamada fuera del hilo principal lanza sincrónicamente IllegalStateException.
  • El SDK mantiene solo una referencia débil a la Vista devuelta y libera el enlace de renderizado cuando finaliza la experiencia; debe eliminar la Vista del diseño usted mismo.
  • Para experiencias de Actuación, dimensione el contenedor de reproducción desde StartTravelData.aspectRatio antes de agregar la vista devuelta a su diseño: la renderización está vinculada con un modo clip-to-fill, por lo que una orientación no coincidente recorta la imagen (ver startTravel).

Firma

fun attachVideo(): SurfaceView

Parámetros

Sin parámetros.

Devuelve

Devuelve un SurfaceView; agréguelo a su diseño para reproducir el video en tiempo real.

Errores

code

Descripción

100001

SDK no inicializado

HappyOyster.endTravel

Finaliza la experiencia. encryptedTravelId se obtiene internamente por el SDK. Después de una llamada exitosa (o una salida anormal), el SDK desconecta automáticamente la conexión en tiempo real, detiene el sondeo interno y libera todos los recursos de la sesión, y el ticket actual se invalida al mismo tiempo.

Firma

suspend fun endTravel(): EndTravelData

Parámetros

Sin parámetros.

Devuelve

Devuelve EndTravelData (contiene encryptedTravelId, status, endedAt, durationSec).

Errores

code

Descripción

100001

SDK no inicializado

103001

Sin experiencia activa

HappyOyster.VERSION

Devuelve la cadena de versión del SDK (SemVer), como "x.y.z". Este valor es una constante de tiempo de compilación inyectada por la propiedad Gradle VERSION_NAME; se puede leer de forma segura sin necesidad de llamar primero a initialize.

Firma

val VERSION: String

Devuelve

La cadena de versión del SDK, como "x.y.z".

Log.d("MyApp", "SDK version: ${HappyOyster.VERSION}")

Escucha de eventos

interface HappyOysterListener {
    fun onStatusChanged(status: TravelStatusValue) {}
    fun onError(error: SDKError) {}
}

fun addListener(listener: HappyOysterListener)
fun removeListener(listener: HappyOysterListener)

Los eventos del SDK son un canal de envío proactivo hacia usted, utilizado para informar situaciones no desencadenadas por sus propias llamadas explícitas (p. ej., problemas en la conexión en tiempo real o en el sondeo de estado que el SDK mantiene automáticamente).

Evento

Descripción

onStatusChanged

Cambio de estado de la experiencia (incluido el ciclo de vida del video en tiempo real); los valores se definen en TravelStatusValue.

onError

Devolución de llamada cuando falla un flujo automático interno; un error fatal también termina la experiencia actual (consulte la sección de códigos de error).

Nota⚠️ Nota: addListener / removeListener deben llamarse después de initialize; llamarlos antes de la inicialización lanza SDKError(100001). Se recomienda registrar los listeners inmediatamente después de que HappyOyster.initialize(...) retorne exitosamente.

Modelos de datos

// Configuration
data class SDKConfig(
    // Required: your account's Bailian API Host (e.g., llm-xxxx.ap-southeast-1.maas.aliyuncs.com), copied from the API Key page in the Bailian console;
    // must belong to the same account/region as the injected API Key, otherwise the gateway returns AccessDenied.
    val apiHost: String,
    // Required with no default: the complete HappyOyster model name and version (e.g., happyoyster-1.0);
    // refer to the official Bailian HappyOyster model documentation for available values.
    val model: String,
    // Minimum level for the built-in Logcat sink (does not affect logHandler); defaults to INFO, which
    // includes the lifecycle anchors (initialize, Travel start/status/end, RTC connect/first-frame) needed
    // to reconstruct a session timeline. Drop to WARN for errors-only, or raise to DEBUG/VERBOSE to diagnose.
    val logLevel: LogLevel = LogLevel.INFO,
    // Timeout for RTC join (timeout triggers 105002), first-frame wait (timeout triggers 105003), and SDK gateway HTTP signaling calls (timeout triggers 105005);
    // the two RTC phases are serial, so the worst-case wait is 2×callbackTimeoutMs (60s).
    val callbackTimeoutMs: Long = SDKConfig.DEFAULT_CALLBACK_TIMEOUT_MS, // 30_000ms
    // Whether the SDK writes its own logs to Android Logcat (tag HappyOysterSDK); defaults to false (silent).
    val logcatEnabled: Boolean = false,
    // Host log callback; receives the full stream of SDK LogRecords, unaffected by logLevel; defaults to null.
    val logHandler: HappyOysterLogHandler? = null,
) {
    companion object {
        /** Default callback timeout (milliseconds). Public constant, usable for comparison or display. */
        const val DEFAULT_CALLBACK_TIMEOUT_MS: Long = 30_000
    }
}

enum class LogLevel { VERBOSE, DEBUG, INFO, WARN, ERROR, NONE }

// Host log sink; injected via SDKConfig.logHandler; full firehose, unaffected by logLevel.
fun interface HappyOysterLogHandler {
    fun onLog(record: LogRecord)
}

// SDK structured log record; delivered to HappyOysterLogHandler; contains no sensitive values (Bearer /
// ticket / RTC token, RTC identity fields, and media URLs are all redacted). travelId (encryptedTravelId)
// is kept in full so it can be correlated with server-side session logs.
data class LogRecord(
    val level: LogLevel,
    val tag: String,        // fixed as "HappyOysterSDK"
    val message: String,    // readable log line (includes event name and details)
    val throwable: Throwable?,
    val timestampMs: Long,  // epoch milliseconds at emission time
)

// Status and mode (unknown values are preserved to allow service extension)
@JvmInline value class TravelStatusValue(val rawValue: String) {
    companion object {
        val Init = TravelStatusValue("init")
        val Pending = TravelStatusValue("pending")
        val Running = TravelStatusValue("running")
        val Paused = TravelStatusValue("paused")
        val Failed = TravelStatusValue("failed")
        val Completed = TravelStatusValue("completed")
    }
}
@JvmInline value class ModeValue(val rawValue: String) {
    companion object {
        val Adventure = ModeValue("adventure")
        val Directing = ModeValue("directing")
        val Acting = ModeValue("acting")
    }
}
@JvmInline value class CreationModelValue(val rawValue: String) {
    companion object {
        val Simple = CreationModelValue("simple")         // default; prompt/instruct-driven, world-exploration worlds normalize to this
        val ScriptList = CreationModelValue("scriptlist") // structured ScriptList world; sendInstruct not supported (throws 103002)
    }
}

// startTravel return
data class StartTravelData(
    val encryptedTravelId: String, // identifier for subsequent control interfaces
    val encryptedWorldId: String,
    val mode: ModeValue,           // adventure / directing / acting
    val creationModel: CreationModelValue = CreationModelValue.Simple, // world creation model; Simple (default) is instruct-driven, ScriptList does not support sendInstruct
    val playUrl: String?,
    val firstFrame: String?,       // first-frame image address, may be produced asynchronously
    val bgmUrl: String?,
    val version: String,           // world version identifier; Acting is actingV2
    val aspectRatio: String? = null, // Acting only: 9:16 / 16:9; other modes null. Use to set player orientation
    val maxExperienceTimeSec: Int? = null, // server-reported max experience time (seconds); non-null only for adventure; null for directing / acting
)

// Control interface parameters and returns
data class AdventureCommand(
    val translation: String, // movement: forward/left/back/right/diagonal (W_A, …)/idle
    val rotation: String,    // view: up/down/left/right/diagonal (Mouse_Up_Left, …)/none
    val interaction: String, // interaction: jump/attack/squat/sprint/none
)
data class TravelStateData(val encryptedTravelId: String, val status: TravelStatusValue)
data class RewindTravelData(val encryptedTravelId: String, val status: TravelStatusValue, val resumedAtSec: Double)
data class EndTravelData(val encryptedTravelId: String, val status: TravelStatusValue, val endedAt: String, val durationSec: Int)
data class SendInstructData(val encryptedTravelId: String, val content: String, val accepted: Boolean)

// Error (identified by code; the original information is in raw)
// Note: SDKError is not a data class (no copy()/destructuring), it is a plain class.
class SDKError(val code: Int, val raw: Any? = null) : Exception("Happy Oyster SDK error: $code")

Códigos de error

Los errores se identifican mediante un code numérico. El SDK transmite los códigos de error de negocio que el invocador puede manejar (comúnmente 4xxxxx / 5xxxxx); los códigos de error locales del SDK son 1xxxxx.

Códigos de error de negocio (comunes)

code

Significado

Manejo sugerido

400000

Parámetro no válido (enum no válido, etc.)

Verifique los parámetros de la solicitud o la versión del SDK

401010

ticket inválido o caducado

Haga que su servidor vuelva a emitir una credencial

401011

ticket ya utilizado

La credencial es de un solo uso; vuélvala a emitir

403001

El mundo no existe, ha sido eliminado o no pertenece al desarrollador actual (incluido un mundo ya eliminado dentro de la credencial de startTravel)

Vuelva a seleccionar un Mundo válido

403002

El Mundo no está en estado listo

Espere hasta que el mundo esté listo antes de comenzar

403004

Violación de contenido de entrada (moderación de contenido); aplica a instrucciones de texto de sendInstruct

Modifique el contenido de entrada e inténtelo de nuevo

403007

Especificación de servicio no habilitada para esta cuenta

No reintente como capacidad llena; cambie a una especificación habilitada o solicite la habilitación

403008

Configuración de capacidad temporalmente no disponible

Reintentar más tarde

404000

El recurso Travel no existe o el ID no pertenece a la cuenta actual

Reiniciar el Travel

409000

La solicitud entra en conflicto con el estado actual del recurso

Verificar el estado del Travel

429001

Límite de concurrencia de SKU alcanzado

Reintente después de que finalice una sesión existente (no confundir con 500001)

429002

No hay capacidad disponible actualmente

Reintentar más tarde

500001

Asignación de recursos de inferencia / fallo del servicio interno

Reintentar más tarde

500000

Error interno del sistema

Reintentar más tarde / reportar

Códigos de error locales del lado del cliente

code

Significado

¿Fatal?

Manejo sugerido

100001

Llamado antes de que el SDK fuera inicializado

Llamada rechazada

initialize primero

100002

model estaba vacío durante la inicialización del SDK; lanzado sincrónicamente con SDKError.raw establecido en SDKConfig.model must not be blank; no se crea ni reemplaza ningún entorno de ejecución y no se realiza ninguna solicitud de red

Error de inicialización

Proporcione un nombre de modelo y versión completos y no vacíos, luego llame a initialize nuevamente; consulte la documentación oficial del modelo HappyOyster de Bailian para conocer los valores disponibles

101001

Clave API de la puerta de enlace de Bailian no inyectada

No

Reintentar después de updateToken

101002

La API Key Bearer de la puerta de enlace de Bailian caducó o fue rechazada. Nota: esta es la Bearer Key de la puerta de enlace, no el ticket de un solo uso; los errores de credenciales a nivel de ticket se identifican mediante códigos de servidor de seis dígitos (p. ej., 401010/401011)

No

Obtenga nuevamente la clave Bearer y updateToken; no es necesario canjear por un nuevo ticket

103001

No hay experiencia activa actualmente

Llamada rechazada

startTravel primero

103002

El estado actual no permite esta operación (incluye: discrepancia de estado; la experiencia no admite pausa / reanudación / rebobinado para pauseTravel/resumeTravel/rewindTravel (su version no es el identificador v2 que requiere su modo); el estado no es paused para rewindTravel; creationModel == ScriptList para sendInstruct)

Llamada rechazada

Verifique el estado y el modo de la experiencia; sendInstruct no está disponible en el modo ScriptList (creationModel == CreationModelValue.ScriptList), donde el contenido del guion no se gestiona a través de este SDK

103003

Desajuste de modo: sendCommand llamado en dirección o Actuación, o pauseTravel / resumeTravel / rewindTravel / sendInstruct llamados en aventura, o rewindTravel llamado en Actuación

Llamada rechazada

Compruebe si el modo actual coincide con el requisito de la interfaz

103004

Una llamada concurrente a startTravel, o reinicialización mientras un Travel se está iniciando, está activo o finalizando (solo se permite un Travel a la vez; SDKError.raw para inicio concurrente contiene solo un resumen redactado del ticket)

Llamada rechazada

Espere a endTravel() antes de reintentar

105001

Error de conexión en tiempo real

Sí

Finalizar y reiniciar

105002

Tiempo de espera agotado para la unión en tiempo real

Sí

Finalizar y reiniciar

105003

Tiempo de espera agotado esperando el primer cuadro de video

Sí

Finalizar y reiniciar

105004

Canal en tiempo real no listo / error de envío

No

Confirme que el canal en tiempo real esté listo y reintente sendCommand después de running; si un dispositivo específico presenta un comportamiento incorrecto, intente declarar y otorgar RECORD_AUDIO (consulte Guía de integración del SDK de Android de Happy Oyster § Instalación · Permisos)

105005

Una llamada de señalización HTTP de la puerta de enlace del SDK no respondió dentro de callbackTimeoutMs (valor predeterminado 30s); los tiempos de espera de unión RTC y de primer fotograma utilizan 105002 y 105003, respectivamente

No

Se recomienda reintentar o aumentar callbackTimeoutMs

105006

Finalización automática por ausencia de flujo: el primer fotograma nunca llegó, o el flujo se interrumpió durante la ejecución y no se recuperó antes del tiempo de espera. El SDK finaliza proactivamente la experiencia actual

Sí

Finalizar y reiniciar

106001

Error de red local

No

Reintentable

106002

Error al analizar la respuesta

No

Lanzado al llamador cuando se activa mediante una llamada explícita; en el sondeo de estado interno, solo se emite onError y la experiencia no se termina

106003

El servicio ascendente devolvió una respuesta de error irreconocible; el SDK ha conservado la información original en SDKError.raw (incluidos rechazos de borde de puerta de enlace como AccessDenied)

No

Reintentable; si persiste, investigue la disponibilidad del servicio junto con raw. Si raw contiene AccessDenied (más frecuentemente en la primera solicitud después de la inicialización), se trata de un problema de configuración de la puerta de enlace; verifique en orden: ① si el nombre y la versión de model son correctos, siguen disponibles, están publicados y autorizados; ② la ortografía de apiHost; ③ si apiHost, model y la Key inyectada mediante updateToken coinciden con la cuenta y la región requeridas; ④ si su cuenta ha sido añadida a la lista de permitidos de la aplicación

108001

SDK deshabilitado remotamente (apagado total o versión demasiado baja); el motivo está en SDKError.raw (String)

Sí (cuando el SDK detecta un resultado de deshabilitación, finaliza automáticamente la experiencia en curso; puede reiniciar después de la recuperación)

Informe al usuario basándose en el motivo en raw; guíe una actualización cuando la versión sea demasiado baja

Nota"¿Fatal?" se refiere específicamente a si desencadena que el SDK finalice automáticamente la experiencia actual. Los errores de validación síncrona como 100001/100002/103xxx solo rechazan/lanzan para esa llamada (100002 falla directamente la inicialización) y no finalizan una experiencia.

Errores fatales vs no fatales:

  • Errores fatales: el SDK termina automáticamente la experiencia actual (desconecta la conexión en tiempo real, libera recursos, llama a endTravel) y lo notifica mediante onError; el anfitrión debe limpiar el estado de la experiencia actual y permitir un reinicio. Existen cuatro categorías: ① el sondeo interno de estado lee un estado de experiencia de failed (por ejemplo, fallo de inferencia 500001); ② error fatal de conexión en tiempo real (105001/105002/105003); ③ finalización automática por ausencia de flujo (105006: el primer fotograma nunca llegó, o el flujo se interrumpió durante la ejecución y no se recuperó antes del tiempo de espera, y el SDK lo finaliza proactivamente); ④ SDK deshabilitado remotamente (108001).
  • Errores no fatales: no terminan la experiencia y solo se notifican mediante onError; puede llamar a updateToken nuevamente o esperar a que el servicio / canal en tiempo real se recupere antes de continuar. Un único fallo en una solicitud del propio sondeo interno de estado (error de red 106001, error de análisis 106002, anomalía ascendente 106003, error de negocio 5xxxxx) entra en esta categoría — el sondeo continúa en el siguiente ciclo, y solo cuando lee un estado de failed finaliza la experiencia; el fallo de autenticación 101001 y un fallo de envío por descarga limitada asíncrona de sendCommand 105004 tampoco son fatales. Nota: el SDK nunca envía ninguna carga útil de keepalive propia a través del canal en tiempo real, por lo que 105004 no puede aparecer mientras la sesión está inactiva — solo puede ser activado por su propia llamada a sendCommand.