2376 lines
71 KiB
Markdown
2376 lines
71 KiB
Markdown
# TSGame 跨平台项目说明文档
|
||
|
||
## 项目概述
|
||
|
||
TSGame 是一个跨平台的棋牌游戏应用,支持Android、iOS、HarmonyOS等主流移动平台。主要实现了聚友棋牌、进贤聚友棋牌等多个地方特色棋牌游戏。项目采用WebView混合开发模式,通过统一的JS Bridge接口实现跨平台功能。
|
||
|
||
### 支持平台
|
||
- **Android**: API 21+ (Android 5.0+)
|
||
- **iOS**: iOS 11.0+
|
||
- **HarmonyOS**: API 8+ (HarmonyOS 3.0+)
|
||
|
||
### 基本信息
|
||
- **应用标识**: `com.jx.jyhd` (可根据平台调整)
|
||
- **应用名称**: 进贤聚友棋牌(支持多种地方棋牌名称配置)
|
||
- **当前版本**: 3.6.3
|
||
- **架构模式**: WebView + JS Bridge 混合开发
|
||
|
||
## 跨平台技术架构
|
||
|
||
### 整体架构设计
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ H5游戏前端 │
|
||
│ (HTML5 + CSS3 + JavaScript) │
|
||
└─────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ JS Bridge 通信层 │
|
||
│ (统一的跨平台API接口定义) │
|
||
└─────────────────────────────────────────────────────┘
|
||
│
|
||
┌─────────────┬─────────────┬─────────────┬─────────────┐
|
||
│ Android │ iOS │ HarmonyOS │ 其他平台 │
|
||
│ Native │ Native │ Native │ Native │
|
||
└─────────────┴─────────────┴─────────────┴─────────────┘
|
||
```
|
||
|
||
### 核心设计原则
|
||
1. **统一接口**: 所有平台实现相同的JS Bridge API接口
|
||
2. **功能对等**: 确保各平台功能基本一致
|
||
3. **性能优化**: 针对不同平台特性进行优化
|
||
4. **用户体验**: 遵循各平台的设计规范
|
||
|
||
## 项目功能特性
|
||
|
||
### 核心功能模块
|
||
1. **多地方棋牌游戏支持**
|
||
- 崇仁聚友棋牌
|
||
- 进贤聚友棋牌
|
||
- 广州聚友棋牌
|
||
- 慈溪聚友棋牌
|
||
- 东乡棋牌等多个地方特色游戏
|
||
|
||
2. **WebView游戏引擎**
|
||
- Android: 腾讯X5 WebView内核
|
||
- iOS: WKWebView
|
||
- HarmonyOS: Web组件
|
||
- 支持HTML5游戏运行
|
||
- 自定义JS桥接功能
|
||
|
||
3. **社交分享功能**
|
||
- 微信好友/朋友圈分享
|
||
- QQ分享(Android)
|
||
- 系统分享(iOS/HarmonyOS)
|
||
- 抖音分享(Intent/URL Scheme方式)
|
||
|
||
4. **位置服务**
|
||
- Android: 高德地图SDK
|
||
- iOS: 高德地图SDK + CoreLocation
|
||
- HarmonyOS: 华为地图服务
|
||
- GPS/网络定位支持
|
||
|
||
5. **语音功能**
|
||
- 声网Agora音频SDK(全平台支持)
|
||
- 实时语音通话
|
||
- 录音功能
|
||
|
||
6. **版本管理**
|
||
- 应用自动更新
|
||
- 游戏资源热更新
|
||
- 配置文件动态加载
|
||
|
||
## 跨平台项目目录结构
|
||
|
||
### 通用项目结构
|
||
```
|
||
tsgame_multiplatform/
|
||
├── shared/ # 共享资源
|
||
│ ├── webview/ # H5游戏资源
|
||
│ │ ├── assets/ # 静态资源
|
||
│ │ ├── js/ # JavaScript文件
|
||
│ │ ├── css/ # 样式文件
|
||
│ │ └── index.html # 游戏入口页面
|
||
│ ├── bridge/ # JS Bridge接口定义
|
||
│ │ ├── bridge-api.js # 统一API接口
|
||
│ │ └── bridge-types.ts # TypeScript类型定义
|
||
│ └── config/ # 配置文件
|
||
│ ├── game-config.json # 游戏配置
|
||
│ └── platform-config.json # 平台配置
|
||
├── android/ # Android平台实现
|
||
│ ├── app/
|
||
│ │ ├── src/main/java/ # Java/Kotlin源码
|
||
│ │ ├── src/main/assets/ # 资源文件
|
||
│ │ └── build.gradle # 构建配置
|
||
│ └── libs/ # 第三方库
|
||
├── ios/ # iOS平台实现
|
||
│ ├── TSGame/
|
||
│ │ ├── Classes/ # OC/Swift源码
|
||
│ │ ├── Resources/ # 资源文件
|
||
│ │ └── Info.plist # 应用配置
|
||
│ └── Podfile # 依赖管理
|
||
├── harmonyos/ # HarmonyOS平台实现
|
||
│ ├── entry/
|
||
│ │ ├── src/main/ets/ # ArkTS源码
|
||
│ │ ├── src/main/resources/ # 资源文件
|
||
│ │ └── build-profile.json5 # 构建配置
|
||
│ └── oh-package.json5 # 依赖管理
|
||
├── docs/ # 项目文档
|
||
│ ├── platform-guides/ # 平台开发指南
|
||
│ ├── api-reference/ # API参考文档
|
||
│ └── deployment/ # 部署指南
|
||
└── tools/ # 开发工具
|
||
├── build-scripts/ # 构建脚本
|
||
└── test-tools/ # 测试工具
|
||
```
|
||
|
||
### 平台特定代码包结构
|
||
|
||
#### Android (Java/Kotlin)
|
||
```
|
||
com.jx.jyhd/
|
||
├── activity/ # 活动页面
|
||
│ ├── MainActivity # 主入口
|
||
│ ├── WebViewActivity # WebView容器
|
||
│ └── SplashActivity # 启动页
|
||
├── bridge/ # JS Bridge实现
|
||
│ ├── BridgeWebView # WebView扩展
|
||
│ ├── BridgeHandler # 方法处理器
|
||
│ └── BridgeManager # 桥接管理器
|
||
├── service/ # 业务服务
|
||
│ ├── AuthService # 认证服务
|
||
│ ├── ShareService # 分享服务
|
||
│ └── LocationService # 定位服务
|
||
└── utils/ # 工具类
|
||
├── NetworkUtils # 网络工具
|
||
└── StorageUtils # 存储工具
|
||
```
|
||
|
||
#### iOS (Objective-C/Swift)
|
||
```
|
||
TSGame/
|
||
├── Controllers/ # 控制器
|
||
│ ├── TSMainViewController # 主控制器
|
||
│ ├── TSWebViewController # WebView控制器
|
||
│ └── TSSplashViewController # 启动页控制器
|
||
├── Bridge/ # JS Bridge实现
|
||
│ ├── TSBridgeWebView # WebView扩展
|
||
│ ├── TSBridgeHandler # 方法处理器
|
||
│ └── TSBridgeManager # 桥接管理器
|
||
├── Services/ # 业务服务
|
||
│ ├── TSAuthService # 认证服务
|
||
│ ├── TSShareService # 分享服务
|
||
│ └── TSLocationService # 定位服务
|
||
└── Utils/ # 工具类
|
||
├── TSNetworkUtils # 网络工具
|
||
└── TSStorageUtils # 存储工具
|
||
```
|
||
|
||
#### HarmonyOS (ArkTS)
|
||
```
|
||
src/main/ets/
|
||
├── entryability/ # 应用入口
|
||
│ └── EntryAbility.ts # 主Ability
|
||
├── pages/ # 页面
|
||
│ ├── Index.ets # 主页面
|
||
│ ├── WebView.ets # WebView页面
|
||
│ └── Splash.ets # 启动页
|
||
├── bridge/ # JS Bridge实现
|
||
│ ├── BridgeWebView.ets # WebView扩展
|
||
│ ├── BridgeHandler.ets # 方法处理器
|
||
│ └── BridgeManager.ets # 桥接管理器
|
||
├── services/ # 业务服务
|
||
│ ├── AuthService.ets # 认证服务
|
||
│ ├── ShareService.ets # 分享服务
|
||
│ └── LocationService.ets # 定位服务
|
||
└── utils/ # 工具类
|
||
├── NetworkUtils.ets # 网络工具
|
||
└── StorageUtils.ets # 存储工具
|
||
```
|
||
|
||
## 跨平台应用启动流程
|
||
|
||
### 统一启动流程设计
|
||
```
|
||
应用启动
|
||
↓
|
||
平台初始化 (原生代码)
|
||
↓
|
||
权限检查与申请
|
||
↓
|
||
配置文件加载
|
||
↓
|
||
JS Bridge初始化
|
||
↓
|
||
WebView组件加载
|
||
↓
|
||
H5游戏资源加载
|
||
↓
|
||
游戏启动完成
|
||
```
|
||
|
||
### 平台特定启动流程
|
||
|
||
#### Android启动流程
|
||
1. **应用启动入口**
|
||
```
|
||
AndroidManifest.xml → SplashActivity → MainActivity → WebViewActivity
|
||
```
|
||
|
||
2. **初始化阶段** (`SplashActivity.onCreate`)
|
||
```java
|
||
1. 权限检查和存储权限申请
|
||
2. 设置全屏显示和屏幕常亮
|
||
3. 初始化日志系统
|
||
4. 友盟统计初始化
|
||
5. 微信API初始化
|
||
6. 获取当前Activity信息
|
||
```
|
||
|
||
#### iOS启动流程
|
||
1. **应用启动入口**
|
||
```
|
||
Info.plist → AppDelegate → TSMainViewController → TSWebViewController
|
||
```
|
||
|
||
2. **初始化阶段** (`AppDelegate.didFinishLaunchingWithOptions`)
|
||
```objc
|
||
1. 权限检查和申请
|
||
2. 状态栏和导航栏配置
|
||
3. 日志系统初始化
|
||
4. 统计SDK初始化
|
||
5. 微信API初始化
|
||
6. 根视图控制器设置
|
||
```
|
||
|
||
#### HarmonyOS启动流程
|
||
1. **应用启动入口**
|
||
```
|
||
module.json5 → EntryAbility → Index.ets → WebView.ets
|
||
```
|
||
|
||
2. **初始化阶段** (`EntryAbility.onCreate`)
|
||
```typescript
|
||
1. 权限检查和申请
|
||
2. 窗口属性配置
|
||
3. 日志系统初始化
|
||
4. 分析服务初始化
|
||
5. 第三方SDK初始化
|
||
6. 主页面加载
|
||
```
|
||
|
||
### 配置加载流程(通用)
|
||
```javascript
|
||
// 第一阶段:本地配置加载
|
||
1. 从本地存储读取游戏配置参数:
|
||
- gamestart: 游戏启动目录
|
||
- gamedir: 游戏父目录
|
||
- gameconfig: 游戏配置URL
|
||
- agent: 代理ID
|
||
- channel: 渠道号
|
||
- market: 市场来源
|
||
|
||
// 第二阶段:版本检查
|
||
1. 检查是否首次安装
|
||
2. 从assets/bundle复制初始游戏资源
|
||
3. 检查本地版本与服务器版本
|
||
4. 决定是否需要更新游戏资源
|
||
|
||
// 第三阶段:资源更新
|
||
if (需要应用更新) {
|
||
// 跳转到应用商店或下载APK/IPA
|
||
} else if (需要游戏资源更新) {
|
||
下载游戏ZIP包 -> 解压到本地 -> 更新配置
|
||
} else {
|
||
直接启动游戏
|
||
}
|
||
|
||
// 第四阶段:游戏启动
|
||
1. 初始化H5数据 (app_data.js)
|
||
2. 写入游戏配置参数到JS文件
|
||
3. 启动主游戏WebView页面
|
||
4. 完成启动流程
|
||
```
|
||
|
||
### 关键配置文件
|
||
|
||
#### version.json (版本配置)
|
||
```json
|
||
{
|
||
"version": 1,
|
||
"gameid": "game001",
|
||
"name": "游戏名称",
|
||
"platform": "all"
|
||
}
|
||
```
|
||
|
||
#### app_data.js (运行时配置)
|
||
```javascript
|
||
var app_version = 1;
|
||
var app_gameconfig = 'config_url';
|
||
var app_gamedir = 'game_directory';
|
||
var app_gamestart = 'start_file';
|
||
var app_agent = 'agent_id';
|
||
var app_channel = 'channel_id';
|
||
var app_market = 'market_id';
|
||
var app_platform = 'android|ios|harmonyos';
|
||
下载游戏ZIP包 -> 解压到本地 -> 更新配置
|
||
} else {
|
||
直接启动游戏
|
||
}
|
||
```
|
||
|
||
#### 第六阶段:游戏启动
|
||
```java
|
||
1. 初始化H5数据 (app_data.js)
|
||
2. 写入游戏配置参数到JS文件
|
||
3. 启动主游戏WebView页面
|
||
4. 完成启动流程
|
||
```
|
||
|
||
## WebView与原生交互API
|
||
|
||
项目使用自定义的JS桥接框架实现WebView与原生Android之间的双向通信。主要基于 `BridgeWebView` 实现。
|
||
|
||
### API快速参考表
|
||
|
||
| 功能分类 | API名称 | 调用方向 | 主要用途 |
|
||
|---------|---------|----------|----------|
|
||
| **认证登录** | `accreditlogin` | JS→Native | 触发微信/QQ登录 |
|
||
| | `sharelogin` | Native→JS | 登录结果回调 |
|
||
| **社交分享** | `friendsSharetypeUrlToptitleDescript` | JS→Native | 微信/抖音分享 |
|
||
| | `sharesuccess` | Native→JS | 分享结果回调 |
|
||
| **设备信息** | `getphoneInfo` | JS→Native | 获取设备基本信息 |
|
||
| | `getbattery` | JS→Native | 获取电池电量 |
|
||
| | `getwifiLevel` | JS→Native | 获取WiFi信号强度 |
|
||
| | `getnetwork` | JS→Native | 获取网络连接状态 |
|
||
| | `getTime` | JS→Native | 获取系统时间 |
|
||
| **多媒体** | `getphoto` | JS↔Native | 图片下载处理 |
|
||
| | `opensaoma` | JS→Native | 二维码扫描 |
|
||
| | `opencamera` | JS→Native | 拍照功能 |
|
||
| **音频** | `prepareaudio` | JS→Native | 录音准备 |
|
||
| | `mediaTypeAudio` | JS→Native | 音频播放 |
|
||
| | `voicePlaying` | JS→Native | 语音动画 |
|
||
| **音视频通话** | `createRoom` | JS→Native | 创建音视频房间 |
|
||
| | `exitRoom` | JS→Native | 退出音视频房间 |
|
||
| **定位服务** | `startlocation` | JS→Native | 开始定位 |
|
||
| | `getlocationinfo` | JS→Native | 获取位置信息 |
|
||
| | `locationinfo` | Native→JS | 定位结果回调 |
|
||
| **系统交互** | `vibrator` | JS→Native | 手机震动 |
|
||
| | `orientation` | JS→Native | 屏幕方向控制 |
|
||
| | `finsh` | JS→Native | 退出应用 |
|
||
| | `notification` | JS→Native | 系统通知 |
|
||
| **摇一摇** | `startshake` | JS→Native | 开始摇一摇监听 |
|
||
| | `stopshake` | JS→Native | 停止摇一摇 |
|
||
| | `shake` | Native→JS | 摇一摇事件回调 |
|
||
| **页面操作** | `OpenurlTitleData` | JS→Native | 打开新WebView页面 |
|
||
| | `browser` | JS→Native | 系统浏览器打开链接 |
|
||
| | `backgameData` | JS→Native | 返回并传递数据 |
|
||
| **复制粘贴** | `gameCopytext` | JS→Native | 复制文本到剪贴板 |
|
||
| | `gamepastetext` | JS→Native | 获取剪贴板内容 |
|
||
| **电话状态** | `getphonestate` | JS→Native | 获取电话状态 |
|
||
| | `phoneStateChanged` | Native→JS | 电话状态变化 |
|
||
| **应用管理** | `getGameinstall` | JS→Native | 检查应用安装状态 |
|
||
| | `getmarketname` | JS→Native | 获取市场来源 |
|
||
| **前后台** | `appservice` | Native→JS | 前后台状态通知 |
|
||
|
||
### 核心组件
|
||
|
||
#### 1. 桥接框架核心类
|
||
- **`BridgeWebView`**: 继承自腾讯X5 WebView,实现JS桥接功能
|
||
- **`BridgeHandler`**: 处理JS调用原生的接口
|
||
- **`CallBackFunction`**: 原生向JS回调的接口
|
||
- **`Message`**: 消息传递的数据模型
|
||
|
||
#### 2. JS桥接原理
|
||
```javascript
|
||
// JS端调用原生方法
|
||
WebViewJavascriptBridge.callHandler('handlerName', data, function(response) {
|
||
console.log('Response from native:', response);
|
||
});
|
||
|
||
// JS端注册方法供原生调用
|
||
WebViewJavascriptBridge.registerHandler('jsHandlerName', function(data, responseCallback) {
|
||
responseCallback('Response from JS');
|
||
});
|
||
```
|
||
|
||
```java
|
||
// 原生端注册方法供JS调用
|
||
x5webview.registerHandler("handlerName", new BridgeHandler() {
|
||
@Override
|
||
public void handler(String data, CallBackFunction function) {
|
||
// 处理JS传来的数据
|
||
function.onCallBack("Response from native");
|
||
}
|
||
});
|
||
|
||
// 原生端调用JS方法
|
||
x5webview.callHandler("jsHandlerName", data, new CallBackFunction() {
|
||
@Override
|
||
public void onCallBack(String data) {
|
||
// 处理JS的回调数据
|
||
}
|
||
});
|
||
```
|
||
|
||
### 完整API接口详细说明
|
||
|
||
#### 1. 用户认证与登录类
|
||
| API名称 | 调用方向 | 参数 | 返回值 | 说明 |
|
||
|---------|----------|------|-------|------|
|
||
| `accreditlogin` | JS→Native | 登录类型 | 无 | 触发授权登录(支持微信/QQ) |
|
||
| `sharelogin` | Native→JS | 用户信息JSON | 无 | 登录成功后回传用户信息 |
|
||
|
||
**JS调用示例:**
|
||
```javascript
|
||
// 触发微信登录
|
||
WebViewJavascriptBridge.callHandler('accreditlogin', '2', function(response) {
|
||
console.log('登录触发成功');
|
||
});
|
||
```
|
||
|
||
**用户信息JSON格式:**
|
||
```json
|
||
{
|
||
"openid": "用户openid",
|
||
"headimgurl": "头像URL",
|
||
"nickname": "用户昵称",
|
||
"sex": "性别(1男2女)",
|
||
"city": "所在城市",
|
||
"province": "所在省份",
|
||
"unionid": "微信unionid"
|
||
}
|
||
```
|
||
|
||
#### 2. 社交分享功能
|
||
| API名称 | 调用方向 | 参数 | 返回值 | 说明 |
|
||
|---------|----------|------|-------|------|
|
||
| `friendsSharetypeUrlToptitleDescript` | JS→Native | 分享参数JSON | 无 | 微信分享(好友/朋友圈/抖音) |
|
||
| `sharesuccess` | Native→JS | 分享结果JSON | 无 | 分享结果回调 |
|
||
|
||
**JS调用示例:**
|
||
```javascript
|
||
// 微信好友分享
|
||
var shareData = {
|
||
"type": "1", // 1-普通分享, 2-截图分享, 3-图片分享
|
||
"sharefriend": "1", // 1-好友, 2-朋友圈, 3-抖音
|
||
"webpageUrl": "https://example.com",
|
||
"title": "分享标题",
|
||
"description": "分享描述",
|
||
"sharetype": "link"
|
||
};
|
||
WebViewJavascriptBridge.callHandler('friendsSharetypeUrlToptitleDescript', JSON.stringify(shareData));
|
||
```
|
||
|
||
**分享结果回调:**
|
||
```javascript
|
||
// 注册分享结果监听
|
||
WebViewJavascriptBridge.registerHandler('sharesuccess', function(data) {
|
||
var result = JSON.parse(data);
|
||
if(result.success === 1) {
|
||
console.log('分享成功');
|
||
} else {
|
||
console.log('分享失败');
|
||
}
|
||
});
|
||
```
|
||
|
||
#### 3. 设备信息获取类
|
||
| API名称 | 调用方向 | 参数 | 返回值 | 说明 |
|
||
|---------|----------|------|-------|------|
|
||
| `getphoneInfo` | JS→Native | 无 | 设备信息JSON | 获取手机基本配置信息 |
|
||
| `getbattery` | JS→Native | 无 | 电量百分比 | 获取当前电池电量 |
|
||
| `getwifiLevel` | JS→Native | 无 | WiFi信息JSON | 获取WiFi信号强度和名称 |
|
||
| `getnetwork` | JS→Native | 无 | 网络状态 | 获取网络连接类型 |
|
||
| `getTime` | JS→Native | 无 | 时间戳 | 获取系统当前时间戳 |
|
||
| `getcompareCode` | JS→Native | 无 | 版本对比结果 | 获取APK版本对比结果 |
|
||
|
||
**JS调用示例:**
|
||
```javascript
|
||
// 获取设备信息
|
||
WebViewJavascriptBridge.callHandler('getphoneInfo', '', function(response) {
|
||
var deviceInfo = JSON.parse(response);
|
||
console.log('设备型号:' + deviceInfo.model);
|
||
console.log('系统版本:' + deviceInfo.version);
|
||
});
|
||
|
||
// 获取网络状态
|
||
WebViewJavascriptBridge.callHandler('getnetwork', '', function(response) {
|
||
// 返回值:1-无网络, 2-WiFi, 3-移动网络
|
||
console.log('网络状态:' + response);
|
||
});
|
||
```
|
||
|
||
#### 4. 多媒体功能类
|
||
| API名称 | 调用方向 | 参数 | 返回值 | 说明 |
|
||
|---------|----------|------|-------|------|
|
||
| `getphoto` | JS→Native | 图片URL数组JSON | 无 | 批量下载网络图片到本地 |
|
||
| `opensaoma` | JS→Native | 无 | 扫码结果 | 打开相机扫描二维码 |
|
||
| `opencamera` | JS→Native | 无 | 图片路径 | 打开相机拍照 |
|
||
|
||
**图片下载示例:**
|
||
```javascript
|
||
// 批量下载图片
|
||
var imageUrls = ["http://example.com/1.jpg", "http://example.com/2.jpg"];
|
||
WebViewJavascriptBridge.callHandler('getphoto', JSON.stringify(imageUrls));
|
||
|
||
// 监听下载完成回调
|
||
WebViewJavascriptBridge.registerHandler('getphoto', function(data) {
|
||
var result = JSON.parse(data);
|
||
console.log('图片下载完成:' + result.localPath);
|
||
});
|
||
```
|
||
|
||
#### 5. 音频功能类
|
||
| API名称 | 调用方向 | 参数 | 返回值 | 说明 |
|
||
|---------|----------|------|-------|------|
|
||
| `prepareaudio` | JS→Native | 无 | 无 | 开始录音准备 |
|
||
| `mediaTypeAudio` | JS→Native | 音频参数JSON | 无 | 播放音频文件 |
|
||
| `voicePlaying` | JS→Native | 语音动画参数 | 无 | 显示语音播放动画 |
|
||
| `srcIsloop` | JS→Native | 循环播放参数 | 无 | 设置音频循环播放 |
|
||
|
||
**音频播放示例:**
|
||
```javascript
|
||
// 播放音频
|
||
var audioData = {
|
||
"audiourl": "http://example.com/audio.mp3",
|
||
"type": "1", // 1-普通播放, 2-历史记录播放
|
||
"user": "0" // 用户位置索引
|
||
};
|
||
WebViewJavascriptBridge.callHandler('mediaTypeAudio', JSON.stringify(audioData));
|
||
```
|
||
|
||
#### 6. 实时音视频功能(声网Agora)
|
||
| API名称 | 调用方向 | 参数 | 返回值 | 说明 |
|
||
|---------|----------|------|-------|------|
|
||
| `createRoom` | JS→Native | 房间信息JSON | 无 | 创建或加入语音/视频房间 |
|
||
| `exitRoom` | JS→Native | 无 | 无 | 退出当前音视频房间 |
|
||
| `getVideoinfo` | JS→Native | 无 | 视频窗口信息JSON | 获取视频悬浮窗位置信息 |
|
||
| `DragViewvideoIsshow` | JS→Native | 显示类型 | 无 | 控制视频悬浮窗显示/隐藏 |
|
||
|
||
**房间管理示例:**
|
||
```javascript
|
||
// 创建音视频房间
|
||
var roomData = {
|
||
"roomId": "12345",
|
||
"userId": "user001",
|
||
"token": "agora_token",
|
||
"type": "video" // audio 或 video
|
||
};
|
||
WebViewJavascriptBridge.callHandler('createRoom', JSON.stringify(roomData));
|
||
|
||
// 退出房间
|
||
WebViewJavascriptBridge.callHandler('exitRoom', '');
|
||
```
|
||
|
||
#### 7. 地理位置服务类
|
||
| API名称 | 调用方向 | 参数 | 返回值 | 说明 |
|
||
|---------|----------|------|-------|------|
|
||
| `startlocation` | JS→Native | 定位类型 | 无 | 开始GPS定位 |
|
||
| `getlocationinfo` | JS→Native | 无 | 位置信息JSON | 获取当前位置信息 |
|
||
| `locationinfo` | Native→JS | 位置信息JSON | 无 | 定位结果回调 |
|
||
|
||
**定位功能示例:**
|
||
```javascript
|
||
// 开始定位
|
||
WebViewJavascriptBridge.callHandler('startlocation', '1'); // 1-连续定位, 2-单次定位
|
||
|
||
// 监听定位结果
|
||
WebViewJavascriptBridge.registerHandler('locationinfo', function(data) {
|
||
var location = JSON.parse(data);
|
||
console.log('纬度:' + location.latitude);
|
||
console.log('经度:' + location.longitude);
|
||
console.log('地址:' + location.address);
|
||
});
|
||
```
|
||
|
||
#### 8. 系统交互功能类
|
||
| API名称 | 调用方向 | 参数 | 返回值 | 说明 |
|
||
|---------|----------|------|-------|------|
|
||
| `vibrator` | JS→Native | 震动时长(毫秒) | 无 | 手机震动指定时长 |
|
||
| `repeatvibrator` | JS→Native | 震动参数 | 无 | 重复震动 |
|
||
| `canclevibrator` | JS→Native | 无 | 无 | 取消所有震动 |
|
||
| `orientation` | JS→Native | 方向值 | 无 | 设置屏幕方向锁定 |
|
||
| `finsh` | JS→Native | 无 | 无 | 退出应用程序 |
|
||
| `notification` | JS→Native | 通知内容 | 无 | 发送系统通知 |
|
||
|
||
**系统交互示例:**
|
||
```javascript
|
||
// 震动500毫秒
|
||
WebViewJavascriptBridge.callHandler('vibrator', '500');
|
||
|
||
// 设置横屏
|
||
WebViewJavascriptBridge.callHandler('orientation', '0'); // 0-横屏, 1-竖屏
|
||
|
||
// 发送通知
|
||
WebViewJavascriptBridge.callHandler('notification', '您有新消息');
|
||
```
|
||
|
||
#### 9. 摇一摇功能类
|
||
| API名称 | 调用方向 | 参数 | 返回值 | 说明 |
|
||
|---------|----------|------|-------|------|
|
||
| `startshake` | JS→Native | 无 | 无 | 开始监听摇一摇动作 |
|
||
| `stopshake` | JS→Native | 无 | 无 | 停止监听摇一摇 |
|
||
| `SwitchShake` | JS→Native | 开关状态 | 无 | 摇一摇震动声音开关 |
|
||
| `shake` | Native→JS | 摇一摇事件 | 无 | 摇一摇动作回调 |
|
||
|
||
**摇一摇功能示例:**
|
||
```javascript
|
||
// 开始摇一摇监听
|
||
WebViewJavascriptBridge.callHandler('startshake', '');
|
||
|
||
// 监听摇一摇事件
|
||
WebViewJavascriptBridge.registerHandler('shake', function(data) {
|
||
console.log('检测到摇一摇动作');
|
||
// 处理摇一摇逻辑
|
||
});
|
||
|
||
// 停止摇一摇
|
||
WebViewJavascriptBridge.callHandler('stopshake', '');
|
||
```
|
||
|
||
#### 10. 网页操作类
|
||
| API名称 | 调用方向 | 参数 | 返回值 | 说明 |
|
||
|---------|----------|------|-------|------|
|
||
| `OpenurlTitleData` | JS→Native | 网页参数JSON | 无 | 打开新的WebView页面 |
|
||
| `browser` | JS→Native | URL | 无 | 使用系统浏览器打开链接 |
|
||
| `backgameData` | JS→Native | 返回数据 | 无 | 返回上级页面并传递数据 |
|
||
|
||
**页面跳转示例:**
|
||
```javascript
|
||
// 打开新页面
|
||
var pageData = {
|
||
"url": "http://example.com/page.html",
|
||
"title": "页面标题",
|
||
"data": "传递数据",
|
||
"orientation": "1" // 0-横屏, 1-竖屏
|
||
};
|
||
WebViewJavascriptBridge.callHandler('OpenurlTitleData', JSON.stringify(pageData));
|
||
|
||
// 返回上级页面
|
||
WebViewJavascriptBridge.callHandler('backgameData', '返回的数据');
|
||
```
|
||
|
||
#### 11. 复制粘贴功能类
|
||
| API名称 | 调用方向 | 参数 | 返回值 | 说明 |
|
||
|---------|----------|------|-------|------|
|
||
| `gameCopytext` | JS→Native | 要复制的文本 | 无 | 复制文本到系统剪贴板 |
|
||
| `gamepastetext` | JS→Native | 无 | 剪贴板内容 | 获取剪贴板中的文本 |
|
||
|
||
**复制粘贴示例:**
|
||
```javascript
|
||
// 复制文本
|
||
WebViewJavascriptBridge.callHandler('gameCopytext', '要复制的文本内容');
|
||
|
||
// 获取剪贴板内容
|
||
WebViewJavascriptBridge.callHandler('gamepastetext', '', function(response) {
|
||
console.log('剪贴板内容:' + response);
|
||
});
|
||
```
|
||
|
||
#### 12. 电话状态监听类
|
||
| API名称 | 调用方向 | 参数 | 返回值 | 说明 |
|
||
|---------|----------|------|-------|------|
|
||
| `getphonestate` | JS→Native | 无 | 电话状态码 | 获取当前电话通话状态 |
|
||
| `phoneStateChanged` | Native→JS | 状态码 | 无 | 电话状态变化回调 |
|
||
|
||
**电话状态监听示例:**
|
||
```javascript
|
||
// 获取电话状态
|
||
WebViewJavascriptBridge.callHandler('getphonestate', '', function(response) {
|
||
// 0-空闲, 1-通话中, 2-来电振铃
|
||
console.log('电话状态:' + response);
|
||
});
|
||
|
||
// 监听电话状态变化
|
||
WebViewJavascriptBridge.registerHandler('phoneStateChanged', function(data) {
|
||
console.log('电话状态改变:' + data);
|
||
});
|
||
```
|
||
|
||
#### 13. 应用管理类
|
||
| API名称 | 调用方向 | 参数 | 返回值 | 说明 |
|
||
|---------|----------|------|-------|------|
|
||
| `getGameinstall` | JS→Native | 包名 | 安装状态 | 检查指定应用是否已安装 |
|
||
| `openApplyDownloadpath` | JS→Native | 无 | 无 | 打开应用下载安装包 |
|
||
| `getmarketname` | JS→Native | 无 | 市场标识 | 获取应用市场来源标识 |
|
||
| `getothername` | JS→Native | 文件夹名 | 自定义名称 | 获取自定义文件夹名称 |
|
||
| `getOther` | JS→Native | 无 | 其他信息 | 获取其他配置信息 |
|
||
|
||
**应用管理示例:**
|
||
```javascript
|
||
// 检查应用是否安装
|
||
WebViewJavascriptBridge.callHandler('getGameinstall', 'com.tencent.mm', function(response) {
|
||
if(response === '1') {
|
||
console.log('微信已安装');
|
||
} else {
|
||
console.log('微信未安装');
|
||
}
|
||
});
|
||
|
||
// 获取市场来源
|
||
WebViewJavascriptBridge.callHandler('getmarketname', '', function(response) {
|
||
console.log('市场来源:' + response);
|
||
});
|
||
```
|
||
|
||
#### 14. 前后台状态管理类
|
||
| API名称 | 调用方向 | 参数 | 返回值 | 说明 |
|
||
|---------|----------|------|-------|------|
|
||
| `SwitchOverGameData` | JS→Native | 流量提醒参数 | 无 | 设置移动网络流量提醒 |
|
||
| `appservice` | Native→JS | 前后台状态 | 无 | 应用前后台状态变化通知 |
|
||
|
||
**前后台监听示例:**
|
||
```javascript
|
||
// 监听应用前后台状态
|
||
WebViewJavascriptBridge.registerHandler('appservice', function(data) {
|
||
if(data === '1') {
|
||
console.log('应用进入前台');
|
||
// 恢复游戏逻辑
|
||
} else if(data === '2') {
|
||
console.log('应用进入后台');
|
||
// 暂停游戏逻辑
|
||
}
|
||
});
|
||
```
|
||
|
||
#### 15. 通讯录功能类(已禁用)
|
||
| API名称 | 调用方向 | 参数 | 返回值 | 说明 |
|
||
|---------|----------|------|-------|------|
|
||
| `getAddressBook` | JS→Native | 无 | 通讯录数据 | 获取手机通讯录(已注释禁用) |
|
||
|
||
> **注意**: 通讯录功能因隐私政策要求已被注释禁用。
|
||
|
||
### 技术实现细节
|
||
|
||
#### JS桥接机制
|
||
项目采用自定义的JS Bridge实现WebView与原生的双向通信:
|
||
|
||
**核心组件:**
|
||
- `BridgeWebView.java` - 继承腾讯X5 WebView,实现桥接功能
|
||
- `BridgeHandler.java` - 原生端消息处理接口
|
||
- `CallBackFunction.java` - JS回调函数接口
|
||
- `Message.java` - 消息实体类
|
||
- `WebViewJavascriptBridge.js` - JS端桥接脚本
|
||
|
||
**消息传递流程:**
|
||
1. JS调用原生:`WebViewJavascriptBridge.callHandler(handlerName, data, callback)`
|
||
2. 消息序列化并添加到消息队列
|
||
3. 通过`loadUrl()`触发原生方法`flushMessageQueue()`
|
||
4. 原生端解析消息并调用对应的`BridgeHandler`
|
||
5. 处理完成后通过`CallBackFunction`返回结果给JS
|
||
|
||
**线程安全保证:**
|
||
```java
|
||
// 确保JS调用在主线程执行
|
||
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
|
||
this.loadUrl(javascriptCommand);
|
||
}
|
||
```
|
||
|
||
#### 权限管理机制
|
||
应用采用动态权限申请机制,主要权限包括:
|
||
|
||
**存储权限(必需):**
|
||
```java
|
||
// 动态申请存储权限
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||
if (ContextCompat.checkSelfPermission(this,
|
||
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||
ActivityCompat.requestPermissions(this,
|
||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
|
||
}
|
||
}
|
||
```
|
||
|
||
**位置权限(可选):**
|
||
- GPS定位:`ACCESS_FINE_LOCATION`
|
||
- 网络定位:`ACCESS_COARSE_LOCATION`
|
||
|
||
**音频权限(可选):**
|
||
- 录音权限:`RECORD_AUDIO`
|
||
- 音频设置:`MODIFY_AUDIO_SETTINGS`
|
||
|
||
#### 资源更新机制
|
||
项目支持热更新,无需重新安装APK即可更新游戏内容:
|
||
|
||
**更新流程:**
|
||
1. 检查本地版本文件`version.xml`
|
||
2. 请求服务器版本信息
|
||
3. 比较版本号决定更新策略:
|
||
- APK版本更新:下载新APK并提示安装
|
||
- 资源版本更新:下载资源ZIP包并解压
|
||
4. 更新本地版本文件
|
||
5. 重新加载游戏
|
||
|
||
**版本控制文件:**
|
||
```xml
|
||
<!-- version.xml -->
|
||
<version>
|
||
<version>资源版本号</version>
|
||
<gameid>游戏标识</gameid>
|
||
<name>游戏名称</name>
|
||
</version>
|
||
```
|
||
|
||
#### 微信SDK集成
|
||
项目集成微信开放平台SDK,支持登录、分享功能:
|
||
|
||
**回调Activity配置:**
|
||
```xml
|
||
<!-- 微信登录回调 -->
|
||
<activity
|
||
android:name=".wxapi.WXEntryActivity"
|
||
android:exported="true"
|
||
android:launchMode="singleTop" />
|
||
```
|
||
|
||
**分享实现:**
|
||
- 支持链接分享、图片分享、小程序分享
|
||
- 区分好友分享和朋友圈分享
|
||
- 抖音分享通过Intent方式实现
|
||
|
||
#### 音视频功能
|
||
集成声网Agora SDK实现实时音视频通话:
|
||
|
||
**基本功能:**
|
||
- 语音房间创建/加入
|
||
- 实时语音通话
|
||
- 视频悬浮窗显示
|
||
- 音频录制播放
|
||
|
||
**房间管理:**
|
||
```javascript
|
||
// 创建音视频房间
|
||
var roomConfig = {
|
||
"roomId": "房间ID",
|
||
"userId": "用户ID",
|
||
"token": "Agora Token",
|
||
"channelProfile": "COMMUNICATION"
|
||
};
|
||
```
|
||
|
||
#### 定位服务
|
||
基于高德地图SDK实现精确定位:
|
||
|
||
**定位模式:**
|
||
- 连续定位:持续获取位置更新
|
||
- 单次定位:获取一次高精度位置
|
||
|
||
**位置信息回调:**
|
||
```javascript
|
||
// 位置信息格式
|
||
{
|
||
"latitude": "31.12345", // 纬度
|
||
"longitude": "121.12345", // 经度
|
||
"accuracy": "15.0", // 精度(米)
|
||
"address": "详细地址",
|
||
"city": "城市名称",
|
||
"province": "省份"
|
||
}
|
||
```
|
||
|
||
### API调用最佳实践
|
||
|
||
#### 1. 错误处理
|
||
```javascript
|
||
// JS端错误处理
|
||
WebViewJavascriptBridge.callHandler('apiName', data, function(response) {
|
||
try {
|
||
var result = JSON.parse(response);
|
||
if(result.success) {
|
||
// 处理成功逻辑
|
||
} else {
|
||
console.error('API调用失败:', result.error);
|
||
}
|
||
} catch(e) {
|
||
console.error('响应解析失败:', e);
|
||
}
|
||
});
|
||
```
|
||
|
||
#### 2. 生命周期管理
|
||
```java
|
||
// 原生端生命周期处理
|
||
@Override
|
||
protected void onDestroy() {
|
||
super.onDestroy();
|
||
// 清理WebView
|
||
if(x5webview != null) {
|
||
x5webview.removeAllViews();
|
||
x5webview.destroy();
|
||
x5webview = null;
|
||
}
|
||
// 停止定位服务
|
||
if(locationClient != null) {
|
||
locationClient.stopLocation();
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. 内存优化
|
||
```java
|
||
// WebView内存优化
|
||
public void optimizeWebView() {
|
||
// 清除缓存
|
||
x5webview.clearCache(true);
|
||
// 清除历史记录
|
||
x5webview.clearHistory();
|
||
// 暂停JavaScript执行
|
||
x5webview.onPause();
|
||
x5webview.pauseTimers();
|
||
}
|
||
```
|
||
|
||
#### 4. 网络优化
|
||
```java
|
||
// 网络状态监听
|
||
private void checkNetworkState() {
|
||
ConnectivityManager cm = (ConnectivityManager)
|
||
getSystemService(Context.CONNECTIVITY_SERVICE);
|
||
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
|
||
|
||
if(activeNetwork != null && activeNetwork.isConnected()) {
|
||
if(activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
|
||
// WiFi连接
|
||
notifyJSNetworkState("2");
|
||
} else {
|
||
// 移动网络连接
|
||
notifyJSNetworkState("3");
|
||
}
|
||
} else {
|
||
// 无网络连接
|
||
notifyJSNetworkState("1");
|
||
}
|
||
}
|
||
```
|
||
|
||
### 调试和测试
|
||
|
||
#### WebView调试
|
||
```java
|
||
// 启用WebView调试模式
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||
WebView.setWebContentsDebuggingEnabled(true);
|
||
}
|
||
```
|
||
|
||
在Chrome浏览器中访问 `chrome://inspect` 可以调试WebView内容。
|
||
|
||
#### JS Bridge测试
|
||
```javascript
|
||
// 测试桥接连通性
|
||
function testBridge() {
|
||
if(typeof WebViewJavascriptBridge !== 'undefined') {
|
||
console.log('Bridge已就绪');
|
||
// 测试简单API调用
|
||
WebViewJavascriptBridge.callHandler('getTime', '', function(response) {
|
||
console.log('当前时间:', response);
|
||
});
|
||
} else {
|
||
console.error('Bridge未初始化');
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 性能监控
|
||
```java
|
||
// 监控WebView性能
|
||
public class WebViewPerformanceMonitor {
|
||
private long pageStartTime;
|
||
|
||
@Override
|
||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||
pageStartTime = System.currentTimeMillis();
|
||
}
|
||
|
||
@Override
|
||
public void onPageFinished(WebView view, String url) {
|
||
long loadTime = System.currentTimeMillis() - pageStartTime;
|
||
Log.d("Performance", "页面加载耗时: " + loadTime + "ms");
|
||
}
|
||
}
|
||
```
|
||
|
||
### 常见问题和解决方案
|
||
|
||
#### 1. JS Bridge不响应
|
||
**问题**: JS调用原生方法无响应
|
||
**解决方案**:
|
||
- 检查WebView是否已完全加载
|
||
- 确认Handler名称拼写正确
|
||
- 验证是否在主线程调用
|
||
|
||
#### 2. 分享功能失效
|
||
**问题**: 微信分享无法拉起
|
||
**解决方案**:
|
||
- 检查微信SDK版本兼容性
|
||
- 验证应用签名是否与微信开放平台一致
|
||
- 确认回调Activity配置正确
|
||
|
||
#### 3. 定位权限问题
|
||
**问题**: 无法获取位置信息
|
||
**解决方案**:
|
||
- 动态申请定位权限
|
||
- 检查GPS是否开启
|
||
- 验证高德SDK配置
|
||
|
||
#### 4. 音视频通话异常
|
||
**问题**: 声网通话质量差或连接失败
|
||
**解决方案**:
|
||
- 检查网络连接质量
|
||
- 验证Agora Token有效性
|
||
- 优化音频编码参数
|
||
|
||
### 完整集成示例
|
||
|
||
#### 前端HTML页面集成示例
|
||
```html
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>TSGame Web</title>
|
||
</head>
|
||
<body>
|
||
<div id="app">
|
||
<button onclick="loginWithWeChat()">微信登录</button>
|
||
<button onclick="shareToWeChat()">微信分享</button>
|
||
<button onclick="getDeviceInfo()">获取设备信息</button>
|
||
<button onclick="startLocation()">开始定位</button>
|
||
<button onclick="takePhoto()">拍照</button>
|
||
<button onclick="playAudio()">播放音频</button>
|
||
<button onclick="startShake()">开始摇一摇</button>
|
||
</div>
|
||
|
||
<script>
|
||
// 初始化Bridge连接
|
||
function connectWebViewJavascriptBridge(callback) {
|
||
if (window.WebViewJavascriptBridge) {
|
||
callback(WebViewJavascriptBridge);
|
||
} else {
|
||
document.addEventListener('WebViewJavascriptBridgeReady', function() {
|
||
callback(WebViewJavascriptBridge);
|
||
}, false);
|
||
}
|
||
}
|
||
|
||
// 等待Bridge就绪
|
||
connectWebViewJavascriptBridge(function(bridge) {
|
||
console.log('Bridge连接成功');
|
||
|
||
// 注册接收原生回调的方法
|
||
bridge.registerHandler('sharelogin', function(data) {
|
||
var userInfo = JSON.parse(data);
|
||
console.log('登录成功:', userInfo);
|
||
alert('欢迎 ' + userInfo.nickname);
|
||
});
|
||
|
||
bridge.registerHandler('sharesuccess', function(data) {
|
||
var result = JSON.parse(data);
|
||
if(result.success === 1) {
|
||
alert('分享成功');
|
||
} else {
|
||
alert('分享失败');
|
||
}
|
||
});
|
||
|
||
bridge.registerHandler('locationinfo', function(data) {
|
||
var location = JSON.parse(data);
|
||
console.log('定位结果:', location);
|
||
alert('当前位置: ' + location.address);
|
||
});
|
||
|
||
bridge.registerHandler('shake', function(data) {
|
||
console.log('检测到摇一摇');
|
||
alert('摇一摇触发!');
|
||
});
|
||
|
||
bridge.registerHandler('appservice', function(data) {
|
||
if(data === '1') {
|
||
console.log('应用进入前台');
|
||
} else if(data === '2') {
|
||
console.log('应用进入后台');
|
||
}
|
||
});
|
||
});
|
||
|
||
// 微信登录
|
||
function loginWithWeChat() {
|
||
WebViewJavascriptBridge.callHandler('accreditlogin', '2', function(response) {
|
||
console.log('登录请求已发送');
|
||
});
|
||
}
|
||
|
||
// 微信分享
|
||
function shareToWeChat() {
|
||
var shareData = {
|
||
"type": "1",
|
||
"sharefriend": "1",
|
||
"webpageUrl": "https://example.com",
|
||
"title": "精彩棋牌游戏",
|
||
"description": "快来和朋友一起玩棋牌游戏吧!"
|
||
};
|
||
WebViewJavascriptBridge.callHandler('friendsSharetypeUrlToptitleDescript',
|
||
JSON.stringify(shareData));
|
||
}
|
||
|
||
// 获取设备信息
|
||
function getDeviceInfo() {
|
||
WebViewJavascriptBridge.callHandler('getphoneInfo', '', function(response) {
|
||
var deviceInfo = JSON.parse(response);
|
||
console.log('设备信息:', deviceInfo);
|
||
alert('设备型号: ' + deviceInfo.model);
|
||
});
|
||
}
|
||
|
||
// 开始定位
|
||
function startLocation() {
|
||
WebViewJavascriptBridge.callHandler('startlocation', '2'); // 单次定位
|
||
}
|
||
|
||
// 拍照
|
||
function takePhoto() {
|
||
WebViewJavascriptBridge.callHandler('opencamera', '', function(imagePath) {
|
||
console.log('照片路径:', imagePath);
|
||
if(imagePath) {
|
||
// 显示拍摄的照片
|
||
var img = document.createElement('img');
|
||
img.src = 'file://' + imagePath;
|
||
img.style.maxWidth = '300px';
|
||
document.body.appendChild(img);
|
||
}
|
||
});
|
||
}
|
||
|
||
// 播放音频
|
||
function playAudio() {
|
||
var audioData = {
|
||
"audiourl": "https://example.com/audio.mp3",
|
||
"type": "1",
|
||
"user": "0"
|
||
};
|
||
WebViewJavascriptBridge.callHandler('mediaTypeAudio', JSON.stringify(audioData));
|
||
}
|
||
|
||
// 开始摇一摇
|
||
function startShake() {
|
||
WebViewJavascriptBridge.callHandler('startshake', '');
|
||
alert('开始摇一摇监听,请摇动手机');
|
||
}
|
||
|
||
// 获取网络状态
|
||
function checkNetwork() {
|
||
WebViewJavascriptBridge.callHandler('getnetwork', '', function(response) {
|
||
var status = ['无网络', 'WiFi', '移动网络'][parseInt(response) - 1];
|
||
alert('网络状态: ' + status);
|
||
});
|
||
}
|
||
|
||
// 复制文本
|
||
function copyText() {
|
||
var text = '这是要复制的文本内容';
|
||
WebViewJavascriptBridge.callHandler('gameCopytext', text);
|
||
alert('已复制到剪贴板');
|
||
}
|
||
|
||
// 获取剪贴板内容
|
||
function pasteText() {
|
||
WebViewJavascriptBridge.callHandler('gamepastetext', '', function(response) {
|
||
alert('剪贴板内容: ' + response);
|
||
});
|
||
}
|
||
|
||
// 震动
|
||
function vibrate() {
|
||
WebViewJavascriptBridge.callHandler('vibrator', '500'); // 震动500毫秒
|
||
}
|
||
|
||
// 退出应用
|
||
function exitApp() {
|
||
if(confirm('确定要退出应用吗?')) {
|
||
WebViewJavascriptBridge.callHandler('finsh', '');
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
#### 原生端集成示例
|
||
```java
|
||
public class GameWebViewActivity extends Activity {
|
||
private BridgeWebView webView;
|
||
|
||
@Override
|
||
protected void onCreate(Bundle savedInstanceState) {
|
||
super.onCreate(savedInstanceState);
|
||
setContentView(R.layout.activity_webview);
|
||
|
||
// 初始化WebView
|
||
webView = findViewById(R.id.webview);
|
||
setupWebView();
|
||
registerHandlers();
|
||
|
||
// 加载游戏页面
|
||
webView.loadUrl("file:///android_asset/game.html");
|
||
}
|
||
|
||
private void setupWebView() {
|
||
WebSettings settings = webView.getSettings();
|
||
settings.setJavaScriptEnabled(true);
|
||
settings.setDomStorageEnabled(true);
|
||
settings.setAllowFileAccess(true);
|
||
settings.setAllowContentAccess(true);
|
||
|
||
// 设置UserAgent
|
||
String userAgent = settings.getUserAgentString();
|
||
settings.setUserAgentString(userAgent + " TSGame/1.0");
|
||
}
|
||
|
||
private void registerHandlers() {
|
||
// 注册登录处理
|
||
webView.registerHandler("accreditlogin", new BridgeHandler() {
|
||
@Override
|
||
public void handler(String data, CallBackFunction function) {
|
||
if("2".equals(data)) {
|
||
// 触发微信登录
|
||
wechatLogin();
|
||
}
|
||
function.onCallBack("success");
|
||
}
|
||
});
|
||
|
||
// 注册分享处理
|
||
webView.registerHandler("friendsSharetypeUrlToptitleDescript", new BridgeHandler() {
|
||
@Override
|
||
public void handler(String data, CallBackFunction function) {
|
||
try {
|
||
JSONObject shareData = new JSONObject(data);
|
||
String type = shareData.getString("type");
|
||
String shareType = shareData.getString("sharefriend");
|
||
|
||
if("1".equals(shareType)) {
|
||
// 微信好友分享
|
||
shareToWechatFriend(shareData);
|
||
} else if("2".equals(shareType)) {
|
||
// 微信朋友圈分享
|
||
shareToWechatTimeline(shareData);
|
||
}
|
||
} catch (JSONException e) {
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
});
|
||
|
||
// 注册设备信息处理
|
||
webView.registerHandler("getphoneInfo", new BridgeHandler() {
|
||
@Override
|
||
public void handler(String data, CallBackFunction function) {
|
||
JSONObject deviceInfo = getDeviceInfo();
|
||
function.onCallBack(deviceInfo.toString());
|
||
}
|
||
});
|
||
|
||
// 注册定位处理
|
||
webView.registerHandler("startlocation", new BridgeHandler() {
|
||
@Override
|
||
public void handler(String data, CallBackFunction function) {
|
||
if("1".equals(data)) {
|
||
startContinuousLocation();
|
||
} else {
|
||
startSingleLocation();
|
||
}
|
||
}
|
||
});
|
||
|
||
// 注册拍照处理
|
||
webView.registerHandler("opencamera", new BridgeHandler() {
|
||
@Override
|
||
public void handler(String data, CallBackFunction function) {
|
||
cameraCallback = function;
|
||
openCamera();
|
||
}
|
||
});
|
||
}
|
||
|
||
private void wechatLogin() {
|
||
// 微信登录实现
|
||
SendAuth.Req req = new SendAuth.Req();
|
||
req.scope = "snsapi_userinfo";
|
||
req.state = "tsgame_login";
|
||
api.sendReq(req);
|
||
}
|
||
|
||
private JSONObject getDeviceInfo() {
|
||
JSONObject info = new JSONObject();
|
||
try {
|
||
info.put("model", Build.MODEL);
|
||
info.put("brand", Build.BRAND);
|
||
info.put("version", Build.VERSION.RELEASE);
|
||
info.put("sdk", Build.VERSION.SDK_INT);
|
||
|
||
DisplayMetrics dm = getResources().getDisplayMetrics();
|
||
info.put("screenWidth", dm.widthPixels);
|
||
info.put("screenHeight", dm.heightPixels);
|
||
info.put("density", dm.density);
|
||
} catch (JSONException e) {
|
||
e.printStackTrace();
|
||
}
|
||
return info;
|
||
}
|
||
|
||
// 微信登录成功回调
|
||
public void onWechatLoginSuccess(String openId, String nickname, String headUrl) {
|
||
JSONObject userInfo = new JSONObject();
|
||
try {
|
||
userInfo.put("openid", openId);
|
||
userInfo.put("nickname", nickname);
|
||
userInfo.put("headimgurl", headUrl);
|
||
|
||
// 回调给JS
|
||
webView.callHandler("sharelogin", userInfo.toString(), null);
|
||
} catch (JSONException e) {
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
|
||
// 定位成功回调
|
||
public void onLocationSuccess(double lat, double lng, String address) {
|
||
JSONObject location = new JSONObject();
|
||
try {
|
||
location.put("latitude", String.valueOf(lat));
|
||
location.put("longitude", String.valueOf(lng));
|
||
location.put("address", address);
|
||
|
||
// 回调给JS
|
||
webView.callHandler("locationinfo", location.toString(), null);
|
||
} catch (JSONException e) {
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
|
||
@Override
|
||
protected void onDestroy() {
|
||
super.onDestroy();
|
||
if(webView != null) {
|
||
webView.removeAllViews();
|
||
webView.destroy();
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
这个完整的集成示例展示了:
|
||
1. 前端HTML页面如何初始化Bridge连接
|
||
2. 如何注册监听原生回调
|
||
3. 如何调用各种原生API
|
||
4. 原生端如何注册Handler处理JS调用
|
||
5. 如何实现完整的登录、分享、定位等功能流程
|
||
|
||
通过这个示例,开发者可以快速理解和集成TSGame的WebView与原生交互功能。
|
||
|
||
#### JS端调用原生方法示例
|
||
```javascript
|
||
// 1. 获取设备信息
|
||
WebViewJavascriptBridge.callHandler('getphoneInfo', '', function(response) {
|
||
console.log('设备信息:', response);
|
||
});
|
||
|
||
// 2. 微信分享
|
||
var shareData = {
|
||
type: "1",
|
||
sharefriend: "1",
|
||
webpageUrl: "https://example.com",
|
||
title: "分享标题",
|
||
description: "分享描述"
|
||
};
|
||
WebViewJavascriptBridge.callHandler('friendsSharetypeUrlToptitleDescript',
|
||
JSON.stringify(shareData));
|
||
|
||
// 3. 震动效果
|
||
WebViewJavascriptBridge.callHandler('vibrator', '500');
|
||
```
|
||
|
||
#### 原生端调用JS方法示例
|
||
```java
|
||
// 1. 发送用户登录信息给JS
|
||
JSONObject userInfo = new JSONObject();
|
||
userInfo.put("openid", openid);
|
||
userInfo.put("nickname", nickname);
|
||
x5webview.callHandler("sharelogin", userInfo.toString(),
|
||
new CallBackFunction() {
|
||
@Override
|
||
public void onCallBack(String data) {
|
||
// JS的回调处理
|
||
}
|
||
});
|
||
|
||
// 2. 通知JS分享结果
|
||
JSONObject result = new JSONObject();
|
||
result.put("success", 1);
|
||
result.put("type", "1");
|
||
x5webview.callHandler("sharesuccess", result.toString(), null);
|
||
```
|
||
|
||
### 注意事项
|
||
|
||
#### 数据传递规范
|
||
1. **数据格式**: 所有传递的数据都是字符串格式,复杂数据需要JSON序列化
|
||
2. **异步回调**: 大部分API都支持异步回调机制
|
||
3. **错误处理**: 需要在JS和原生端都做好异常处理
|
||
4. **线程安全**: 原生端调用JS必须在主线程进行
|
||
5. **生命周期**: 注意WebView生命周期,避免内存泄漏
|
||
|
||
#### WebView与原生通信注意事项
|
||
1. **消息队列**: 使用消息队列机制确保数据不丢失
|
||
2. **特殊字符转义**: JSON字符串中的特殊字符需要转义处理
|
||
3. **回调函数**: 每个API调用可以包含一个回调函数
|
||
4. **超时处理**: 长时间无响应的API调用需要超时机制
|
||
|
||
#### 性能优化建议
|
||
1. **减少频繁调用**: 避免在短时间内频繁调用原生API
|
||
2. **数据压缩**: 传递大量数据时考虑压缩或分批传输
|
||
3. **内存管理**: 及时释放不需要的WebView资源
|
||
4. **缓存策略**: 合理利用WebView缓存机制
|
||
|
||
#### 安全性考虑
|
||
1. **数据校验**: 对JS传入的数据进行严格校验
|
||
2. **权限控制**: 敏感API需要权限检查
|
||
3. **防注入**: 防范恶意JS代码注入
|
||
4. **HTTPS通信**: 敏感数据传输使用HTTPS
|
||
|
||
#### 兼容性处理
|
||
1. **Android版本**: 针对不同Android版本做适配
|
||
2. **设备差异**: 考虑不同设备的硬件差异
|
||
3. **WebView版本**: 处理不同WebView内核版本的差异
|
||
4. **网络环境**: 适配不同网络环境下的功能表现
|
||
|
||
### 3. 主要Activity流转
|
||
|
||
```
|
||
weclomeactivity1 (启动页)
|
||
↓ (配置加载完成)
|
||
webviewActivity/NewwebviewActivity (主游戏页面)
|
||
↓ (外部链接)
|
||
openwebActivity1 (外部页面)
|
||
↓ (微信回调)
|
||
WXEntryActivity (微信相关)
|
||
```
|
||
|
||
### 4. 关键配置文件
|
||
|
||
#### version.xml (版本配置)
|
||
```xml
|
||
<version>
|
||
<version>1</version>
|
||
<gameid>game001</gameid>
|
||
<name>游戏名称</name>
|
||
</version>
|
||
```
|
||
|
||
#### app_data.js (运行时配置)
|
||
```javascript
|
||
var app_version = 1;
|
||
var app_gameconfig = 'config_url';
|
||
var app_gamedir = 'game_directory';
|
||
var app_gamestart = 'start_file';
|
||
var app_agent = 'agent_id';
|
||
var app_channel = 'channel_id';
|
||
var app_market = 'market_id';
|
||
```
|
||
|
||
## 平台技术栈与第三方服务
|
||
|
||
### Android平台
|
||
#### 开发工具和框架
|
||
- **Gradle**: 7.0+ (构建工具)
|
||
- **Android SDK**: API 33
|
||
- **最低支持**: API 21 (Android 5.0)
|
||
- **开发语言**: Java 1.8 / Kotlin 1.8
|
||
- **IDE**: Android Studio
|
||
|
||
#### 第三方SDK集成
|
||
| 功能模块 | Android SDK | 版本 |
|
||
|---------|-------------|------|
|
||
| **WebView** | 腾讯X5 WebView | 44286 |
|
||
| **网络请求** | OkHttp | 4.12.0 |
|
||
| **JSON解析** | Gson | 2.8.9 |
|
||
| **微信SDK** | 微信开放平台SDK | 6.8.11 |
|
||
| **地图定位** | 高德地图SDK | 5.2.0 |
|
||
| **音视频** | 声网Agora SDK | 最新版 |
|
||
| **统计分析** | 友盟统计 | 7.5.0 |
|
||
| **崩溃收集** | 腾讯Bugly | 3.4.4 |
|
||
| **二维码扫描** | ZXing | 3.5.0 |
|
||
|
||
### iOS平台
|
||
#### 开发工具和框架
|
||
- **Xcode**: 14.0+
|
||
- **iOS SDK**: iOS 16.0
|
||
- **最低支持**: iOS 11.0
|
||
- **开发语言**: Objective-C / Swift 5.0
|
||
- **依赖管理**: CocoaPods / Swift Package Manager
|
||
|
||
#### 第三方SDK集成
|
||
| 功能模块 | iOS SDK | 版本 |
|
||
|---------|---------|------|
|
||
| **WebView** | WKWebView (系统) | - |
|
||
| **网络请求** | AFNetworking / Alamofire | 5.0+ |
|
||
| **JSON解析** | 系统JSONSerialization | - |
|
||
| **微信SDK** | 微信开放平台SDK | 1.9.2 |
|
||
| **地图定位** | 高德地图SDK / CoreLocation | 9.0+ |
|
||
| **音视频** | 声网Agora SDK | 最新版 |
|
||
| **统计分析** | 友盟统计 / Firebase | 最新版 |
|
||
| **崩溃收集** | Bugly / Crashlytics | 最新版 |
|
||
| **二维码扫描** | AVFoundation (系统) | - |
|
||
|
||
### HarmonyOS平台
|
||
#### 开发工具和框架
|
||
- **DevEco Studio**: 4.0+
|
||
- **HarmonyOS SDK**: API 9
|
||
- **最低支持**: API 8 (HarmonyOS 3.0)
|
||
- **开发语言**: ArkTS / TypeScript
|
||
- **应用模型**: Stage模型
|
||
|
||
#### 第三方SDK集成
|
||
| 功能模块 | HarmonyOS SDK | 版本 |
|
||
|---------|---------------|------|
|
||
| **WebView** | Web组件 (系统) | - |
|
||
| **网络请求** | @ohos.net.http | 系统API |
|
||
| **JSON解析** | JSON (系统) | - |
|
||
| **微信SDK** | 微信开放平台SDK | 待适配 |
|
||
| **地图定位** | 华为地图服务 | 最新版 |
|
||
| **音视频** | 声网Agora SDK | 最新版 |
|
||
| **统计分析** | 华为分析服务 | 最新版 |
|
||
| **崩溃收集** | 华为崩溃服务 | 最新版 |
|
||
| **二维码扫描** | @ohos.multimedia.camera | 系统API |
|
||
|
||
### 跨平台共享组件
|
||
- **H5游戏引擎**: HTML5 + CSS3 + JavaScript
|
||
- **JS Bridge**: 统一接口定义
|
||
- **音视频通话**: 声网Agora SDK(全平台支持)
|
||
- **数据存储**: 本地存储 + 云端同步
|
||
- **配置管理**: JSON配置文件
|
||
|
||
## 跨平台权限管理
|
||
|
||
### Android权限配置
|
||
#### 核心权限
|
||
```xml
|
||
<!-- AndroidManifest.xml -->
|
||
<uses-permission android:name="android.permission.INTERNET" />
|
||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||
```
|
||
|
||
#### 定位权限
|
||
```xml
|
||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||
```
|
||
|
||
#### 音频权限
|
||
```xml
|
||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||
```
|
||
|
||
#### 其他权限
|
||
```xml
|
||
<uses-permission android:name="android.permission.CAMERA" />
|
||
<uses-permission android:name="android.permission.VIBRATE" />
|
||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||
```
|
||
|
||
### iOS权限配置
|
||
#### Info.plist配置
|
||
```xml
|
||
<!-- Info.plist -->
|
||
<key>NSCameraUsageDescription</key>
|
||
<string>应用需要使用相机进行拍照功能</string>
|
||
|
||
<key>NSMicrophoneUsageDescription</key>
|
||
<string>应用需要使用麦克风进行语音通话</string>
|
||
|
||
<key>NSLocationWhenInUseUsageDescription</key>
|
||
<string>应用需要获取位置信息提供定位服务</string>
|
||
|
||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||
<string>应用需要获取位置信息提供定位服务</string>
|
||
|
||
<key>NSPhotoLibraryUsageDescription</key>
|
||
<string>应用需要访问相册选择图片</string>
|
||
|
||
<key>NSContactsUsageDescription</key>
|
||
<string>应用需要访问通讯录获取联系人信息</string>
|
||
```
|
||
|
||
#### App Transport Security
|
||
```xml
|
||
<key>NSAppTransportSecurity</key>
|
||
<dict>
|
||
<key>NSAllowsArbitraryLoads</key>
|
||
<true/>
|
||
</dict>
|
||
```
|
||
|
||
### HarmonyOS权限配置
|
||
#### module.json5配置
|
||
```json
|
||
{
|
||
"requestPermissions": [
|
||
{
|
||
"name": "ohos.permission.INTERNET",
|
||
"reason": "网络访问",
|
||
"usedScene": {
|
||
"abilities": ["EntryAbility"],
|
||
"when": "inuse"
|
||
}
|
||
},
|
||
{
|
||
"name": "ohos.permission.LOCATION",
|
||
"reason": "获取位置信息",
|
||
"usedScene": {
|
||
"abilities": ["EntryAbility"],
|
||
"when": "inuse"
|
||
}
|
||
},
|
||
{
|
||
"name": "ohos.permission.CAMERA",
|
||
"reason": "拍照功能",
|
||
"usedScene": {
|
||
"abilities": ["EntryAbility"],
|
||
"when": "inuse"
|
||
}
|
||
},
|
||
{
|
||
"name": "ohos.permission.MICROPHONE",
|
||
"reason": "录音功能",
|
||
"usedScene": {
|
||
"abilities": ["EntryAbility"],
|
||
"when": "inuse"
|
||
}
|
||
},
|
||
{
|
||
"name": "ohos.permission.READ_MEDIA",
|
||
"reason": "读取媒体文件",
|
||
"usedScene": {
|
||
"abilities": ["EntryAbility"],
|
||
"when": "inuse"
|
||
}
|
||
},
|
||
{
|
||
"name": "ohos.permission.WRITE_MEDIA",
|
||
"reason": "写入媒体文件",
|
||
"usedScene": {
|
||
"abilities": ["EntryAbility"],
|
||
"when": "inuse"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 权限动态申请代码示例
|
||
|
||
#### Android权限申请
|
||
```java
|
||
// 检查和申请权限
|
||
private void checkPermissions() {
|
||
String[] permissions = {
|
||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||
Manifest.permission.CAMERA,
|
||
Manifest.permission.RECORD_AUDIO,
|
||
Manifest.permission.ACCESS_FINE_LOCATION
|
||
};
|
||
|
||
List<String> permissionsToRequest = new ArrayList<>();
|
||
for (String permission : permissions) {
|
||
if (ContextCompat.checkSelfPermission(this, permission)
|
||
!= PackageManager.PERMISSION_GRANTED) {
|
||
permissionsToRequest.add(permission);
|
||
}
|
||
}
|
||
|
||
if (!permissionsToRequest.isEmpty()) {
|
||
ActivityCompat.requestPermissions(this,
|
||
permissionsToRequest.toArray(new String[0]),
|
||
REQUEST_PERMISSIONS);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### iOS权限申请
|
||
```objc
|
||
// 请求相机权限
|
||
- (void)requestCameraPermission {
|
||
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
|
||
if (status == AVAuthorizationStatusNotDetermined) {
|
||
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
if (granted) {
|
||
// 权限获取成功
|
||
} else {
|
||
// 权限被拒绝
|
||
}
|
||
});
|
||
}];
|
||
}
|
||
}
|
||
|
||
// 请求定位权限
|
||
- (void)requestLocationPermission {
|
||
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
|
||
[locationManager requestWhenInUseAuthorization];
|
||
}
|
||
```
|
||
|
||
#### HarmonyOS权限申请
|
||
```typescript
|
||
// 申请权限
|
||
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
|
||
|
||
async requestPermissions() {
|
||
const atManager = abilityAccessCtrl.createAtManager();
|
||
const permissions: Permissions[] = [
|
||
'ohos.permission.CAMERA',
|
||
'ohos.permission.MICROPHONE',
|
||
'ohos.permission.LOCATION'
|
||
];
|
||
|
||
try {
|
||
await atManager.requestPermissionsFromUser(this.context, permissions);
|
||
console.log('权限申请成功');
|
||
} catch (error) {
|
||
console.log('权限申请失败:', error);
|
||
}
|
||
}
|
||
```
|
||
|
||
## 安全配置
|
||
|
||
### 签名配置
|
||
- **Keystore**: gamehall.keystore
|
||
- **密码**: tswl2015
|
||
- **别名**: gamehall
|
||
- **签名版本**: v1 + v2
|
||
|
||
### 网络安全
|
||
- `usesCleartextTraffic="true"` - 允许HTTP流量
|
||
- 支持HTTPS和HTTP混合请求
|
||
|
||
### 文件权限
|
||
- 使用FileProvider共享文件
|
||
- 适配Android 7.0+文件访问限制
|
||
|
||
## 跨平台构建和发布
|
||
|
||
### Android平台构建
|
||
#### 环境要求
|
||
- **JDK**: OpenJDK 11+
|
||
- **Android Studio**: 2022.1.1+
|
||
- **Gradle**: 7.0+
|
||
- **Android SDK**: API 33
|
||
|
||
#### 构建命令
|
||
```bash
|
||
# 调试版本
|
||
./gradlew assembleDebug
|
||
|
||
# 发布版本
|
||
./gradlew assembleRelease
|
||
|
||
# 清理项目
|
||
./gradlew clean
|
||
|
||
# 生成签名APK
|
||
./gradlew assembleRelease -Pandroid.injected.signing.store.file=keystore/gamehall.keystore
|
||
```
|
||
|
||
#### 签名配置
|
||
```gradle
|
||
// app/build.gradle
|
||
android {
|
||
signingConfigs {
|
||
release {
|
||
storeFile file('../keystore/gamehall.keystore')
|
||
storePassword 'tswl2015'
|
||
keyAlias 'gamehall'
|
||
keyPassword 'tswl2015'
|
||
}
|
||
}
|
||
|
||
buildTypes {
|
||
release {
|
||
signingConfig signingConfigs.release
|
||
minifyEnabled false
|
||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### iOS平台构建
|
||
#### 环境要求
|
||
- **macOS**: 12.0+
|
||
- **Xcode**: 14.0+
|
||
- **iOS SDK**: 16.0+
|
||
- **CocoaPods**: 1.11+
|
||
|
||
#### 构建步骤
|
||
```bash
|
||
# 安装依赖
|
||
cd ios
|
||
pod install
|
||
|
||
# 清理项目
|
||
xcodebuild clean -workspace TSGame.xcworkspace -scheme TSGame
|
||
|
||
# 构建Debug版本
|
||
xcodebuild build -workspace TSGame.xcworkspace -scheme TSGame -configuration Debug
|
||
|
||
# 构建Release版本
|
||
xcodebuild archive -workspace TSGame.xcworkspace -scheme TSGame -configuration Release -archivePath build/TSGame.xcarchive
|
||
|
||
# 导出IPA
|
||
xcodebuild -exportArchive -archivePath build/TSGame.xcarchive -exportPath build/ipa -exportOptionsPlist ExportOptions.plist
|
||
```
|
||
|
||
#### 证书配置
|
||
```xml
|
||
<!-- ExportOptions.plist -->
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>method</key>
|
||
<string>app-store</string>
|
||
<key>teamID</key>
|
||
<string>YOUR_TEAM_ID</string>
|
||
<key>provisioningProfiles</key>
|
||
<dict>
|
||
<key>com.jx.jyhd</key>
|
||
<string>YOUR_PROVISIONING_PROFILE</string>
|
||
</dict>
|
||
</dict>
|
||
</plist>
|
||
```
|
||
|
||
### HarmonyOS平台构建
|
||
#### 环境要求
|
||
- **DevEco Studio**: 4.0+
|
||
- **HarmonyOS SDK**: API 9
|
||
- **Node.js**: 16.0+
|
||
- **npm**: 8.0+
|
||
|
||
#### 构建步骤
|
||
```bash
|
||
# 清理项目
|
||
hvigorw clean
|
||
|
||
# 构建Debug版本
|
||
hvigorw assembleHap --mode module -p module=entry@default -p debuggable=true
|
||
|
||
# 构建Release版本
|
||
hvigorw assembleHap --mode module -p module=entry@default -p debuggable=false
|
||
|
||
# 构建APP包
|
||
hvigorw assembleApp --mode module -p module=entry@default
|
||
```
|
||
|
||
#### 签名配置
|
||
```json5
|
||
// build-profile.json5
|
||
{
|
||
"app": {
|
||
"signingConfigs": [
|
||
{
|
||
"name": "default",
|
||
"type": "HarmonyOS",
|
||
"material": {
|
||
"certpath": "path/to/certificate.p12",
|
||
"storePassword": "your_password",
|
||
"keyAlias": "your_key_alias",
|
||
"keyPassword": "your_key_password",
|
||
"profile": "path/to/profile.p7b",
|
||
"signAlg": "SHA256withECDSA",
|
||
"verify": true,
|
||
"compatibleVersion": 9
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
### 自动化构建配置
|
||
|
||
#### CI/CD流水线 (GitHub Actions)
|
||
```yaml
|
||
# .github/workflows/build.yml
|
||
name: Multi-Platform Build
|
||
|
||
on:
|
||
push:
|
||
branches: [ main, develop ]
|
||
pull_request:
|
||
branches: [ main ]
|
||
|
||
jobs:
|
||
build-android:
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v3
|
||
- name: Set up JDK 11
|
||
uses: actions/setup-java@v3
|
||
with:
|
||
java-version: '11'
|
||
distribution: 'adopt'
|
||
- name: Build Android APK
|
||
run: |
|
||
cd android
|
||
./gradlew assembleRelease
|
||
- name: Upload APK
|
||
uses: actions/upload-artifact@v3
|
||
with:
|
||
name: android-apk
|
||
path: android/app/build/outputs/apk/release/
|
||
|
||
build-ios:
|
||
runs-on: macos-latest
|
||
steps:
|
||
- uses: actions/checkout@v3
|
||
- name: Set up Xcode
|
||
uses: maxim-lobanov/setup-xcode@v1
|
||
with:
|
||
xcode-version: '14.0'
|
||
- name: Install CocoaPods
|
||
run: |
|
||
cd ios
|
||
pod install
|
||
- name: Build iOS
|
||
run: |
|
||
cd ios
|
||
xcodebuild build -workspace TSGame.xcworkspace -scheme TSGame -configuration Release
|
||
|
||
build-harmonyos:
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v3
|
||
- name: Set up Node.js
|
||
uses: actions/setup-node@v3
|
||
with:
|
||
node-version: '16'
|
||
- name: Build HarmonyOS
|
||
run: |
|
||
cd harmonyos
|
||
npm install
|
||
hvigorw assembleHap --mode module -p module=entry@default
|
||
```
|
||
|
||
### 发布配置
|
||
|
||
#### Android发布
|
||
- **Google Play Store**: 上传AAB包,配置应用签名
|
||
- **国内应用市场**: 上传APK包到各大应用商店
|
||
- **企业分发**: 配置内部分发渠道
|
||
|
||
#### iOS发布
|
||
- **App Store**: 通过App Store Connect上传IPA
|
||
- **TestFlight**: 内测版本分发
|
||
- **企业证书**: 企业内部分发
|
||
|
||
#### HarmonyOS发布
|
||
- **华为应用市场**: 上传HAP/APP包
|
||
- **企业分发**: 通过华为开发者平台配置
|
||
|
||
## 跨平台开发指南
|
||
|
||
### 平台适配策略
|
||
#### 1. 功能对等原则
|
||
确保各平台实现相同的核心功能,但可根据平台特性进行优化:
|
||
- **必须功能**: 游戏核心玩法、登录认证、基础分享
|
||
- **平台特色**: 分享渠道、推送通知
|
||
- **性能优化**: 根据平台特性调整渲染和网络策略
|
||
|
||
#### 2. 统一接口设计
|
||
所有平台实现相同的JS Bridge API接口:
|
||
```typescript
|
||
// bridge-api.d.ts - 统一接口定义
|
||
interface TSGameBridge {
|
||
// 用户认证
|
||
accreditLogin(type: string): Promise<void>;
|
||
|
||
// 设备信息
|
||
getPhoneInfo(): Promise<DeviceInfo>;
|
||
getNetworkStatus(): Promise<NetworkStatus>;
|
||
|
||
// 多媒体
|
||
openCamera(): Promise<string>;
|
||
playAudio(config: AudioConfig): Promise<void>;
|
||
|
||
// 系统交互
|
||
vibrate(duration: number): Promise<void>;
|
||
showNotification(message: string): Promise<void>;
|
||
|
||
// 平台特定功能(可选)
|
||
platformSpecific?: {
|
||
[key: string]: any;
|
||
};
|
||
}
|
||
```
|
||
|
||
#### 3. 平台差异处理
|
||
```javascript
|
||
// platform-detector.js
|
||
const Platform = {
|
||
isAndroid: () => /Android/i.test(navigator.userAgent),
|
||
isIOS: () => /iPhone|iPad|iPod/i.test(navigator.userAgent),
|
||
isHarmonyOS: () => /HarmonyOS/i.test(navigator.userAgent),
|
||
|
||
// 根据平台调用不同的API
|
||
shareToSocial: (data) => {
|
||
if (Platform.isAndroid()) {
|
||
return TSGameBridge.shareToWechat(data);
|
||
} else if (Platform.isIOS()) {
|
||
return TSGameBridge.shareToSystem(data);
|
||
} else if (Platform.isHarmonyOS()) {
|
||
return TSGameBridge.shareToHuawei(data);
|
||
}
|
||
}
|
||
};
|
||
```
|
||
|
||
### 开发环境搭建
|
||
|
||
#### 1. Android开发环境
|
||
```bash
|
||
# 安装Android Studio
|
||
# 配置环境变量
|
||
export ANDROID_HOME=/path/to/android-sdk
|
||
export PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools
|
||
|
||
# 创建项目
|
||
cd android
|
||
./gradlew build
|
||
```
|
||
|
||
#### 2. iOS开发环境
|
||
```bash
|
||
# 安装Xcode (仅限macOS)
|
||
# 安装CocoaPods
|
||
sudo gem install cocoapods
|
||
|
||
# 创建项目
|
||
cd ios
|
||
pod init
|
||
pod install
|
||
open TSGame.xcworkspace
|
||
```
|
||
|
||
#### 3. HarmonyOS开发环境
|
||
```bash
|
||
# 下载DevEco Studio
|
||
# 安装HarmonyOS SDK
|
||
# 创建项目
|
||
cd harmonyos
|
||
hvigorw assembleHap
|
||
```
|
||
|
||
### 代码复用策略
|
||
|
||
#### 1. 共享Web资源
|
||
```javascript
|
||
// shared/webview/js/game-core.js
|
||
class GameCore {
|
||
constructor() {
|
||
this.platform = this.detectPlatform();
|
||
this.bridge = this.initBridge();
|
||
}
|
||
|
||
detectPlatform() {
|
||
if (/Android/i.test(navigator.userAgent)) return 'android';
|
||
if (/iPhone|iPad/i.test(navigator.userAgent)) return 'ios';
|
||
if (/HarmonyOS/i.test(navigator.userAgent)) return 'harmonyos';
|
||
return 'unknown';
|
||
}
|
||
|
||
initBridge() {
|
||
// 统一的桥接初始化逻辑
|
||
return new PlatformBridge(this.platform);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. 平台抽象层
|
||
```java
|
||
// Android抽象层
|
||
public abstract class PlatformService {
|
||
public abstract void login(String type, Callback callback);
|
||
public abstract void share(ShareData data, Callback callback);
|
||
public abstract DeviceInfo getDeviceInfo();
|
||
}
|
||
|
||
public class AndroidPlatformService extends PlatformService {
|
||
@Override
|
||
public void login(String type, Callback callback) {
|
||
// Android特定的登录实现
|
||
}
|
||
}
|
||
```
|
||
|
||
```objc
|
||
// iOS抽象层
|
||
@protocol TSPlatformService <NSObject>
|
||
- (void)loginWithType:(NSString *)type callback:(void(^)(NSDictionary *result))callback;
|
||
- (void)shareData:(NSDictionary *)data callback:(void(^)(BOOL success))callback;
|
||
- (NSDictionary *)getDeviceInfo;
|
||
@end
|
||
|
||
@interface TSIOSPlatformService : NSObject <TSPlatformService>
|
||
@end
|
||
```
|
||
|
||
```typescript
|
||
// HarmonyOS抽象层
|
||
abstract class PlatformService {
|
||
abstract login(type: string): Promise<LoginResult>;
|
||
abstract share(data: ShareData): Promise<boolean>;
|
||
abstract getDeviceInfo(): DeviceInfo;
|
||
}
|
||
|
||
class HarmonyOSPlatformService extends PlatformService {
|
||
async login(type: string): Promise<LoginResult> {
|
||
// HarmonyOS特定的登录实现
|
||
}
|
||
}
|
||
```
|
||
|
||
### 测试策略
|
||
|
||
#### 1. 单元测试
|
||
```javascript
|
||
// 跨平台测试用例
|
||
describe('Bridge API Tests', () => {
|
||
test('should get device info on all platforms', async () => {
|
||
const deviceInfo = await TSGameBridge.getPhoneInfo();
|
||
expect(deviceInfo).toHaveProperty('model');
|
||
expect(deviceInfo).toHaveProperty('version');
|
||
expect(deviceInfo).toHaveProperty('platform');
|
||
});
|
||
|
||
test('should handle network status correctly', async () => {
|
||
const network = await TSGameBridge.getNetworkStatus();
|
||
expect(['1', '2', '3']).toContain(network);
|
||
});
|
||
});
|
||
```
|
||
|
||
#### 2. 集成测试
|
||
```yaml
|
||
# test-plan.yml
|
||
test_scenarios:
|
||
- name: "登录流程测试"
|
||
platforms: ["android", "ios", "harmonyos"]
|
||
steps:
|
||
- "打开应用"
|
||
- "点击登录按钮"
|
||
- "验证登录结果"
|
||
|
||
- name: "分享功能测试"
|
||
platforms: ["android", "ios", "harmonyos"]
|
||
steps:
|
||
- "进入游戏页面"
|
||
- "点击分享按钮"
|
||
- "选择分享平台"
|
||
- "验证分享成功"
|
||
```
|
||
|
||
### 性能优化建议
|
||
|
||
#### 1. WebView优化
|
||
```javascript
|
||
// 通用WebView优化配置
|
||
const webViewConfig = {
|
||
android: {
|
||
hardwareAccelerated: true,
|
||
cacheMode: 'LOAD_CACHE_ELSE_NETWORK',
|
||
domStorageEnabled: true
|
||
},
|
||
ios: {
|
||
allowsInlineMediaPlayback: true,
|
||
mediaTypesRequiringUserActionForPlayback: 'none',
|
||
scrollView: { bounces: false }
|
||
},
|
||
harmonyos: {
|
||
javaScriptAccess: true,
|
||
domStorageAccess: true,
|
||
imageAccess: true
|
||
}
|
||
};
|
||
```
|
||
|
||
#### 2. 资源加载优化
|
||
```javascript
|
||
// 预加载关键资源
|
||
class ResourceManager {
|
||
preloadAssets() {
|
||
const criticalAssets = [
|
||
'images/logo.png',
|
||
'audio/bgm.mp3',
|
||
'fonts/game-font.woff2'
|
||
];
|
||
|
||
return Promise.all(
|
||
criticalAssets.map(asset => this.loadAsset(asset))
|
||
);
|
||
}
|
||
|
||
loadAsset(url) {
|
||
return new Promise((resolve, reject) => {
|
||
const ext = url.split('.').pop();
|
||
if (['png', 'jpg', 'gif'].includes(ext)) {
|
||
const img = new Image();
|
||
img.onload = resolve;
|
||
img.onerror = reject;
|
||
img.src = url;
|
||
} else if (['mp3', 'wav'].includes(ext)) {
|
||
const audio = new Audio();
|
||
audio.oncanplaythrough = resolve;
|
||
audio.onerror = reject;
|
||
audio.src = url;
|
||
}
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
### 维护建议
|
||
|
||
#### 1. 版本管理
|
||
```json
|
||
// version-config.json
|
||
{
|
||
"version": "3.6.3",
|
||
"platforms": {
|
||
"android": {
|
||
"versionCode": 36300,
|
||
"minSdkVersion": 21,
|
||
"targetSdkVersion": 33
|
||
},
|
||
"ios": {
|
||
"buildNumber": "36300",
|
||
"minimumOSVersion": "11.0"
|
||
},
|
||
"harmonyos": {
|
||
"versionCode": 36300,
|
||
"minAPIVersion": 8,
|
||
"targetAPIVersion": 9
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. 更新策略
|
||
- **强制更新**: 影响安全性和关键功能的更新
|
||
- **建议更新**: 新功能和体验优化
|
||
- **静默更新**: 资源文件和配置更新
|
||
- **灰度发布**: 分阶段推送给不同用户群体
|
||
|
||
#### 3. 兼容性处理
|
||
```javascript
|
||
// 版本兼容性检查
|
||
class CompatibilityChecker {
|
||
checkPlatformSupport() {
|
||
const requirements = {
|
||
android: { minVersion: 21 },
|
||
ios: { minVersion: '11.0' },
|
||
harmonyos: { minAPIVersion: 8 }
|
||
};
|
||
|
||
const current = this.getCurrentPlatformVersion();
|
||
const required = requirements[this.platform];
|
||
|
||
return this.compareVersions(current, required.minVersion) >= 0;
|
||
}
|
||
}
|
||
```
|
||
|
||
## 项目总结与展望
|
||
|
||
### 技术特色
|
||
1. **跨平台统一**: 基于WebView + JS Bridge的跨平台解决方案
|
||
2. **功能完备**: 涵盖认证、分享、定位、音视频等完整功能
|
||
3. **性能优化**: 针对不同平台特性进行深度优化
|
||
4. **易于维护**: 统一的API接口和代码架构
|
||
|
||
### 适用场景
|
||
- **棋牌游戏**: 完整的棋牌游戏解决方案
|
||
- **社交游戏**: 支持实时音视频和社交分享
|
||
- **地方特色应用**: 可快速适配不同地区需求
|
||
- **跨平台应用**: 一套代码适配多个平台
|
||
|
||
### 开发优势
|
||
- **开发效率**: H5游戏 + 原生能力,快速开发
|
||
- **维护成本**: 统一的业务逻辑,降低维护成本
|
||
- **用户体验**: 原生性能 + Web灵活性
|
||
- **迭代速度**: 支持热更新,快速迭代
|
||
|
||
### 扩展方向
|
||
1. **平台支持**: 扩展到更多平台(Windows、Web)
|
||
2. **技术升级**: 升级到最新的SDK版本和技术栈
|
||
3. **功能增强**: 添加更多游戏类型和社交功能
|
||
4. **性能优化**: 进一步优化启动速度和运行性能
|
||
|
||
### 常见问题解答
|
||
|
||
#### Q: 如何在现有项目基础上开发iOS版本?
|
||
A:
|
||
1. 复制`shared/`目录下的所有Web资源
|
||
2. 创建iOS项目,实现相同的JS Bridge接口
|
||
3. 根据iOS平台特性调整权限配置和第三方SDK
|
||
4. 参考本文档的iOS开发指南进行适配
|
||
|
||
#### Q: HarmonyOS版本开发有什么注意事项?
|
||
A:
|
||
1. 使用ArkTS语言开发,语法类似TypeScript
|
||
2. 权限管理更加严格,需要详细配置使用场景
|
||
3. 部分第三方SDK可能需要寻找替代方案
|
||
4. 遵循华为应用市场的审核规范
|
||
|
||
#### Q: 如何保证各平台功能一致性?
|
||
A:
|
||
1. 使用统一的API接口定义
|
||
2. 建立跨平台测试用例
|
||
3. 定期进行功能对比测试
|
||
4. 维护详细的功能兼容性文档
|
||
|
||
#### Q: 性能优化的重点是什么?
|
||
A:
|
||
1. WebView初始化和资源加载优化
|
||
2. JS Bridge通信效率优化
|
||
3. 内存管理和垃圾回收优化
|
||
4. 网络请求和数据缓存优化
|
||
|
||
### 技术支持
|
||
- **文档更新**: 定期更新技术文档和API说明
|
||
- **问题反馈**: 通过Issue跟踪和解决技术问题
|
||
- **技术交流**: 建立开发者交流群组
|
||
- **版本发布**: 提供稳定的版本发布和更新通知
|
||
|
||
---
|
||
|
||
**文档版本**: v2.0
|
||
**更新时间**: 2025年7月5日
|
||
**适用版本**: TSGame 3.6.3+
|
||
**支持平台**: Android、iOS、HarmonyOS
|
||
|
||
> 本文档为TSGame跨平台项目的完整技术说明,涵盖了项目架构、开发指南、API参考等所有必要信息。开发者可以基于本文档快速理解项目结构,并在任意支持的平台上实现相同功能的应用。
|