你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
订阅 SDK 事件
Azure 通信服务 SDK 是动态的,包含很多属性。 当这些属性更改时,作为开发人员,你可能想要了解何时发生了变更,更重要的是发生了哪些更改。 以下是操作方法!
Azure 通信通话 SDK 上的事件
本指南介绍应用可以订阅的各种事件或属性变化。 订阅这些事件可让应用了解通话 SDK 中的状态变化,并相应地做出反应。
跟踪事件至关重要,因为它使应用程序的状态能够与 ACSCalling 框架的状态保持同步,而无需你在 SDK 对象上实现拉取机制。
本指南假定你已完成快速入门,或者你实现了一个能够拨打和接听电话的应用程序。 如果未完成入门指南,请参阅我们的快速入门。
JavaScript 通话 SDK 中的每个对象都有 properties
和 collections
。 其值在对象的整个生存期内都会更改。
使用 on()
方法订阅对象的事件,使用 off()
方法取消订阅对象的事件。
属性
你可以订阅 '<property>Changed'
事件以侦听相应属性上的值变化。
属性订阅示例
在以下示例中,我们订阅了 isLocalVideoStarted
属性的值变化。
call.on('isLocalVideoStartedChanged', () => {
// At that point the value call.isLocalVideoStarted is updated
console.log(`isLocalVideoStarted changed: ${call.isLocalVideoStarted}`);
});
集合
你可以订阅“<collection>Updated”事件,以接收有关对象集合中的变化的通知。 每当在监视的集合中添加或移除元素时,都会触发“<collection>Updated”事件。
'<collection>Updated'
事件的负载具有added
数组,其中包含添加到集合的值。'<collection>Updated'
事件的负载还具有removed
数组,其中包含从集合中删除的值。
集合订阅示例
在以下示例中,我们订阅了通话对象 LocalVideoStream
的值变化。
call.on('localVideoStreamsUpdated', updateEvent => {
updateEvent.added.forEach(async (localVideoStream) => {
// Contains an array of LocalVideoStream that were added to the call
// Add a preview and start any processing if needed
handleAddedLocalVideoStream(localVideoStream )
});
updateEvent.removed.forEach(localVideoStream => {
// Contains an array of LocalVideoStream that were removed from the call
// Remove the preview and stop any processing if needed
handleRemovedLocalVideoStream(localVideoStream )
});
});
CallAgent
对象上的事件
事件名称:incomingCall
当客户端收到来电时,系统将触发 incomingCall
事件。
应用程序应如何响应该事件?
应用程序应通知用户有来电。 通知提示应为用户提供接受或拒绝通话的选项。
代码示例:
callClient.on('incomingCall', (async (incomimgCallEvent) => {
try {
// Store a reference to the call object
incomingCall = incomimgCallEvent.incomingCall;
// Update your UI to allow
acceptCallButton.disabled = false;
callButton.disabled = true;
} catch (error) {
console.error(error);
}
});
事件名称:callsUpdated
callsUpdated
更新事件将在移除通话或将其添加到通话代理时触发。 当用户拨打、接听或终止通话时,便会发生此事件。
应用程序应如何响应该事件? 应用程序应根据 CallAgent 实例的活动通话数更新其 UI。
事件名称:connectionStateChanged
connectionStateChanged
事件将在 CallAgent
的信号状态发生更新时触发。
应用程序应如何响应该事件?
应用程序应根据新状态更新其 UI。 可能的连接状态值为 Connected
和 Disconnected
代码示例:
callClient.on('connectionStateChanged', (async (connectionStateChangedEvent) => {
if (connectionStateChangedEvent.newState === "Connected") {
enableCallControls() // Enable all UI element that allow user to make a call
}
if (connectionStateChangedEvent.newState === 'Disconnected') {
if (typeof connectionStateChangedEvent.reason !== 'undefined') {
alert(`Disconnected reason: ${connectionStateChangedEvent.reason}`)
}
disableCallControls() // Disable all the UI element that allows the user to make a call
}
});
Call
对象上的事件
事件名称:stateChanged
stateChanged
事件将在通话状态发生变化时触发。 例如,当通话的状态从 connected
变为 disconnected
时。
应用程序应如何响应该事件?
应用程序应相应地更新其 UI, 根据新的通话状态禁用或启用相应的按钮和其他 UI 元素。
代码示例:
call.on('stateChanged', (async (connectionStateChangedEvent) => {
if(call.state === 'Connected') {
connectedLabel.hidden = false;
acceptCallButton.disabled = true;
startCallButton.disabled = true;
startVideoButton.disabled = false;
stopVideoButton.disabled = false
} else if (call.state === 'Disconnected') {
connectedLabel.hidden = true;
startCallButton.disabled = false;
console.log(`Call ended, call end reason={code=${call.callEndReason.code}, subCode=${call.callEndReason.subCode}}`);
}
});
事件:idChanged
idChanged
事件将在通话 ID 发生变化时触发。 当通话状态从 connecting
变为 connected
时,通话的 ID 将发生变化。 在通话完成连接后,通话的 ID 将保持不变。
应用程序可能会如何响应该事件?
应用程序应保存新的通话 ID,但稍后也可以在需要时从通话对象中检索该信息。
代码示例:
let callId = "";
call.on('idChanged', (async (callIdChangedEvent) => {
callId = call.id; // You can log it as the call ID is useful for debugging call issues
});
事件:isMutedChanged
isMutedChanged
事件将在本地音频静音或取消静音时触发。
应用程序可能会如何响应该事件?
应用程序应将静音/取消静音按钮更新为适当的状态。
代码示例:
call.on('isMutedChanged', (async (isMutedChangedEvent) => {
microphoneButton.disabled = call.isMuted;
});
事件:isScreenSharingOnChanged
isScreenSharingOnChanged
事件将在启用或禁用本地用户的屏幕共享时触发。
应用程序可能会如何响应该事件?
在打开屏幕共享时,应用程序应向用户显示预览和/或警告。 在关闭屏幕共享时,应用程序应移除预览和警告。
代码示例:
call.on('isScreenSharingOnChanged', () => {
if (!this.call.isScreenSharing) {
displayStartScreenSharingButton();
hideScreenSharingWarning()
removeScreenSharingPreview();
} else {
displayScreenSharingWarning()
displayStopScreenSharingButton();
renderScreenSharingPreview();
}
});
事件:isLocalVideoStartedChanged
isLocalVideoStartedChanged
事件将在用户启用或禁用其本地视频时触发。
应用程序可能会如何响应该事件?
应用程序应显示本地视频的预览,并启用或禁用摄像头激活按钮。
代码示例:
call.on('isLocalVideoStartedChanged', () => {
showdDisableCameraButton(call.isLocalVideoStarted);
});
事件:remoteParticipantsUpdated
应用程序应该为每个添加的 RemoteParticipants
订阅事件,并为已离开通话的参与者取消订阅事件。
应用程序可能会如何响应该事件? 应用程序应显示本地视频的预览,并启用或禁用摄像头激活按钮。
代码示例:
call.on('remoteParticipantsUpdated', (remoteParticipantsUpdatedEvent) => {
remoteParticipantsUpdatedEvent.added.forEach(participant => {
// handleParticipant should
// - subscribe to the remote participants events
// - update the UI
handleParticipant(participant);
});
remoteParticipantsUpdatedEvent.removed.forEach(participant => {
// removeParticipant should
// - unsubcribe from the remote participants events
// - update the UI
removeParticipant(participant);
});
});
事件:localVideoStreamsUpdated
localVideoStreamsUpdated
事件将在本地视频流列表发生变化时触发。 当用户启动或移除视频流时,便会发生此类变化。
应用程序可能会如何响应该事件?
对于添加的每个 LocalVideoStream
,应用程序应为其显示预览。 对于移除的每个 LocalVideoStream
,应用程序应移除预览,并停止对其进行处理。
代码示例:
call.on('localVideoStreamsUpdated', (localVideoStreamUpdatedEvent) => {
localVideoStreamUpdatedEvent.added.forEach(addedLocalVideoStream => {
// Add a preview and start any processing if needed
handleAddedLocalVideoStream(addedLocalVideoStream)
});
localVideoStreamUpdatedEvent.removed.forEach(removedLocalVideoStream => {
// Remove the preview and stop any processing if needed
this.handleRemovedLocalVideoStream(removedLocalVideoStream)
});
});
事件:remoteAudioStreamsUpdated
remoteAudioStreamsUpdated
事件将在远程音频流列表发生变化时触发。 当远程参与者在通话中添加或移除音频流时,便会发生此类变化。
应用程序可能会如何响应该事件?
如果正在处理某个流,但现已将其移除,则应停止处理。 另一方面,如果添加了流,则事件接收是开始处理新音频流的好地方。
事件:totalParticipantCountChanged
totalParticipantCountChanged
将在通话中的 totalParticipant 数量发生变化时触发。
应用程序可能会如何响应该事件?
如果应用程序显示参与者计数器,应用程序可以在收到事件时更新其参与者计数器。
代码示例:
call.on('totalParticipantCountChanged', () => {
participantCounterElement.innerText = call.totalParticipantCount;
});
事件:roleChanged
roleChanged
参与者将在通话中的 localParticipant 角色发生变化时触发。 例如,当本地参与者在通话中成为演示者 ACSCallParticipantRolePresenter
时。
应用程序可能会如何响应该事件? 应用程序应基于用户的新角色启用或禁用相应按钮。
代码示例:
call.on('roleChanged', () => {
this.roleElement = call.role;
});
事件:mutedByOthers
当本地参与者将通话中的其他参与者静音时,便会发生 mutedByOthers
事件。
应用程序可能会如何响应该事件? 应用程序应向用户显示一条消息,通知其已静音。
代码示例:
call.on('mutedByOthers', () => {
messageBanner.innerText = "You have been muted by other participant in this call";
});
事件:callerInfoChanged
更新主叫方信息时发生 callerInfoChanged
事件。
应用程序可能会如何响应该事件? 应用程序可以更新主叫方信息。
代码示例:
call.on('callerInfoChanged', () => {
showCallerInfo(call.callerInfo)
});
事件:transferorInfoChanged
更新转接方信息时发生 transferorInfoChanged
事件。
应用程序可能会如何响应该事件? 应用程序可以更新转接方信息。
代码示例:
call.on('transferorInfoChanged', () => {
showTransferorInfo(call.transferorInfo)
});
RemoteParticipant
对象上的事件
事件:roleChanged
roleChanged
事件将在通话中的 RemotePartipant
角色发生变化时触发。 例如,当 RemoteParticipant 在通话中成为演示者 ACSCallParticipantRolePresenter
时。
应用程序可能会如何响应该事件?
应用程序应根据 RemoteParticipant
新角色更新其 UI。
代码示例:
remoteParticipant.on('roleChanged', () => {
updateRole(remoteParticipant);
});
事件:isMutedChanged
isMutedChanged
事件将在其中一个 RemoteParticipant
静音或取消静音其麦克风时触发。
应用程序可能会如何响应该事件?
应用程序可能会在显示参与者的视图附近显示一个图标。
代码示例:
remoteParticipant.on('isMutedChanged', () => {
updateMuteStatus(remoteParticipant); // Update the UI based on the mute state of the participant
});
事件:displayNameChanged
displayNameChanged
事件将在 RemoteParticipant
的名称发生更新时触发。
应用程序可能会如何响应该事件?
如果参与者显示在 UI 中,应用程序应更新其名称。
代码示例:
remoteParticipant.on('displayNameChanged', () => {
remoteParticipant.nameLabel.innerText = remoteParticipant.displayName;
});
事件:isSpeakingChanged
isSpeakingChanged
事件将在通话的主导发言人发生变化时触发。
应用程序可能会如何响应该事件?
应用程序 UI 应优先显示成为主导发言人的 RemotePartipant
。
代码示例:
remoteParticipant.on('isSpeakingChanged', () => {
showAsRemoteSpeaker(remoteParticipant) // Display a speaking icon near the participant
});
事件:videoStreamsUpdated
videoStreamsUpdated
事件将在远程参与者在通话中添加或移除 VideoStream 时触发。
应用程序可能会如何响应该事件?
如果应用程序正在处理已移除的流, 应用程序应停止处理。 添加新的流时,应用程序可能需要呈现或处理该流。
代码示例:
remoteParticipant.on('videoStreamsUpdated', (videoStreamsUpdatedEvent) => {
videoStreamsUpdatedEvent.added.forEach(addedRemoteVideoStream => {
// Remove a renderer and start processing the stream if any processing is needed
handleAddedRemoteVideoStream(addedRemoteVideoStream)
});
videoStreamsUpdatedEvent.removed.forEach(removedRemoteVideoStream => {
// Remove the renderer and stop processing the stream if any processing is ongoing
this.handleRemovedRemoteVideoStream(removedRemoteVideoStream)
});
});
AudioEffectsFeature
对象上的事件
事件:effectsStarted
当所选音频效果应用于音频流时,便会发生此事件。 例如,当有人开启“噪声抑制”时,系统将触发 effectsStarted
。
应用程序可能会如何响应该事件?
应用程序可以显示或启用允许用户禁用音频效果的按钮。
代码示例:
audioEffectsFeature.on('effectsStarted', (effects) => {
stopEffectButton.style.visibility = "visible";
});
事件:effectsStopped
当所选音频效果应用于音频流时,便会发生此事件。 例如,当有人关闭“噪音抑制”时,系统将触发 effectsStopped
。
应用程序可能会如何响应该事件?
应用程序可以显示或启用允许用户启用音频效果的按钮。
代码示例:
audioEffectsFeature.on('effectsStopped', (effects) => {
startEffectButton.style.visibility = "visible";
});
事件:effectsError
如果启动或应用音频效果时发生错误,便会发生此事件。
应用程序可能会如何响应该事件?
应用程序应显示警报或错误消息,指出音频效果未按预期发挥作用。
代码示例:
audioEffectsFeature.on('effectsError', (error) => {
console.log(`Error with the audio effect ${error}`);
alert(`Error with the audio effect`);
});
安装 SDK
找到项目级 build.gradle
文件,并将 mavenCentral()
添加到 buildscript
和 allprojects
下的存储库列表中:
buildscript {
repositories {
...
mavenCentral()
...
}
}
allprojects {
repositories {
...
mavenCentral()
...
}
}
然后,在模块级 build.gradle
文件中,将以下行添加到 dependencies
部分:
dependencies {
...
implementation 'com.azure.android:azure-communication-calling:1.0.0'
...
}
初始化所需的对象
若要创建 CallAgent
实例,必须对 CallClient
实例调用 createCallAgent
方法。 此调用将异步返回 CallAgent
实例对象。
createCallAgent
方法采用 CommunicationUserCredential
作为参数来封装访问令牌。
若要访问 DeviceManager
,必须先创建 callAgent
实例。 然后,可以使用 CallClient.getDeviceManager
方法获取 DeviceManager
。
String userToken = '<user token>';
CallClient callClient = new CallClient();
CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(userToken);
android.content.Context appContext = this.getApplicationContext(); // From within an activity, for instance
CallAgent callAgent = callClient.createCallAgent(appContext, tokenCredential).get();
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();
若要为主叫方设置显示名称,请使用以下替代方法:
String userToken = '<user token>';
CallClient callClient = new CallClient();
CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(userToken);
android.content.Context appContext = this.getApplicationContext(); // From within an activity, for instance
CallAgentOptions callAgentOptions = new CallAgentOptions();
callAgentOptions.setDisplayName("Alice Bob");
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();
CallAgent callAgent = callClient.createCallAgent(appContext, tokenCredential, callAgentOptions).get();
使用我们的 Android SDK,可以订阅大多数属性和集合,以便在值更改时收到通知。
属性
若要订阅 property changed
事件,请采取以下操作:
// subscribe
PropertyChangedListener callStateChangeListener = new PropertyChangedListener()
{
@Override
public void onPropertyChanged(PropertyChangedEvent args)
{
Log.d("The call state has changed.");
}
}
call.addOnStateChangedListener(callStateChangeListener);
//unsubscribe
call.removeOnStateChangedListener(callStateChangeListener);
使用在同一类中定义的事件侦听器时,请将侦听器绑定到变量。 将变量作为参数传入以添加和删除侦听器方法。
如果尝试直接将侦听器作为参数传入,你将丢失对该侦听器的引用。 Java 将创建这些侦听器的新实例,而不是引用以前创建的实例。 这些侦听器仍会正常触发,但无法删除,因为你将不再有对它们的引用。
集合
若要订阅 collection updated
事件,请采取以下操作:
LocalVideoStreamsChangedListener localVideoStreamsChangedListener = new LocalVideoStreamsChangedListener()
{
@Override
public void onLocalVideoStreamsUpdated(LocalVideoStreamsEvent localVideoStreamsEventArgs) {
Log.d(localVideoStreamsEventArgs.getAddedStreams().size());
Log.d(localVideoStreamsEventArgs.getRemovedStreams().size());
}
}
call.addOnLocalVideoStreamsChangedListener(localVideoStreamsChangedListener);
// To unsubscribe
call.removeOnLocalVideoStreamsChangedListener(localVideoStreamsChangedListener);
设置系统
按照以下步骤设置系统。
创建 Xcode 项目
在 Xcode 中,创建新的 iOS 项目,并选择“单视图应用”模板。 本文使用 SwiftUI 框架,因此应将“语言”设置为“Swift”,并将“界面”设置为“SwiftUI”。
在本文中,无需创建测试。 请随意清除“包括测试”复选框。
使用 CocoaPods 安装包和依赖项
为应用程序创建 Podfile,如此示例所示:
platform :ios, '13.0' use_frameworks! target 'AzureCommunicationCallingSample' do pod 'AzureCommunicationCalling', '~> 1.0.0' end
运行
pod install
。使用 Xcode 打开
.xcworkspace
。
请求访问麦克风
若要访问设备的麦克风,需要使用 NSMicrophoneUsageDescription
更新应用的信息属性列表。 将关联的值设置为一个字符串,该字符串将包含在系统用于向用户请求访问权限的对话框中。
右键单击项目树的 Info.plist 条目,然后选择“打开为...”>“源代码”。 将以下代码行添加到顶层 <dict>
节,然后保存文件。
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
设置应用框架
打开项目的 ContentView.swift
文件。 将 import
声明添加到文件顶部以导入 AzureCommunicationCalling
库。 此外,导入 AVFoundation
。 你需要用它来处理代码中的音频权限请求。
import AzureCommunicationCalling
import AVFoundation
初始化 CallAgent
若要从 CallClient
创建 CallAgent
实例,必须使用 callClient.createCallAgent
方法,该方法在初始化后异步返回 CallAgent
对象。
若要创建通话客户端,请传递 CommunicationTokenCredential
对象:
import AzureCommunication
let tokenString = "token_string"
var userCredential: CommunicationTokenCredential?
do {
let options = CommunicationTokenRefreshOptions(initialToken: token, refreshProactively: true, tokenRefresher: self.fetchTokenSync)
userCredential = try CommunicationTokenCredential(withOptions: options)
} catch {
updates("Couldn't created Credential object", false)
initializationDispatchGroup!.leave()
return
}
// tokenProvider needs to be implemented by Contoso, which fetches a new token
public func fetchTokenSync(then onCompletion: TokenRefreshOnCompletion) {
let newToken = self.tokenProvider!.fetchNewToken()
onCompletion(newToken, nil)
}
将创建的 CommunicationTokenCredential
对象传递给 CallClient
并设置显示名称:
self.callClient = CallClient()
let callAgentOptions = CallAgentOptions()
options.displayName = " iOS Azure Communication Services User"
self.callClient!.createCallAgent(userCredential: userCredential!,
options: callAgentOptions) { (callAgent, error) in
if error == nil {
print("Create agent succeeded")
self.callAgent = callAgent
} else {
print("Create agent failed")
}
})
使用我们的 iOS SDK,可以订阅大多数属性和集合,以便在值更改时收到通知。
属性
若要订阅 property changed
事件,请使用以下代码。
call.delegate = self
// Get the property of the call state by getting on the call's state member
public func call(_ call: Call, didChangeState args: PropertyChangedEventArgs) {
{
print("Callback from SDK when the call state changes, current state: " + call.state.rawValue)
}
// to unsubscribe
self.call.delegate = nil
集合
若要订阅 collection updated
事件,请使用以下代码。
call.delegate = self
// Collection contains the streams that were added or removed only
public func call(_ call: Call, didUpdateLocalVideoStreams args: LocalVideoStreamsUpdatedEventArgs) {
{
print(args.addedStreams.count)
print(args.removedStreams.count)
}
// to unsubscribe
self.call.delegate = nil