Lio*_*lak 25 audio opengl-es multitasking ios sprite-kit
只是我遇到的一个问题 - 我有一个SKScene,我在其中使用SKAction类方法播放声音fx
[SKAction playSoundFileNamed:@"sound.wav" waitForCompletion:NO];
Run Code Online (Sandbox Code Playgroud)
现在,当我尝试去背景时,无论声音结束,显然iOS正在终止我的应用程序gpus_ReturnNotPermittedKillClient.
现在只有当我评论这一行并且没有运行动作时,iOS才能在后台运行它(当然,暂停,但没有终止).
我究竟做错了什么?
编辑:如果没有运行,iOS将不会终止应用程序 - 例如,如果它if statement没有运行(soundOn == YES)或类似的东西,当布尔是false
Lea*_*s2D 28
AVAudioSession当应用程序进入后台时,问题无法激活.这并不是很明显,因为Sprite Kit没有提到它在内部使用AVAudioSession.
修复非常简单,也适用于ObjectAL => AVAudioSession在应用程序处于后台时将其设置为非活动状态,并在应用程序进入前台时重新激活音频会话.
使用此修复程序的简化AppDelegate如下所示:
#import <AVFoundation/AVFoundation.h>
...
- (void)applicationWillResignActive:(UIApplication *)application
{
// prevent audio crash
[[AVAudioSession sharedInstance] setActive:NO error:nil];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// prevent audio crash
[[AVAudioSession sharedInstance] setActive:NO error:nil];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// resume audio
[[AVAudioSession sharedInstance] setActive:YES error:nil];
}
Run Code Online (Sandbox Code Playgroud)
PS:此修复程序将包含在Kobold Kit v7.0.3中.
Krz*_*oda 14
我发现它是关于在AppDelegate applicationDidEnterBackground中停用AVAudioSession的,但通常它会失败并出现错误(实际上没有停用):
Error Domain=NSOSStatusErrorDomain Code=560030580 "The operation couldn’t be completed. (OSStatus error 560030580.)
Run Code Online (Sandbox Code Playgroud)
这仍然导致这里描述的崩溃:进入后台时Spritekit崩溃.
所以,setActive还不够:不 - 我们必须有效地停用它(没有那个错误).我通过向AppDelegate添加专用实例方法做了一个简单的解决方案,只要没有错误就停用AVAudioSession.
简而言之它看起来像这样:
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@"%s", __FUNCTION__);
[self stopAudio];
}
- (void)stopAudio {
NSError *error = nil;
[[AVAudioSession sharedInstance] setActive:NO error:&error];
NSLog(@"%s AudioSession Error: %@", __FUNCTION__, error);
if (error) [self stopAudio];
}
Run Code Online (Sandbox Code Playgroud)
NSLog证明:
2014-01-25 11:41:48.426 MyApp[1957:60b] -[ATWAppDelegate applicationDidEnterBackground:]
2014-01-25 11:41:48.431 MyApp[1957:60b] -[ATWAppDelegate stopAudio] AudioSession Error: Error Domain=NSOSStatusErrorDomain Code=560030580 "The operation couldn’t be completed. (OSStatus error 560030580.)"
2014-01-25 11:41:48.434 MyApp[1957:60b] -[ATWAppDelegate stopAudio] AudioSession Error: Error Domain=NSOSStatusErrorDomain Code=560030580 "The operation couldn’t be completed. (OSStatus error 560030580.)"
2014-01-25 11:41:48.454 MyApp[1957:60b] -[ATWAppDelegate stopAudio] AudioSession Error: Error Domain=NSOSStatusErrorDomain Code=560030580 "The operation couldn’t be completed. (OSStatus error 560030580.)"
2014-01-25 11:41:49.751 MyApp[1957:60b] -[ATWAppDelegate stopAudio] AudioSession Error: (null)
Run Code Online (Sandbox Code Playgroud)
这真的很短,因为它不关心stackoverflow :)如果AVAudioSession不想在几千次尝试后关闭(崩溃也是不可避免的).因此,在Apple修复它之前,这只能被视为黑客攻击.顺便说一句,控制启动AVAudioSession也是值得的.
完整解决方案可能如下所示:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSLog(@"%s", __FUNCTION__);
[self startAudio];
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@"%s", __FUNCTION__);
// SpriteKit uses AVAudioSession for [SKAction playSoundFileNamed:]
// AVAudioSession cannot be active while the application is in the background,
// so we have to stop it when going in to background
// and reactivate it when entering foreground.
// This prevents audio crash.
[self stopAudio];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
NSLog(@"%s", __FUNCTION__);
[self startAudio];
}
- (void)applicationWillTerminate:(UIApplication *)application {
NSLog(@"%s", __FUNCTION__);
[self stopAudio];
}
static BOOL isAudioSessionActive = NO;
- (void)startAudio {
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *error = nil;
if (audioSession.otherAudioPlaying) {
[audioSession setCategory: AVAudioSessionCategoryAmbient error:&error];
} else {
[audioSession setCategory: AVAudioSessionCategorySoloAmbient error:&error];
}
if (!error) {
[audioSession setActive:YES error:&error];
isAudioSessionActive = YES;
}
NSLog(@"%s AVAudioSession Category: %@ Error: %@", __FUNCTION__, [audioSession category], error);
}
- (void)stopAudio {
// Prevent background apps from duplicate entering if terminating an app.
if (!isAudioSessionActive) return;
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *error = nil;
[audioSession setActive:NO error:&error];
NSLog(@"%s AVAudioSession Error: %@", __FUNCTION__, error);
if (error) {
// It's not enough to setActive:NO
// We have to deactivate it effectively (without that error),
// so try again (and again... until success).
[self stopAudio];
} else {
isAudioSessionActive = NO;
}
}
Run Code Online (Sandbox Code Playgroud)
然而,与SpriteKit应用程序中的AVAudioSession中断相比,这个问题是小菜一碟.如果我们不处理它,迟早会遇到内存泄漏和CPU 99%的大问题(来自[SKSoundSource queuedBufferCount的56%]和来自[SKSoundSource isPlaying]的34% - 见工具),因为SpriteKit很顽固,"播放"声音,即使他们无法播放:)
据我所知,最简单的方法是setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionMixWithOthers.我认为,任何其他AVAudioSession类别都需要避免使用playFileNamed:这可以通过制作自己的SKNode runAction:类别来播放声音方法来实现,例如使用AVAudioPlayer.但这是一个单独的主题.
我的完整的一体化解决方案与AVAudioPlayer实现在这里:http://iknowsomething.com/ios-sdk-spritekit-sound/
编辑:修复失踪的paren.