[英]iOS AudioSessionSetActive() blocking main thread?
在我的 iOS 应用程序中,我试图实现“闪避”:当我的应用程序播放短促的“类似命令”的声音时,任何背景音乐的音量都应该降低。 播放完声音后,音乐音量应恢复到原始值。
在实施时,闪避基本上按预期工作。 但是,当我在 audioPlayerDidFinishPlaying: 中调用 AudioSessionSetActive(NO) 时,为了结束闪避,此时发生的任何 UI 更新都会有一个小暂停。 这涉及自定义绘图,以及例如。 自动滚动文本等。
现在,问题来了:
这是 iOS6 中的已知问题吗? 我在 iPod/iOS5 上运行相同的代码,但我没有看到这种行为。 或者我在代码中遗漏了什么? 也许你们中的一个人已经遇到了同样的问题并找到了一个可行的解决方案。
非常感谢您的支持,
格茨
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//...
NSError *err = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&err];
//...
}
- (void) playSound {
// Enable ducking of music playing in the background (code taken from the Breadcrumb iOS Sample)
UInt32 value = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(value), &value);
// Required if using kAudioSessionCategory_MediaPlayback
value = YES;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(value), &value);
UInt32 isOtherAudioPlaying = 0;
UInt32 size = sizeof(isOtherAudioPlaying);
AudioSessionGetProperty(kAudioSessionProperty_OtherAudioIsPlaying, &size, &isOtherAudioPlaying);
if (isOtherAudioPlaying) {
AudioSessionSetProperty(kAudioSessionProperty_OtherMixableAudioShouldDuck, sizeof(value), &value);
}
AudioSessionSetActive(YES);
// Initialization of the AVAudioPlayer
NSString *soundFileName = [[NSBundle mainBundle] pathForResource:@"Beep" ofType:@"caf"];
NSURL *soundFileURL = [[NSURL alloc] initFileURLWithPath:soundFileURL];
self.soundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
[self.soundPlayer setDelegate:self];
[self.soundPlayer setVolume:[80.0/100.0];
[self.soundPlayer play];
}
- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
// Callback coming from the AVAudioPlayer
// This will block the main thread; however, it is necessary to disable ducking again
AudioSessionSetActive(NO);
}
经过一番摸索和调试,我找到了这个问题的答案。 正如原始问题所述,为了在闪避后恢复音频电平,您必须停用音频会话。
问题在于,在播放音频时停用会话会导致 0.5 秒延迟,这会阻塞 UI 线程并导致应用程序无响应(在我的情况下,计时器丢失 0.5 秒并断断续续)。
为了解决这个问题,我打电话在一个单独的线程上停用计时器。 这解决了 UI 阻塞问题并允许音频按预期闪避。 下面的代码显示了解决方案。 请注意,这是 C# 代码,因为我使用的是 Xamarin,但它可以轻松转换为 Objective-C 或 Swift 以获得相同的结果:
private void ActivateAudioSession()
{
var session = AVAudioSession.SharedInstance();
session.SetCategory(AVAudioSessionCategory.Playback, AVAudioSessionCategoryOptions.DuckOthers);
session.SetActive(true);
}
private void DeactivateAudioSession()
{
new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
var session = AVAudioSession.SharedInstance();
session.SetActive(false);
})).Start();
}
我在连接 AVAudioPlayer 之前调用 ActivateAudioSession,一旦我的播放器播放完毕,我就会调用 DeactivateAudioSession(这是恢复音频电平所必需的)。 在新线程上启动停用会降低音频级别,但不会阻塞 UI。
一个 Swift 5 解决方案:
// Create a background serial queue to call AVAudioPlayer.setActive() on
queue = DispatchQueue(label: "backgroundAudio", qos: .userInitiated, attributes: [], autoreleaseFrequency: .inherit, target: nil)
// ... sometime later, activate or deactivate the audio instance
queue.async {
do {
try AVAudioSession.sharedInstance().setActive(false, options: [/* my options */])
} catch {
print("AVAudioSession.sharedInstance().setActive(false) failed: \(error)")
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.