需求
最近开发一个项目,需要实现声网的接入。由于采用uniapp模式,按照最佳实践采用优先开发H5再适配的模式。因此实现了H5和Android、IOS三模式的接入,Android和IOS里采用离线打包自定义基座来进行调试。怕自己忘记了,在这里详细的记录完整的核心整合逻辑
准备
开始前,准备了以下组件:
unibest 菲鸽开源的uniapp模板,对于安卓代码,采用的是中间编译模式。因此APP模式下已经是混淆压缩过的代码了,即使采用uniapp云打包上传代码,也是相对的安全。当然对于咱的开发来说可以更抠门一点,连云打包都不用了,直接离线编译基座🤪(项目没上线,银子有限而已)
JDK 万年不变的1.8版本,主要是因为下来的参考代码也都是jdk1.8编译级别的
AndroidStudio 版本是2024.1.2,SDK下载了30和28的版本(为什么是28?因为目前各大市场都要求最低最少兼容安卓9.0)
HBuilder安卓示例 这个是HBuilder的安卓示例项目。里面包含了AS插件项目以及AS整合项目。你需要以这个为基础项目来参考修改。【福利】百度网盘太慢,这里我自己收录了一份
Agora-RTC_3.7.0_example AgoraRTC SDK使用的示例项目,由于unibest使用的是Typescript,因此需要这个项目里包含的typescript模式的sdk使用代码
Agora-RTC_3.7.2 AgoraRTC的uniapp插件包,可以从DCloud市场下载,里面包含了安卓插件编译aar文件
接入步骤
H5接入
H5接入的前提假设你使用unibest模板建立了一个基础项目。
1.添加npm组件
首先是给项目添加npm组件
pnpm i agora-rtc-sdk-ng
2.导入组件
在页面的script头部申明导入
import AgoraRTC, { IAgoraRTCClient } from 'agora-rtc-sdk-ng'
3.定义控制对象
对于H5,我们只需要两个组件变量即可,一个是RTC客户端,一个是用于控制本地音频采集的本地音轨
let client: IAgoraRTCClient, localAudioTrack
4.初始化引擎
client = AgoraRTC.createClient({ mode: 'rtc', codec: 'h264' })
5.订阅音轨
这样可以收听到其它用户声音,注意时间额度用光后,这里是进不来的
client.on('user-published', async (user, mediaType) => {
// 声网IP/ID每日免费额度到了后这里进不来(大坑)
console.log('==== start subscribe audio track ====')
// 开始订阅远端用户。
await client.subscribe(user, mediaType)
console.log('subscribe success')
// 表示本次订阅的是音频。
if (mediaType === 'audio') {
// 订阅完成后,从 `user` 中获取远端音频轨道对象。
const remoteAudioTrack = user.audioTrack
// 播放音频因为不会有画面,不需要提供 DOM 元素的信息。
remoteAudioTrack.play()
}
})
6.加入Channel
这里由于我们后端采用的是雪花分片算法,属于64位长整型,因此不能直接采用number传入,这里有坑。API备注写的很清楚,如果要长整型的用户ID,请使用string传入
生成token其它文章官网或其它的文章很多,我就不啰嗦了,包括生成临时token或者channel专用token。不懂生成token的先搜索下其他文章。appId是你在声网建立的应用ID。
const uid = await client.join(
groupVoiceConf.appId,
groupId.toString(),
groupVoiceConf.token,
currentUserId.toString()
)
7.发布音频
发布和停止发布音频即上麦和下麦功能。
上麦代码:
try {
if (client) {
localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack()
await client.publish(localAudioTrack) // 发布音频
}
} catch (e) {
console.log(e)
}
下麦代码:
try {
if (localAudioTrack) {
await client.unpublish(localAudioTrack)
localAudioTrack.stop()
localAudioTrack.close()
}
} catch (e) {
console.log(e)
}
8.离开频道
离开频道后不会收听以及发言,代码如下:
if (client) await client.leave()
9.HTML引擎不支持怎么办
对于某些精简的浏览器以及小米浏览器,存在无法兼容AgoraRTC代码的情况。是否可以根据支持情况提前判断呢?其实AgoraRTC提供了检测API checkSystemRequirements,只需要先检测,然后再执行RTC的代码即可,格式看上去这样的:
const result = AgoraRTC.checkSystemRequirements()
if (result) {
try {
// your RTC code
} catch (e) {
console.log(e)
}
} else {
utils.alert('您的浏览器不支持交互语音')
}
注意RTC引擎都是Promise结构,因此都需要await调用和try...catch接口来判断调用失败
另外请在https环境下执行代码(搜一下plugin-basic-ssl把这个接入到vite去),直接在http环境下功能是受限制的。
这个检测目前已知的至少两个情况适用:
A)小米浏览器不支持RTC音频,能够被检测出不支持
B)HTTPS证书过期,也能够检测出不被支持
10.其它要求
另外按照官方的要求,自己生成token后,是需要定时刷新的,这部分代码页面使用一个定时器刷新即可,具体代码请自己琢磨前后端代码。
安卓接入
由于unibest开发模式推荐VSCode不是基于HBuilder,这里会有取舍:
坏处:不能像直接使用HBuilder那样在Manifest提供多个可视化选项来进行插件云打包。
好处:如果不用打自定义基座,云打包上传的代码都是压缩编译过的,相对来说比较安全。
虽然无法直接使用云打包的插件,但是我们可以使用离线打包,来弥补这个不足。
注意后面如果不特别说明,项目都是指AndroidStudio项目。
1.导入AS
我们解开Android-SDK@4.28.82186_20240923.zip,将里面的HBuilder-Integrate-AS目录释放到一个文件夹,将这个项目导入到AndroidStudio,开始配置。
2.AS配置:证书
按照官网的流程,生成自己的证书,并上传到DCloud后台(这里不是HBuilder,要登录DCloud开发者平台)。详细的自己查,这里只记录几个简要的流程:
生成证书:
set PATH=C:/JDK11/bin
keytool -genkey -alias <aliasName> -keyalg RSA -keysize 2048 -validity 36500 -keystore simpleDemo/<aliasName>.keystore
aliasName以及密码等根据自己实际情况设置。注意后面上传到DCloud后台要用这些信息的。
查看填写到DCloud后台的几个信息
set PATH=C:/JDK11/bin
keytool -list -v -keystore simpleDemo/<aliasName>.keystore
pause
查看时要输入前面genkey时的密码
然后要把证书的几个信息都配置到simpleDemo/build.gradle的signingConfigs
signingConfigs {
config {
keyAlias '<aliasName>'
keyPassword '<yourPass>'
storeFile file('<aliasName>.keystore')
storePassword '<yourStorePass>'
v1SigningEnabled true
v2SigningEnabled true
}
}
建议前面的keypass和storepass设置成一致,免得密码太多了操作蛋疼
这里有坑,注意如果上传DCloud后台时包名发生了变更,也一定要在AndroidManifest.xml里修改包名,不然有签名设置不正确报错
3.AS配置:权限
打开项目的manifests/AndroidManifest.xml,在<manifest xmlns:android>这个节点下加入权限申明(本项目只使用音频):
<!-- 声音 -->
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
4.AS配置:开启调试
修改项目的assets/data/dcloud_control.xml,将根节点修改为以下格式:
<hbuilder debug="true" syncDebug="true">
这个模式表示是打包自定义调试基座,如果要做正式发布时,要把debug和syncDebug去掉
另外按照uniapp整合规范,需要把appid修改为你自己项目的appid值
5.AS配置:相关的gradle组件包
打开项目根目录下的simpleDemo/build.gradle,在dependencies节点下添加4个组件
implementation 'io.agora.rtc:agora-special-full:3.7.2.4'
implementation 'io.agora.rtc:full-screen-sharing:3.7.2.4'
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.3.50'
//自定义基座需要的
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
okhttp如果不导入的话,当做自定义基座就会无法将调试信息发送到HBuilder,如果你用adb的话就无所谓。建议始终带上。
另外在Agora-Uniapp-SDK.zip里找到uniplugin_agora_rtc-release.aar,把这个放入项目simpleDemo\libs下。如果要打正式release包,要把这个组件去掉,不用删除,直接写在exclude里好了:
implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: ['debug-server-release.aar'])
然后记得点AS菜单File->Sync Project With Gradle Files
6.AS配置:配置自定义插件
在项目simpleDemo\assets下建立文件dcloud_uniplugins.json,内容如下:
{
"nativePlugins": [
{
"plugins": [
{
"type": "module",
"name": "Agora-RTC-EngineModule",
"class": "io.agora.rtc.uni.AgoraRtcEngineModule"
},
{
"type": "module",
"name": "Agora-RTC-ChannelModule",
"class": "io.agora.rtc.uni.AgoraRtcChannelModule"
},
{
"type": "component",
"name": "Agora-RTC-SurfaceView",
"class": "io.agora.rtc.uni.AgoraRtcSurfaceView"
},
{
"type": "component",
"name": "Agora-RTC-TextureView",
"class": "io.agora.rtc.uni.AgoraRtcTextureView"
}
]
}
]
}
7.链接你的编译结果
在你的unibest项目下执行命令
pnpm build:app-android --mode production
然后把项目dist\build\app拷贝到项目的simpleDemo/assets/apps/<你项目的UNIAPP_ID>/www下。
如果你嫌每次拷贝麻烦,直接建立一个软链接过去就好(WINDOWS下不会?搜一下mklink怎么用)
最后一步,注意了,我就是在这里没操作正确导致卡了2天。
删除项目下的simpleDemo/build目录,然后
A)出基座,使用Build -> Build App Bundle(s)/APK(s)
B)出release,使用Build -> Generate Singed App Bundle /APK
如果你的操作正确,后面接入代码后HBuilder启动时就不会出现下面报错:
[JS Framework] 当前运行的基座不包含原生插件[Agora-RTC-ChannelModule],请在manifest中配置该插件,重新制作包括该原生插件的自定义运行基座
[JS Framework] 当前运行的基座不包含原生插件[Agora-RTC-EngineModule],请在manifest中配置该插件,重新制作包括该原生插件的自定义运行基座
8.接入TypeScript组件
下一步要接入代码了,但是unibest是Typescript代码,Agora-RTC_3.7.0_example里的就不顶用了。实际上,在官网github里有一份typescript代码,只是没放到发布包里。从github里把整个项目下下来,找到src/components,把整个目录放到unibest项目的src/components下。在VSCode里看上去的文件是这样:
9.引入组件
然后就可以在语音界面开始调用了,script里引入组件
import RtcEngine, { RtcChannel } from '../../components/Agora-RTC-JS/index'
import { ClientRole, ChannelProfile } from '../../components/Agora-RTC-JS/common/Enums'
10.定义rtcEngine
APP这里定义变量简单点,只需要定义好rtcEngine即可
let rtcEngine: RtcEngine
11.初始化引擎
申请录音权限是自己的框架代码,您可以自己封装一下。然后就是收听其它人员,加入频道等。这里就不一一解释了,加入频道支持雪花ID需要使用joinChannelWithUserAccount。代码在下面
if (uni.getSystemInfoSync().platform === 'android') {
// 华为市场规范,先申请权限
await requestPermission('RECORD', '用于听美丽的歌声啊啊啊啊')
}
rtcEngine = await RtcEngine.create(groupVoiceConf.appId) // 创建引擎
await rtcEngine.enableAudio() // 收听其它伙伴的发言
await rtcEngine.setChannelProfile(ChannelProfile.LiveBroadcasting)
await rtcEngine.setClientRole(ClientRole.Broadcaster)
await rtcEngine.joinChannelWithUserAccount(
groupVoiceConf.token,
groupId.toString(),
currentUserId.toString()
)
await rtcEngine.enableLocalAudio(false) // 默认进入频道麦克风是开启的,需要关闭
12.上/下麦
代码如下
await rtcEngine.enableLocalAudio(true) // 上麦
await rtcEngine.enableLocalAudio(false) // 下麦
12.离开频道
if (rtcEngine) await rtcEngine.leaveChannel()
IOS接入
--------------------------------------[ 2024-10-19更新 ]---------------------------------------------
今天开始继续踩个大坑:IOS自定义离线基座接入模式。为了实现最新的xcode编译,把12年前的MAC机器系统连跳3级升级到Sonoma了,对应的Xcode也升级到了16.0,开始搞事情。
本人是IOS开发新手级别,常用的环境也是WINDOWS,主要通过SSH控制开发机,因此记录的有一些差异。
1.准备环境
最新的uniapp SDK要求安装 Xcode 15 及以上版本,因此就有了上面的升级。折腾系统升级和下载模拟器弄了数小时。
2.准备项目
IOS开发需要准备三个项目
1)下载HBuilder的离线SDK SDK 升级说明 | uni小程序SDK
2)下载声网的Agora-RTC_3.7.2,里面包含IOS的framework组件
3)Agora-Uniapp-SDK的源码项目,这里有个需要的项目要拉下来。如果前面有接入安卓,应该有这个项目,否则git clone GitHub - AgoraIO-Community/Agora-Uniapp-SDK
3.整合代码
解压HBuilder的离线SDK,将其中的HBuilder-uniPluginDemo项目展开。导入XOCDE,然后在项目上右键Add Files to "HBuilder-uniPlugin"
选择源码目录里Agora-Uniapp-SDK//ios/AgoraRtcUniPlugin.xcodeproj进行导入。注意要整个源码目录展开,不要单独只有AgoraRtcUniPlugin.xcodeproj目录,至少从ios目录下完整展开。
导入后,首先要修改一下Target级别,因为原来的是9.0,编译会导致和SDK项目的级别不匹配,选择项目AgoraRtcUniPlugin.xcodeproj->Build Settings -> iOS Deployment Target,修改为12.0以上。
然后把插件项目里DCTestUniPlugin的Targets下的也要改一下,那个也是9.0级别,改成12.0
在 Xcode 中点击 HBuilder-uniPlugin 工程,并点击 HBuilder Target,选中 Build Phases
- 在 Dependencies 中添加 AgoraRtcUniPlugin
- 在 Link Binary With Libraries 中添加 AgoraRtcUniPlugin.framework
- 在 Embed Frameworks 中添加 AgoraCore.framework AgoraRtcKit.framework Agorafdkaac.framework Agoraffmpeg.framework AgoraSoundTouch.framework (需要通过 Add Other... 选择 ios/libs 目录中的 .framework 文件添加)
第3个添加的framwork要选择Agora-RTC_3.7.2里展开的组件目录。我是为了方便管理直接把展开的几个目录拷贝到IOS项目SDK下了。导入这5个组件
• 在 HBuilder-Hello/HBuilder-uniPlugin-Info.plist 中添加(SDK项目有几个HBuilder-Hello,注意位置是在SDK/HBuilder-uniPluginDemo/HBuilder-Hello/HBuilder-uniPlugin-Info.plist)
...
<key>dcloud_uniplugins</key>
<array>
<dict>
<key>plugins</key>
<array>
<dict>
<key>class</key>
<string>AgoraRtcEngineModule</string>
<key>name</key>
<string>Agora-RTC-EngineModule</string>
<key>type</key>
<string>module</string>
</dict>
<dict>
<key>class</key>
<string>AgoraRtcChannelModule</string>
<key>name</key>
<string>Agora-RTC-ChannelModule</string>
<key>type</key>
<string>module</string>
</dict>
<dict>
<key>class</key>
<string>AgoraRtcSurfaceView</string>
<key>name</key>
<string>Agora-RTC-SurfaceView</string>
<key>type</key>
<string>component</string>
</dict>
<dict>
<key>class</key>
<string>AgoraRtcTextureView</string>
<key>name</key>
<string>Agora-RTC-TextureView</string>
<key>type</key>
<string>component</string>
</dict>
</array>
</dict>
...
</array>
...
我是把<key>dcloud_uniplugins</key>原来示例那个几个组件去掉了,直接替换为上面的。
然后把dcloud_app_key也配置一下,是一个32位串,需要在dcloud官网申请,注意dcloud的appkey是区分终端的,开发和测试版本、IOS和安卓都要单独申请。
<key>dcloud_appkey</key>
<string>去DCloud官方申请获取对应的值</string>
<key>dcloud_uninview_background</key>
再进入HBuilder-uniPluginDemo/HBuilder-Hello/项目,修改control.xml,看到
<?xml version="1.0" encoding="utf-8"?>
<HBuilder debug="true" syncDebug="true" version="1.9.9.81459">
<!--应用节点-->
<apps>
<app appid="__UNI__33C5XXX" appver="1.0.1"/>
</apps>
</HBuilder>
把appid的__UNI__33C5XXX修改为你的APPID
另外在plist里添加Application supports iTunes file sharing为YES:
最后一步,这里有坑(IDE模式就没问题,命令行会导致编译不过)
修改AgoraRtcUniPlugin的配置,把头文件指向你的SDK的路径,原来用的是相对位置。由于我挪动了,不匹配原来的结构,因此必须修改,否则下一步xcodebuild会报:
error: 'WeexSDK.h' file not found
打开ArgoraSDK项目文件AgoraRtcUniPlugin.xcodeproj/project.pbxproj。找到行
HEADER_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/../../SDK/inc\"/**",
);
修改为指向你的SDK的inc路径,
<yourpath>/SDK/inc
然后下一步把资源替换过去,资源在HBuilder-uniPluginDemo/HBuilder-Hello/Pandora/apps/目录,将你编译的目录整个复制过去(注意不要把unpackage也复制了,那个是基座app目录)
4.编译
按照上面的配置,项目应该可以跑起来了。由于我使用SSH操作,我采用直接命令行的方式忽略证书试一下:
cd HBuilder-uniPluginDemo
xcodebuild -scheme HBuilder -configuration Release -derivedDataPath build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO
编译成功会看到这个结果
然后看下编译出来的文件,在
build/Build/Products/Release-iphoneos/UniPluginDemo.app/下
UniPluginDemo那个就是
5.复制与打包
下一步要将编译的结果进行打包。需要做的事情是压缩与签名,虽然XCode界面是可以操作。像我们这种“懒人”肯定是借助脚本了。在HBuilder-uniPluginDemo项目下写一个shell脚本:
vi package_ipa_unsiged.sh
#!/bin/bash
if [ -z "$1" ]; then
echo "Usage: $0 <app_name>"
exit 1
fi
APP_NAME=$1
BUILD_DIR="build/Build/Products/Release-iphoneos"
mkdir -p "${BUILD_DIR}/Payload"
mv "${BUILD_DIR}/${APP_NAME}.app" "${BUILD_DIR}/Payload/"
cd "${BUILD_DIR}"
zip -r "${APP_NAME}.ipa" "Payload"
TIMESTAMP=$(date +%Y%m%d%H%M%S)
mv "${APP_NAME}.ipa" "../../../../${APP_NAME}_${TIMESTAMP}_unsigned.ipa"
echo "IPA file created at ${BUILD_DIR}/${APP_NAME}.ipa"
echo "Moved to current directory as ${APP_NAME}_${TIMESTAMP}_unsigned.ipa"
然后chmod +x 添加可执行权限,第一个参数是你的项目Product Name,例如前面执行结果是UniPluginDemo. app,那么就是
./package_ipa_unsiged.sh UniPluginDemo
执行完后,在你的.sh目录就可以看到ipa文件了,通过SFTP工具(我调用的是FLasshFXP)下载到工作机WINDOWS(我的MAC老牛太慢了,还是用WIN好),下一步开始签名。
如果你想再省事一点,直接一个脚本把清理资源、编译和打包3个串一起一条命令执行:
#!/bin/bash
rm -rf build
xcodebuild -scheme HBuilder -configuration Release -derivedDataPath build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO
./package_ipa_unsigned.sh <yourProject>
6. 安装包签名
首先你需要生成.p12的证书文件以及.mobileprovision的配置文件。对应的生成教程很多,请先自行百度。对于未缴年费的开发者,也可以使用AppUploader助手(收费软件)来生成这两个文件,有了这两个文件,就可以开始签名了。
windows下签名工具我使用IPA ResignTool,请自行下载。我也上传了一份在这里
功能很简单,看界面一下就操作会,生成ipa后,请拷贝项目的unpackage\debug,对于unibese就是目录dist\build\app\unpackage\debug(如果unibest你打的debug包则是dist\dev\app\unpackage\debug),文件名修改为iOS_debug.ipa。然后在运行->运行到手机或模拟器->运行到iOS App基座,就可以选择你生成的自定义基座了。
关于图标:
这样编译出来的图标是没有的,需要自己做一个1024x1024的图片,然后在网站:
App Icon Generator 生成其它的图标,导入到HBuilder/Images.xcassets/下(这个目录可能没有,需要XCODE在项目上右键Add Files to...导入。
剩下:
1)默认的启动页是一个storyboard,找到HBuilder-uniPluginDemo/HBuilder-Hello/Base.lproj/LaunchScreen.storyboard调整。可以用初雪云生成一个,替换前面位置以及上级目录的sotryboard文件,如果觉得变形不好看。可以修改里面的:
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill"
将scaleToFill修改为scaleAspectFill以裁剪首位的边缘实现不变形。
2)默认没有相机、蓝牙等模块,在项目的Build Phases ->Link Binary With Libraries里添加。注意添加相应的framework,以及设置各个NSDescription描述。否则可能会闪退。