# 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 **联系方式**: 技术支持组