过去两年多时间里,视频会议成为职场工作乃至社会常态,在各类场景中得到广泛应用。例如企业会议、培训赋能、远程咨询、产品发布、远程面试等。本案例中的视频会议app来自开发者实战,采用YonBuilder移动开发平台的AVM.js多端框架进行开发。
使用AVM.js,一个技术栈可同时开发Android& iOS app、小程序和H5,且多端渲染效果统一;全新的app引擎3.0不依赖webView,提供百分百的原生渲染,保障app性能和体验与原生app一致;现有api直接映射兼容小程序接口,延续已有开发习惯;后台使用的PHP的thinkphp框架,通过composer集成各类插件。
一、思维导图
二、功能介绍
1.创建会议:确认会议时间、参会人员、会议主题、确定会议主持人(默认为发起人)可开启会议;同时会通过应用消息和短信通知参会人员。
2.加入会议:可通过会议大厅的会议列表直接加入,也可通过输入会议编号加入会议;加入会议的前提是会议已在进行中。
3.快速会议:可直接确认会议人员然后发起实时视频会议,参会人员实时接收应用消息或短信,快速进入会议。
4.历史会议:分为我主持的会议、我参与的会议。
5.会议大厅:列表显示今天需要参加的会议。
6.会议纪要:会议结束后,会议主持人可通过app或后台系统,把会议纪要整理发布到相关会议中,参会人员可在会议详情中查看会议纪要。
7.会议附件:主持人员可在会议详情中,把会议相关的附件上传至相关会议中,参与人员可在会议详情中下载附件。
8.通讯录:展示系统内的联系人,在创建会议时,会议中邀请人的时候会用到。
三、应用模块
四、项目目录
五、应用展示
六、开发过程
应用导航:使用的是tabLayout布局作为应用的导航。
系统首页使用tabLayout,可以将相关参数配置在JSON文件中,再在config.xml中将content的值设置成该JSON文件的路径。
如果底部导航没有特殊需求这里强烈建议大家使用tabLayout为app进行布局,官方已经将各类手机屏幕及不同的分辨率进行了适配,免去了很多关于适配方面的问题。
{
"name": "root",
"hideNavigationBar": true,
"navigationBar": {
"background": "#ffffff",
"color": "#333333",
"shadow": "#ffffff",
"hideBackButton": true
},
"tabBar": {
"scrollEnabled": false,
"background": "#fff",
"shadow": "#dddddd",
"color": "#aaaaaa",
"selectedColor": "#333333",
"index":0,
"preload": 0,
"frames": [{
"name": "home",
"url": "pages/main/home.stml",
"title": "会议"
}, {
"name": "classify-index",
"url": "pages/classify/classify-index.stml",
"title": "消息"
}, {
"name": "shopping-index",
"url": "pages/shopping/shopping-index.stml",
"title": "文档"
}, {
"name": "my-index",
"url": "pages/my/my-index.stml",
"title": "我的"
}],
"list": [{
"text": "会议",
"iconPath": "image/tabbar/meeting.png",
"selectedIconPath": "image/tabbar/meeting-o.png",
"scale":3
}, {
"text": "消息",
"iconPath": "image/tabbar/message.png",
"selectedIconPath": "image/tabbar/message-o.png",
"scale":3
}, {
"text": "文档",
"iconPath": "image/tabbar/doc.png",
"selectedIconPath": "image/tabbar/doc-o.png",
"scale":3
}, {
"text": "我的",
"iconPath": "image/tabbar/user.png",
"selectedIconPath": "image/tabbar/user-o.png",
"scale":3
}]
}
}
动态权限
安卓10之后,对应用的权限要求提高,不像老版本一样配置上就会自动获取,必须进行提示。
依据官方给出的教程进行了动态权限的设置。
添加mianfest.xml文件;
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<application name="targetSdkVersion" value="30"/>
</manifest>
具体的使用说明,在官方论坛中有专门的帖子,app动态权限及Android平台targetSdkVersion设置。
在系统主页进行动态权限获取,也可在特殊页面的中获取本页面所需的权限,这个可根据具体的业务需求进行处理。本系统涉及到了文件存储、摄像头、麦克风的获取,具体的获取方式见如下代码,因为本系统的初始化页面是home.stml,所以在本页面的apiready()中进行权限验证。
apiready(){
let limits=[];
//获取权限
var resultList = api.hasPermission({
list: ['storage', 'camera', 'microphone']
});
if (resultList[0].granted) {
// 已授权,可以继续下一步操作
} else {
limits.push(resultList[0].name);
}
if (resultList[1].granted) {
// 已授权,可以继续下一步操作
} else {
limits.push(resultList[1].name);
}
if (resultList[2].granted) {
// 已授权,可以继续下一步操作
} else {
limits.push(resultList[2].name);
}
if(limits.length>0){
api.requestPermission({
list: limits,
}, (res) => {
});
}
}
WebSocket
用于即时通话的时候,监听用户在线状态,可通知用户加入会议。
具体的通讯原理步骤是:
会议发起人发起会议→通过websocket给参会人员发送消息指令→参会人员接收发送的websocket消息,通过监听触发进入会议房间,同时给会议发起人发送进入会议房间的消息→会议发起人收到有人进入了会议房间消息后,通过监听触发进入会议房间的操作。
这种流程是会议发起人不必先进入回房间进行等待,不用启用RTC模块,只有当有其他人员收到提醒进入会议房间后才会启用RTC模块进入房间,可以有效避免资源浪费。
还有一种简易模式,会议发起人发起会议,并启用RTC模块,进入会议房间进行等待(判断等待时间,比如超过3分钟没有其他人员加入房间,自动退出会议房间结束会议)→通过websocket给参会人员发送消息指令→参会人员接收发送的websocket消息,通过监听触发进入会议房间。
这种模式如果其他参会人员不及时参加会议的时候会造成部分资源的浪费。
进入会议后其他后续的操作,就可以通过tencentTRTC模块中的方法进行处理。
websocket的目的就是及时通知参会人员有会议要参加,因为RTC模块本身没有集成这个功能,这部分是在进入会议房间之前的操作。
本app用的是websocket模块,可配置全局变量,方便实用。当然也可以尝试其他的websocket模块。
AVM框架里官方就集成了websocket。使用说明文档。
apiready(){
//链接websocket
var webSocket = api.require('webSocket');
//消息监听,可以监听连接,断开,接收消息等事件
webSocket.addEventListener((ret, err) => {
console.log(JSON.stringify(ret) + " " + JSON.stringify(err));
//断开重连
if(ret.evenType=='Closed'){
webSocket.open({
url : 'ws://192.168.1.5:8888/socket'
}, (ret, err) => {
console.log(JSON.stringify(ret) + " " + JSON.stringify(err));
});
}
//收到消息
if(ret.evenType=='ReturnData'){
//解析data中的内容,获取会议房间ID进入会议
}
});
//获取当前的websocket链接状态
var webSocketStatus = webSocket.getConnectState();
//未链接则进行链接,如果已链接则无效操作
if(webSocketStatus.State =='CLOSED'){
webSocket.open({
url : 'ws://192.168.1.5:8888/socket'
}, (ret, err) => {
console.log(JSON.stringify(ret) + " " + JSON.stringify(err));
});
}
},
视频通话RTC
使用的是tencentTRTC模块,查看模块文档。
首先需要去申请腾讯云SDKappId,进入腾讯云实时音视频控制台创建应用,即可看到SDKappId。
为什么用tencentTRTC?tencentTRTC模块不会把SDKappId与应用进行绑定,这样就可以使用一个SDKappId来实现两个不同的app之间的视频通话了,共用腾讯云的通话时长。
而且tencentTRTC的接口相比较其他RTC模块更丰富,可以更好地满足一些个性化需求。
消息事件
通过sendEvent把事件广播出去,然后在其他页面通过addEventListener监听事件,通过事件名和附带的参数进行其他操作。
举例说明:
1.当创建会议成功之后,需要发送一个会议创建成功的事件;在会议列表或者其他展示会议的页面,需要监听此事件,然后在监听成功的回调中做刷新操作。
2.当会议开始或者结束之后,需要发送相应的事件,在会议列表或者其他展示会议的页面,需要监听此类事件,在监听成功的回调中做刷新列表或者更改会议状态的操作。
消息推送
ajpush模块封装了极光推送平台的SDK,使用此模块可实现接收推送通知和透传消息功能。
//初始化JpushSDK
initJpush(){
var jpush = api.require('ajpush');
jpush.init((ret, err)=>{
if(ret && ret.status){
//绑定别名
if(api.getPrefs({sync: true,key: 'userid'})){
jpush.bindAliasAndTags({
alias:api.getPrefs({sync: true,key: 'userid'}),
tags:['APPUSER']
}, (ret, err)=>{
if(ret.statusCode==0){
api.toast({ msg: '推送服务初始化成功'});
}
else{
api.toast({ msg: '绑定别名失败'});
}
});
}
//监听消息
jpush.setListener((ret) => {
// var content = ret.content;
api.toast({ msg: ret.content});
});
}
else{
api.toast({ msg: '推送服务初始化失败'});
}
});
api.addEventListener({name:'pause'}, function(ret,err) {
jpush.onResume();//监听应用进入后台,通知jpush暂停事件
})
api.addEventListener({name:'resume'}, function(ret,err) {
jpush.onResume();//监听应用恢复到前台,通知jpush恢复事件
})
},
短信验证码
用户注册的时候需要通过手机短信验证码进行校验,以保证手机号真实有效,能够正常接收应用推送的各类短信通知提醒。
本应用中使用的是AVM模块库中的verification-code-input组件,可自定义验证码长度和再次获取时间间隔,自动校验验证码有效性。
示例代码
<template>
<view class="page">
<safe-area></safe-area>
<verification-code-input :limitSecond={seconds} :limitCode={codeLen} onsetCode="getCode"></verification-code-input>
</view>
</template>
<script>
import '../../components/verification-code-input.stml'
export default {
name: 'demo-verification-code-input',
apiready(){
},
data() {
return{
code:'',
seconds:60,
codeLen:4
}
},
methods: {
getCode(e){
// console.log(JSON.stringify(e.detail));
this.data.code = e.detail;
}
}
}
</script>
关于验证码的有效时间,是通过后台进行设定的,通过session缓存每个手机号的验证码,并设置缓存有效时间,表单提交的时候通过session去获取验证码,如果session失效,则无法获取验证码,接口可直接返回验证码失效提示。
清空缓存
首先通过getCacheSize获取应用的缓存数量,并在标签中显示,然后给标签添加点击事件,在事件中通过clearCache清除应用缓存。
计算当前应用的缓存大小,保留一位小数。
apiready(){
//获取APP缓存 异步返回结果:
api.getCacheSize((ret) => {
this.data.cache = parseInt(ret.size/1024/1024).toFixed(1);
});
},
执行清除缓存,并提示信息。
clearCache(){
api.clearCache(() => {
this.data.cache=0.0;
api.toast({
msg:'清除完成'
})
});
}
AVM组件使用
项目中使用了很多的AVM组件,其中包括视频通话组件、通讯录组件、滑动单元格组件、日期时间Picker组件、数字键盘组件等等。
其中视频通话组件(easy-video-call、easy-voice-communication、multi-person-video-call)用的是声网的SDK,这里借用了样式,把模块换成了tencentRTC。
消息列表列表中使用了easy-swiper-cell滑动单元格组件,来实现滑动操作已读。
时期和时间选择用到了time-picker、date-picker组件。
通讯录使用的是address-book组件。
在通过会议编号进入会议时,由于会议编号全是数字,这里使用了number-keyboard数组键盘组件。
文档下载、图片浏览
会议结束后会上传会议纪要,会议相关文件等各类文档,主要包括doc、excel、pdf和图片。
对于doc、excel、pdf这类文件使用的是docReader模块。方式是先通过api.download方法下载文件,然后在回调中通过docReader模块唤醒第三方工具进行文件浏览。