first commit
This commit is contained in:
833
guides/JavaScript调用原生接口文档/TSGame_JSBridge_位置与媒体服务.md
Normal file
833
guides/JavaScript调用原生接口文档/TSGame_JSBridge_位置与媒体服务.md
Normal file
@@ -0,0 +1,833 @@
|
||||
# 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
|
||||
**联系方式**: 技术支持组
|
||||
Reference in New Issue
Block a user