Files
youle_app_android/guides/JavaScript调用原生接口文档/TSGame_JSBridge_位置与媒体服务.md
2026-02-16 18:18:11 +08:00

26 KiB
Raw Permalink Blame History

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接口规范的第三部分完整文档包括

  1. 用户认证与页面管理 - 登录、页面跳转、语音控制
  2. 系统功能与交互 - 剪贴板、游戏状态、设备检测
  3. 位置与媒体服务 (本文档) - GPS定位、音频播放

文档维护: TSGame开发团队
最后更新: 2025年7月13日
版本: v1.9
联系方式: 技术支持组