26 KiB
26 KiB
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。
工作流程
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
参数规范
调用格式
WebViewJavascriptBridge.callHandler('startlocation', locationType, function(response) {
console.log('定位服务已启动');
});
参数说明
| 参数 | 类型 | 必需 | 说明 | 取值 |
|---|---|---|---|---|
| locationType | string | ✓ | 定位类型 | "1"=连续定位, "2"或其他=单次定位 |
定位类型说明
| 类型值 | 定位模式 | 说明 | 适用场景 |
|---|---|---|---|
| "1" | 连续定位 | 每3秒更新一次位置信息 | 导航、实时跟踪 |
| "2" | 单次定位 | 获取一次最高精度位置 | 位置标记、签到 |
| 其他值 | 单次定位 | 默认为单次定位模式 | 通用位置获取 |
接口调用示例
启动连续定位
// 启动连续定位,适用于导航场景
function startContinuousLocation() {
WebViewJavascriptBridge.callHandler('startlocation', '1', function(response) {
console.log('连续定位已启动,每3秒更新一次位置');
// 连续定位会自动调用getlocationinfo回调
// 需要注册位置更新处理函数
window.onLocationUpdate = function(locationData) {
updateUserPosition(locationData);
};
});
}
启动单次定位
// 启动单次定位,适用于签到场景
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('定位失败或未完成');
}
});
}
智能定位管理器
// 定位管理器
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接口启动定位服务,才能获取到有效的位置数据。
工作流程
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: 处理位置信息
参数规范
调用格式
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" |
接口调用示例
基础位置获取
// 获取当前位置信息
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);
});
}
}
完整的位置服务流程
// 位置服务管理器
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。
工作流程
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
参数规范
调用格式
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" | 历史音频 | 历史音频重播,无防重复限制 | 聊天记录回放 |
接口调用示例
播放游戏音效
// 播放游戏胜利音效
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('按钮音效播放中...');
});
}
完整的音频管理系统
// 音频播放管理器
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接口规范的第三部分,完整文档包括:
- 用户认证与页面管理 - 登录、页面跳转、语音控制
- 系统功能与交互 - 剪贴板、游戏状态、设备检测
- 位置与媒体服务 (本文档) - GPS定位、音频播放
文档维护: TSGame开发团队
最后更新: 2025年7月13日
版本: v1.9
联系方式: 技术支持组