834 lines
26 KiB
Markdown
834 lines
26 KiB
Markdown
# TSGame JSBridge 接口文档 - 位置与媒体服务
|
||
|
||
**版本**: v1.9
|
||
**更新时间**: 2025年7月13日
|
||
**文档类型**: 接口规范文档 - 第三部分
|
||
|
||
## 文档说明
|
||
|
||
本文档是TSGame JSBridge接口规范文档的第三部分,专注于位置服务与媒体播放相关接口,包括GPS定位、音频播放控制等功能。
|
||
|
||
**适用平台**: Android / HarmonyOS
|
||
**接口范围**: GPS定位服务、音频媒体播放
|
||
|
||
### 本文档包含的接口
|
||
|
||
| 接口名称 | 调用方向 | 主要功能 | 使用场景 |
|
||
|----------|----------|----------|----------|
|
||
| `startlocation` | WebView→App | 开始位置定位 | GPS定位服务 |
|
||
| `getlocationinfo` | WebView→App | 获取当前位置信息 | 位置信息查询 |
|
||
| `mediaTypeAudio` | WebView→App | 音频播放控制 | 音频媒体播放 |
|
||
|
||
---
|
||
|
||
## 1. 开始定位接口 (startlocation)
|
||
|
||
### 接口概述
|
||
当WebView需要开始GPS定位服务时,通过此接口启动位置定位功能。支持连续定位和单次定位两种模式,定位结果会通过`getlocationinfo`接口异步返回或自动回调到WebView。
|
||
|
||
### 工作流程
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant User as 用户
|
||
participant WebView as WebView
|
||
participant App as App
|
||
participant LocationClient as 位置服务
|
||
participant AMapSDK as 高德地图SDK
|
||
participant GPS as GPS/网络定位
|
||
|
||
User->>WebView: 触发定位需求
|
||
WebView->>App: 调用startlocation接口
|
||
App->>App: 解析定位类型参数
|
||
App->>LocationClient: 初始化AMapLocationClient
|
||
LocationClient->>AMapSDK: 配置定位选项
|
||
AMapSDK->>GPS: 启动定位服务
|
||
|
||
alt 连续定位模式
|
||
GPS->>AMapSDK: 持续返回位置信息
|
||
AMapSDK->>LocationClient: 定位回调(每3秒)
|
||
LocationClient->>App: onLocationChanged事件
|
||
App->>WebView: 调用getlocationinfo传递位置
|
||
else 单次定位模式
|
||
GPS->>AMapSDK: 返回最高精度位置
|
||
AMapSDK->>LocationClient: 单次定位回调
|
||
LocationClient->>App: onLocationChanged事件
|
||
App->>App: 保存位置信息供后续查询
|
||
end
|
||
```
|
||
|
||
### 参数规范
|
||
|
||
#### 调用格式
|
||
```javascript
|
||
WebViewJavascriptBridge.callHandler('startlocation', locationType, function(response) {
|
||
console.log('定位服务已启动');
|
||
});
|
||
```
|
||
|
||
#### 参数说明
|
||
| 参数 | 类型 | 必需 | 说明 | 取值 |
|
||
|------|------|------|------|------|
|
||
| locationType | string | ✓ | 定位类型 | "1"=连续定位, "2"或其他=单次定位 |
|
||
|
||
#### 定位类型说明
|
||
| 类型值 | 定位模式 | 说明 | 适用场景 |
|
||
|--------|----------|------|----------|
|
||
| "1" | 连续定位 | 每3秒更新一次位置信息 | 导航、实时跟踪 |
|
||
| "2" | 单次定位 | 获取一次最高精度位置 | 位置标记、签到 |
|
||
| 其他值 | 单次定位 | 默认为单次定位模式 | 通用位置获取 |
|
||
|
||
### 接口调用示例
|
||
|
||
#### 启动连续定位
|
||
```javascript
|
||
// 启动连续定位,适用于导航场景
|
||
function startContinuousLocation() {
|
||
WebViewJavascriptBridge.callHandler('startlocation', '1', function(response) {
|
||
console.log('连续定位已启动,每3秒更新一次位置');
|
||
|
||
// 连续定位会自动调用getlocationinfo回调
|
||
// 需要注册位置更新处理函数
|
||
window.onLocationUpdate = function(locationData) {
|
||
updateUserPosition(locationData);
|
||
};
|
||
});
|
||
}
|
||
```
|
||
|
||
#### 启动单次定位
|
||
```javascript
|
||
// 启动单次定位,适用于签到场景
|
||
function startOnceLocation() {
|
||
WebViewJavascriptBridge.callHandler('startlocation', '2', function(response) {
|
||
console.log('单次定位已启动,正在获取最精确位置');
|
||
|
||
// 等待3秒后查询位置信息
|
||
setTimeout(() => {
|
||
getLocationInfo();
|
||
}, 3000);
|
||
});
|
||
}
|
||
|
||
// 获取定位结果
|
||
function getLocationInfo() {
|
||
WebViewJavascriptBridge.callHandler('getlocationinfo', '', function(locationJson) {
|
||
if (locationJson) {
|
||
const location = JSON.parse(locationJson);
|
||
console.log('定位成功:', location);
|
||
showLocationOnMap(location);
|
||
} else {
|
||
console.log('定位失败或未完成');
|
||
}
|
||
});
|
||
}
|
||
```
|
||
|
||
#### 智能定位管理器
|
||
```javascript
|
||
// 定位管理器
|
||
const LocationManager = {
|
||
isLocating: false,
|
||
locationType: 'once', // 'once' 或 'continuous'
|
||
locationTimer: null,
|
||
|
||
// 开始定位
|
||
startLocation(type = 'once') {
|
||
if (this.isLocating) {
|
||
console.log('定位服务已在运行中');
|
||
return;
|
||
}
|
||
|
||
this.locationType = type;
|
||
this.isLocating = true;
|
||
|
||
const typeParam = type === 'continuous' ? '1' : '2';
|
||
|
||
WebViewJavascriptBridge.callHandler('startlocation', typeParam, (response) => {
|
||
console.log(`${type}定位已启动`);
|
||
|
||
if (type === 'continuous') {
|
||
this.startLocationMonitoring();
|
||
} else {
|
||
this.checkLocationResult();
|
||
}
|
||
});
|
||
},
|
||
|
||
// 停止定位
|
||
stopLocation() {
|
||
this.isLocating = false;
|
||
|
||
if (this.locationTimer) {
|
||
clearInterval(this.locationTimer);
|
||
this.locationTimer = null;
|
||
}
|
||
|
||
console.log('定位服务已停止');
|
||
},
|
||
|
||
// 监控连续定位
|
||
startLocationMonitoring() {
|
||
// 每4秒检查一次位置更新(略大于定位间隔3秒)
|
||
this.locationTimer = setInterval(() => {
|
||
this.getCurrentLocation((location) => {
|
||
if (location) {
|
||
this.onLocationUpdate(location);
|
||
}
|
||
});
|
||
}, 4000);
|
||
},
|
||
|
||
// 检查单次定位结果
|
||
checkLocationResult() {
|
||
// 等待3秒让定位完成
|
||
setTimeout(() => {
|
||
this.getCurrentLocation((location) => {
|
||
this.isLocating = false;
|
||
|
||
if (location) {
|
||
this.onLocationReceived(location);
|
||
} else {
|
||
this.onLocationError('定位失败');
|
||
}
|
||
});
|
||
}, 3000);
|
||
},
|
||
|
||
// 获取当前位置
|
||
getCurrentLocation(callback) {
|
||
WebViewJavascriptBridge.callHandler('getlocationinfo', '', (locationJson) => {
|
||
if (locationJson) {
|
||
try {
|
||
const location = JSON.parse(locationJson);
|
||
callback(location);
|
||
} catch (e) {
|
||
console.error('位置数据解析失败:', e);
|
||
callback(null);
|
||
}
|
||
} else {
|
||
callback(null);
|
||
}
|
||
});
|
||
},
|
||
|
||
// 位置更新回调(连续定位)
|
||
onLocationUpdate(location) {
|
||
console.log('位置更新:', location);
|
||
// 更新地图显示
|
||
updateMapPosition(location);
|
||
// 触发位置变化事件
|
||
this.triggerLocationChange(location);
|
||
},
|
||
|
||
// 位置获取回调(单次定位)
|
||
onLocationReceived(location) {
|
||
console.log('定位完成:', location);
|
||
// 显示位置信息
|
||
showLocationDetails(location);
|
||
// 执行签到等操作
|
||
performLocationBasedAction(location);
|
||
},
|
||
|
||
// 定位错误处理
|
||
onLocationError(error) {
|
||
console.error('定位错误:', error);
|
||
showMessage('定位失败,请检查位置权限和网络连接');
|
||
},
|
||
|
||
// 触发位置变化事件
|
||
triggerLocationChange(location) {
|
||
const event = new CustomEvent('locationChanged', {
|
||
detail: location
|
||
});
|
||
window.dispatchEvent(event);
|
||
}
|
||
};
|
||
|
||
// 使用示例
|
||
LocationManager.startLocation('continuous'); // 启动连续定位
|
||
// LocationManager.startLocation('once'); // 启动单次定位
|
||
|
||
// 监听位置变化
|
||
window.addEventListener('locationChanged', function(event) {
|
||
const location = event.detail;
|
||
console.log('位置已更新:', location.address);
|
||
});
|
||
```
|
||
|
||
### HarmonyOS实现要点
|
||
- **接口名称**: `startlocation`
|
||
- **调用方向**: WebView → App
|
||
- **参数格式**: 字符串类型的定位类型("1"=连续定位, "2"=单次定位)
|
||
- **权限要求**: 需要精确位置权限和网络权限
|
||
- **SDK依赖**: 使用高德地图SDK (AMapLocationClient)
|
||
- **定位模式**: 高精度定位模式 (Hight_Accuracy)
|
||
- **连续定位**: 3秒间隔的连续位置更新
|
||
- **单次定位**: 3秒内最高精度的一次定位
|
||
- **回调机制**: 通过AMapLocationListener.onLocationChanged接收位置更新
|
||
|
||
---
|
||
|
||
## 2. 获取位置信息接口 (getlocationinfo)
|
||
|
||
### 接口概述
|
||
当WebView需要获取当前设备的位置信息时,通过此接口查询最近一次定位的结果。必须先调用`startlocation`接口启动定位服务,才能获取到有效的位置数据。
|
||
|
||
### 工作流程
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant WebView as WebView
|
||
participant App as App
|
||
participant LocationCache as 位置缓存
|
||
participant LocationData as 位置数据对象
|
||
|
||
WebView->>App: 调用getlocationinfo接口
|
||
App->>LocationCache: 检查缓存的位置信息
|
||
|
||
alt 有位置数据
|
||
LocationCache->>LocationData: 获取MaplocationInfo对象
|
||
LocationData->>LocationData: 序列化为JSON
|
||
LocationData->>App: 返回JSON字符串
|
||
App->>WebView: 回调返回位置JSON
|
||
else 无位置数据
|
||
LocationCache->>App: 返回null
|
||
App->>WebView: 回调返回null
|
||
end
|
||
|
||
WebView->>WebView: 解析位置JSON数据
|
||
WebView->>WebView: 处理位置信息
|
||
```
|
||
|
||
### 参数规范
|
||
|
||
#### 调用格式
|
||
```javascript
|
||
WebViewJavascriptBridge.callHandler('getlocationinfo', '', function(locationJson) {
|
||
if (locationJson) {
|
||
const location = JSON.parse(locationJson);
|
||
console.log('当前位置:', location);
|
||
} else {
|
||
console.log('暂无位置信息');
|
||
}
|
||
});
|
||
```
|
||
|
||
#### 参数说明
|
||
| 参数 | 类型 | 必需 | 说明 | 取值 |
|
||
|------|------|------|------|------|
|
||
| data | string | ✓ | 固定传入空字符串 | "" |
|
||
|
||
#### 返回数据结构
|
||
| 字段 | 类型 | 说明 | 示例值 |
|
||
|------|------|------|-------|
|
||
| latitude | number | 纬度 | 39.908692 |
|
||
| longitude | number | 经度 | 116.397477 |
|
||
| address | string | 详细地址 | "北京市东城区正义路2号" |
|
||
| country | string | 国家 | "中国" |
|
||
| province | string | 省份 | "北京市" |
|
||
| city | string | 城市 | "北京市" |
|
||
| district | string | 城区 | "东城区" |
|
||
| street | string | 街道 | "正义路" |
|
||
| cityCode | string | 城市编码 | "010" |
|
||
|
||
### 接口调用示例
|
||
|
||
#### 基础位置获取
|
||
```javascript
|
||
// 获取当前位置信息
|
||
function getCurrentLocation() {
|
||
WebViewJavascriptBridge.callHandler('getlocationinfo', '', function(locationJson) {
|
||
if (locationJson) {
|
||
try {
|
||
const location = JSON.parse(locationJson);
|
||
displayLocationInfo(location);
|
||
} catch (e) {
|
||
console.error('位置数据解析失败:', e);
|
||
}
|
||
} else {
|
||
console.log('位置信息不可用,请先启动定位服务');
|
||
startLocationFirst();
|
||
}
|
||
});
|
||
}
|
||
|
||
// 显示位置信息
|
||
function displayLocationInfo(location) {
|
||
console.log('=== 当前位置信息 ===');
|
||
console.log('纬度:', location.latitude);
|
||
console.log('经度:', location.longitude);
|
||
console.log('地址:', location.address);
|
||
console.log('城市:', location.city);
|
||
console.log('区县:', location.district);
|
||
|
||
// 更新页面显示
|
||
updateLocationDisplay(location);
|
||
}
|
||
|
||
// 提示启动定位
|
||
function startLocationFirst() {
|
||
if (confirm('需要先启动定位服务,是否立即启动?')) {
|
||
WebViewJavascriptBridge.callHandler('startlocation', '2', function(response) {
|
||
console.log('定位服务已启动,3秒后重新获取位置');
|
||
setTimeout(getCurrentLocation, 3000);
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 完整的位置服务流程
|
||
```javascript
|
||
// 位置服务管理器
|
||
const LocationService = {
|
||
// 获取位置信息(带自动定位)
|
||
getLocationWithAutoStart(callback) {
|
||
// 先尝试获取已有的位置信息
|
||
WebViewJavascriptBridge.callHandler('getlocationinfo', '', (locationJson) => {
|
||
if (locationJson) {
|
||
// 已有位置信息,直接返回
|
||
try {
|
||
const location = JSON.parse(locationJson);
|
||
callback(null, location);
|
||
} catch (e) {
|
||
callback('位置数据解析失败', null);
|
||
}
|
||
} else {
|
||
// 没有位置信息,启动定位
|
||
this.startLocationAndGet(callback);
|
||
}
|
||
});
|
||
},
|
||
|
||
// 启动定位并获取结果
|
||
startLocationAndGet(callback) {
|
||
console.log('正在启动定位服务...');
|
||
|
||
WebViewJavascriptBridge.callHandler('startlocation', '2', (response) => {
|
||
console.log('定位服务已启动,等待定位完成...');
|
||
|
||
// 等待定位完成
|
||
this.waitForLocation(callback, 0);
|
||
});
|
||
},
|
||
|
||
// 等待定位完成
|
||
waitForLocation(callback, attempts) {
|
||
if (attempts >= 10) {
|
||
callback('定位超时', null);
|
||
return;
|
||
}
|
||
|
||
setTimeout(() => {
|
||
WebViewJavascriptBridge.callHandler('getlocationinfo', '', (locationJson) => {
|
||
if (locationJson) {
|
||
try {
|
||
const location = JSON.parse(locationJson);
|
||
callback(null, location);
|
||
} catch (e) {
|
||
callback('位置数据解析失败', null);
|
||
}
|
||
} else {
|
||
// 继续等待
|
||
this.waitForLocation(callback, attempts + 1);
|
||
}
|
||
});
|
||
}, 1000); // 每秒检查一次
|
||
},
|
||
|
||
// 验证位置数据完整性
|
||
validateLocation(location) {
|
||
const required = ['latitude', 'longitude'];
|
||
const missing = required.filter(field => !location[field]);
|
||
|
||
return {
|
||
isValid: missing.length === 0,
|
||
missing: missing,
|
||
hasAddress: !!location.address
|
||
};
|
||
},
|
||
|
||
// 格式化位置显示
|
||
formatLocationDisplay(location) {
|
||
const validation = this.validateLocation(location);
|
||
|
||
if (!validation.isValid) {
|
||
return '位置信息不完整';
|
||
}
|
||
|
||
// 构建显示文本
|
||
let display = `${location.latitude.toFixed(6)}, ${location.longitude.toFixed(6)}`;
|
||
|
||
if (validation.hasAddress) {
|
||
display += `\n${location.address}`;
|
||
}
|
||
|
||
return display;
|
||
}
|
||
};
|
||
|
||
// 使用示例
|
||
LocationService.getLocationWithAutoStart((error, location) => {
|
||
if (error) {
|
||
console.error('获取位置失败:', error);
|
||
showMessage('定位失败: ' + error);
|
||
} else {
|
||
console.log('位置获取成功:', location);
|
||
|
||
const validation = LocationService.validateLocation(location);
|
||
if (validation.isValid) {
|
||
const displayText = LocationService.formatLocationDisplay(location);
|
||
showLocationInfo(displayText);
|
||
} else {
|
||
console.warn('位置数据不完整:', validation.missing);
|
||
}
|
||
}
|
||
});
|
||
```
|
||
|
||
### HarmonyOS实现要点
|
||
- **接口名称**: `getlocationinfo`
|
||
- **调用方向**: WebView → App
|
||
- **参数格式**: 空字符串
|
||
- **返回数据**: JSON字符串格式的位置信息对象
|
||
- **数据来源**: 由startlocation接口触发的定位结果
|
||
- **数据结构**: MaplocationInfo对象的JSON序列化
|
||
- **前置条件**: 需要先调用startlocation启动定位服务
|
||
- **数据字段**: 包含经纬度、地址、行政区划等完整位置信息
|
||
- **缓存机制**: App内存中保存最新一次定位结果
|
||
|
||
---
|
||
|
||
## 3. 音频播放接口 (mediaTypeAudio)
|
||
|
||
### 接口概述
|
||
当WebView需要播放音频文件时,通过此接口实现音频的播放控制。支持普通音频和历史音频两种类型,具有防重复播放机制,播放开始时会调用gameui_play_voice通知,播放完成后会自动通知WebView。
|
||
|
||
### 工作流程
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant User as 用户
|
||
participant WebView as WebView
|
||
participant App as App
|
||
participant MediaPlayer as 媒体播放器
|
||
participant AudioSystem as 音频系统
|
||
participant Server as 音频服务器
|
||
|
||
User->>WebView: 触发音频播放
|
||
WebView->>App: 调用mediaTypeAudio接口
|
||
App->>App: 解析音频参数(audiourl/type/user)
|
||
App->>App: 检查防重复播放标志
|
||
|
||
alt 可以播放音频
|
||
App->>App: 验证音频URL格式
|
||
App->>Server: 下载音频文件(如果是网络URL)
|
||
Server->>App: 返回音频数据
|
||
App->>MediaPlayer: 初始化播放器
|
||
App->>WebView: 调用gameui_play_voice通知播放开始
|
||
MediaPlayer->>AudioSystem: 开始播放音频
|
||
|
||
Note over MediaPlayer,AudioSystem: 播放过程中
|
||
MediaPlayer->>App: 播放状态回调
|
||
App->>App: 更新播放标志状态
|
||
|
||
alt 播放完成
|
||
MediaPlayer->>App: 播放完成回调
|
||
App->>WebView: 调用gameui_stop_voice通知
|
||
App->>App: 重置播放状态
|
||
else 播放错误
|
||
MediaPlayer->>App: 播放错误回调
|
||
App->>App: 重置播放状态
|
||
App->>WebView: 错误通知
|
||
end
|
||
|
||
else 正在播放中
|
||
App->>App: 添加到延时播放队列
|
||
Note over App: 200ms后重试播放
|
||
end
|
||
```
|
||
|
||
### 参数规范
|
||
|
||
#### 调用格式
|
||
```javascript
|
||
const audioData = {
|
||
audiourl: "https://example.com/audio.mp3",
|
||
type: "1",
|
||
user: "user_123"
|
||
};
|
||
|
||
WebViewJavascriptBridge.callHandler('mediaTypeAudio', JSON.stringify(audioData), function(response) {
|
||
console.log('音频播放请求已发送');
|
||
});
|
||
```
|
||
|
||
#### 参数说明
|
||
| 参数 | 类型 | 必需 | 说明 | 示例值 |
|
||
|------|------|------|------|--------|
|
||
| audiourl | string | ✓ | 音频文件URL地址 | "https://cdn.example.com/sound.mp3" |
|
||
| type | string | ✓ | 音频类型 | "1"=普通音频, "2"=历史音频 |
|
||
| user | string | ✓ | 用户标识或动画位置 | "user_123", "avatar_position_1" |
|
||
|
||
#### 音频类型说明
|
||
| 类型值 | 播放模式 | 说明 | 适用场景 |
|
||
|--------|----------|------|----------|
|
||
| "1" | 普通音频 | 新音频播放,支持防重复机制 | 音效、提示音 |
|
||
| "2" | 历史音频 | 历史音频重播,无防重复限制 | 聊天记录回放 |
|
||
|
||
### 接口调用示例
|
||
|
||
#### 播放游戏音效
|
||
```javascript
|
||
// 播放游戏胜利音效
|
||
function playVictorySound() {
|
||
const audioData = {
|
||
audiourl: "https://game.example.com/sounds/victory.mp3",
|
||
type: "1",
|
||
user: "game_effect_victory"
|
||
};
|
||
|
||
WebViewJavascriptBridge.callHandler('mediaTypeAudio', JSON.stringify(audioData), function(response) {
|
||
console.log('胜利音效播放中...');
|
||
});
|
||
}
|
||
|
||
// 播放按钮点击音效
|
||
function playButtonClickSound() {
|
||
const audioData = {
|
||
audiourl: "https://game.example.com/sounds/button_click.mp3",
|
||
type: "1",
|
||
user: "ui_button_click"
|
||
};
|
||
|
||
WebViewJavascriptBridge.callHandler('mediaTypeAudio', JSON.stringify(audioData), function(response) {
|
||
console.log('按钮音效播放中...');
|
||
});
|
||
}
|
||
```
|
||
|
||
#### 完整的音频管理系统
|
||
```javascript
|
||
// 音频播放管理器
|
||
const AudioManager = {
|
||
playingAudios: new Map(), // 正在播放的音频
|
||
audioQueue: [], // 音频播放队列
|
||
isProcessingQueue: false,
|
||
|
||
// 播放音频
|
||
playAudio(audioUrl, options = {}) {
|
||
const audioData = {
|
||
audiourl: audioUrl,
|
||
type: options.type || "1",
|
||
user: options.userId || this.generateUserId()
|
||
};
|
||
|
||
// 添加到播放队列
|
||
this.addToQueue(audioData, options.callback);
|
||
|
||
// 处理队列
|
||
this.processQueue();
|
||
|
||
return audioData.user;
|
||
},
|
||
|
||
// 生成唯一用户ID
|
||
generateUserId() {
|
||
return "audio_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
|
||
},
|
||
|
||
// 添加到播放队列
|
||
addToQueue(audioData, callback) {
|
||
this.audioQueue.push({
|
||
audioData: audioData,
|
||
callback: callback,
|
||
timestamp: Date.now()
|
||
});
|
||
},
|
||
|
||
// 处理播放队列
|
||
processQueue() {
|
||
if (this.isProcessingQueue || this.audioQueue.length === 0) {
|
||
return;
|
||
}
|
||
|
||
this.isProcessingQueue = true;
|
||
|
||
const queueItem = this.audioQueue.shift();
|
||
this.executePlayback(queueItem);
|
||
},
|
||
|
||
// 执行播放
|
||
executePlayback(queueItem) {
|
||
const { audioData, callback } = queueItem;
|
||
|
||
// 记录播放状态
|
||
this.playingAudios.set(audioData.user, {
|
||
audioData: audioData,
|
||
startTime: Date.now(),
|
||
callback: callback
|
||
});
|
||
|
||
console.log('开始播放音频:', audioData.audiourl);
|
||
|
||
WebViewJavascriptBridge.callHandler('mediaTypeAudio', JSON.stringify(audioData), (response) => {
|
||
console.log('音频播放请求已发送:', audioData.user);
|
||
|
||
if (callback) {
|
||
callback(null, audioData.user);
|
||
}
|
||
|
||
// 继续处理下一个队列项
|
||
setTimeout(() => {
|
||
this.isProcessingQueue = false;
|
||
this.processQueue();
|
||
}, 200);
|
||
});
|
||
},
|
||
|
||
// 处理播放开始
|
||
onAudioStart(userId) {
|
||
const playingInfo = this.playingAudios.get(userId);
|
||
|
||
if (playingInfo) {
|
||
console.log(`音频播放开始: ${userId}`);
|
||
|
||
// 触发播放开始事件
|
||
this.triggerAudioStartEvent(userId);
|
||
|
||
// 调用回调
|
||
if (playingInfo.callback) {
|
||
playingInfo.callback(null, 'started');
|
||
}
|
||
} else {
|
||
console.log(`收到播放开始通知,但未找到对应的音频记录: ${userId}`);
|
||
}
|
||
},
|
||
|
||
// 处理播放完成
|
||
onAudioComplete(userId) {
|
||
const playingInfo = this.playingAudios.get(userId);
|
||
|
||
if (playingInfo) {
|
||
const duration = Date.now() - playingInfo.startTime;
|
||
console.log(`音频播放完成: ${userId}, 耗时: ${duration}ms`);
|
||
|
||
// 移除播放记录
|
||
this.playingAudios.delete(userId);
|
||
|
||
// 触发完成事件
|
||
this.triggerAudioCompleteEvent(userId, duration);
|
||
|
||
// 调用回调
|
||
if (playingInfo.callback) {
|
||
playingInfo.callback(null, 'completed');
|
||
}
|
||
}
|
||
},
|
||
|
||
// 触发音频开始事件
|
||
triggerAudioStartEvent(userId) {
|
||
const event = new CustomEvent('audioPlayStart', {
|
||
detail: {
|
||
userId: userId
|
||
}
|
||
});
|
||
window.dispatchEvent(event);
|
||
},
|
||
|
||
// 触发音频完成事件
|
||
triggerAudioCompleteEvent(userId, duration) {
|
||
const event = new CustomEvent('audioPlayComplete', {
|
||
detail: {
|
||
userId: userId,
|
||
duration: duration
|
||
}
|
||
});
|
||
window.dispatchEvent(event);
|
||
},
|
||
|
||
// 停止所有音频
|
||
stopAllAudio() {
|
||
this.playingAudios.clear();
|
||
this.audioQueue = [];
|
||
this.isProcessingQueue = false;
|
||
console.log('已停止所有音频播放');
|
||
},
|
||
|
||
// 获取播放状态
|
||
getPlayingStatus() {
|
||
return {
|
||
playingCount: this.playingAudios.size,
|
||
queueLength: this.audioQueue.length,
|
||
isProcessing: this.isProcessingQueue
|
||
};
|
||
}
|
||
};
|
||
|
||
// 注册音频播放相关监听
|
||
if (typeof WebViewJavascriptBridge !== 'undefined') {
|
||
// 注册音频播放开始通知
|
||
WebViewJavascriptBridge.registerHandler('gameui_play_voice', function(userId) {
|
||
console.log('收到音频播放开始通知:', userId);
|
||
AudioManager.onAudioStart(userId);
|
||
});
|
||
|
||
// 注册音频播放完成通知
|
||
WebViewJavascriptBridge.registerHandler('gameui_stop_voice', function(userId) {
|
||
console.log('收到音频播放完成通知:', userId);
|
||
AudioManager.onAudioComplete(userId);
|
||
});
|
||
}
|
||
|
||
// 使用示例
|
||
const userId1 = AudioManager.playAudio('https://example.com/sound1.mp3', {
|
||
type: '1',
|
||
userId: 'background_music',
|
||
callback: (error, result) => {
|
||
if (error) {
|
||
console.error('播放失败:', error);
|
||
} else {
|
||
console.log('播放结果:', result);
|
||
}
|
||
}
|
||
});
|
||
|
||
// 监听播放开始事件
|
||
window.addEventListener('audioPlayStart', function(event) {
|
||
const { userId } = event.detail;
|
||
console.log(`音频 ${userId} 开始播放`);
|
||
});
|
||
|
||
// 监听播放完成事件
|
||
window.addEventListener('audioPlayComplete', function(event) {
|
||
const { userId, duration } = event.detail;
|
||
console.log(`音频 ${userId} 播放完成,耗时 ${duration}ms`);
|
||
});
|
||
```
|
||
|
||
### HarmonyOS实现要点
|
||
- **接口名称**: `mediaTypeAudio`
|
||
- **调用方向**: WebView → App
|
||
- **参数格式**: JSON字符串,包含audiourl、type、user字段
|
||
- **音频支持**: 支持各种音频格式(MP3、WAV、AAC等)
|
||
- **防重复机制**: type为"1"时启用防重复播放检查
|
||
- **播放队列**: 正在播放时新音频延时200ms重试
|
||
- **开始通知**: 通过gameui_play_voice回调通知WebView播放开始
|
||
- **完成通知**: 通过gameui_stop_voice回调通知WebView播放完成
|
||
- **音量控制**: 受voicePlaying接口的全局音量开关影响
|
||
- **错误处理**: 包含URL验证、格式检查、播放状态管理
|
||
|
||
---
|
||
|
||
## 文档关联
|
||
|
||
本文档是TSGame JSBridge接口规范的第三部分,完整文档包括:
|
||
|
||
1. **用户认证与页面管理** - 登录、页面跳转、语音控制
|
||
2. **系统功能与交互** - 剪贴板、游戏状态、设备检测
|
||
3. **位置与媒体服务** (本文档) - GPS定位、音频播放
|
||
|
||
---
|
||
|
||
**文档维护**: TSGame开发团队
|
||
**最后更新**: 2025年7月13日
|
||
**版本**: v1.9
|
||
**联系方式**: 技术支持组
|