【问题标题】:Playing sound in didReceiveRemoteNotification, while in the background, using text to speech feature在 didReceiveRemoteNotification 中播放声音,而在后台,使用文本到语音功能
【发布时间】:2019-03-03 15:58:47
【问题描述】:

我目前正在尝试的是当应用在后台(或可能从挂起状态唤醒)收到远程通知时播放消息。

应用从挂起模式唤醒后,完全不播放声音。

当应用程序在前台时,调用didReceiveRemoteNotification:方法后立即播放声音。

当应用程序从挂起模式唤醒时调用didReceiveRemoteNotification: 方法时立即播放声音的合适方法是什么?

这是一些代码(语音管理器类):

-(void)textToSpeechWithMessage:(NSString*)message andLanguageCode:(NSString*)languageCode{

AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *error = nil;
DLog(@"Activating audio session");
if (![audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionMixWithOthers error:&error]) {
    DLog(@"Unable to set audio session category: %@", error);
}
BOOL result = [audioSession setActive:YES error:&error];
if (!result) {
    DLog(@"Error activating audio session: %@", error);

}else{
    AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:message];

    [utterance setRate:0.5f];

    [utterance setVolume:0.8f];

    utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:languageCode];

    [self.synthesizer speakUtterance:utterance];
}

}

-(void)textToSpeechWithMessage:(NSString*)message{

[self textToSpeechWithMessage:message andLanguageCode:[[NSLocale preferredLanguages] objectAtIndex:0]];

}

之后在AppDelegate:

[[MCSpeechManager sharedInstance] textToSpeechWithMessage:messageText];

我在 Capabilities->Background Modes 部分启用了 Audio、AirPlay 和 Picture in Picture 选项。

编辑:

如果需要,也许我应该启动一个后台任务并运行过期处理程序?我想这可能会奏效,但我也想听听解决这种情况的常用方法。

当我在后台收到通知时,使用此代码也会收到下一个错误:

激活音频会话时出错:Error Domain=NSOSStatusErrorDomain 代码=561015905“(空)”

代码 561015905 适用于:

AVAudioSessionErrorCodeCannotStartPlaying = '!pla', /* 0x21706C61, 561015905

它被描述为:

如果应用程序的信息属性列表出现此错误类型 不允许使用音频,或者如果应用程序在后台并使用 不允许背景音频的类别。

但我在其他类别(AVAudioSessionCategoryAmbientAVAudioSessionCategorySoloAmbient)中遇到同样的错误

【问题讨论】:

  • 我一拿到电脑就会添加这个信息。在应用程序挂起时调用 didReceiveRemoteNotification。意味着应用程序没有运行,手机可以被锁定或其他一些应用程序可以运行等。
  • @SwiftArchitect 收到远程通知时调用的方法是application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo(注意它没有完成处理程序)。此外,在这里:developer.apple.com/reference/uikit/uiapplicationdelegate/… 我们可以发现只有当应用程序在前台运行时才会调用此方法......但据我所知,它是在我的应用程序处于暂停模式时调用的(它在休息时停止点我把上面的方法)。
  • 引入:3.0,弃用:10.0。即使您找到了使用该路由的解决方案,它也会使用最终会被拒绝的已弃用 API。
  • 虽然看起来不相关,但从后台播放音频需要 Info.plist 中的另一个特殊权限。我想在项目的 Info.plist 中设置 'UIBackgroundModes' = 'audio' 可能值得一试。请记住,此权限用于需要在后台播放音乐的应用程序。所有无线电应用程序都使用它。如果设置了此权限而没有具体说明您的应用需要此权限的原因,App Store 可能会拒绝您的应用。祝你好运!
  • @TarunTyagi 谢谢塔伦。实际上我确实启用了后台模式(请参阅我的问题)。我即将为此写支持票,因此最终会提供解决方案。

标签: ios objective-c avspeechsynthesizer background-mode avspeechutterance


【解决方案1】:

由于我无法重现您描述的错误,让我提供一些指针和一些代码。

  • 您是否针对最新的 SDK 构建/测试/运行? iOS X 中的通知机制发生了重大变化
  • 我必须假设对didReceiveRemoteNotification 的调用必须响应来自所述通知的用户操作,例如点击通知消息。
  • 无需设置任何后台模式保存应用下载内容以响应推送通知

如果以上所有陈述都是正确的,那么当前的答案将集中在通知到达时会发生什么。

  1. 设备收到通知
  2. 用户点击消息
  3. 应用启动
  4. didReceiveRemoteNotification 被调用

在第 4 步,textToSpeechWithMessage 按预期工作:

func application(_ application: UIApplication,
                 didReceiveRemoteNotification
                 userInfo: [AnyHashable : Any],
                 fetchCompletionHandler completionHandler:
                 @escaping (UIBackgroundFetchResult) -> Void) {
    textToSpeechWithMessage(message: "Speak up", "en-US")
}

为简单起见,我使用OneSignal 连接通知:

import OneSignal
...
_ = OneSignal.init(launchOptions: launchOptions,
                   appId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
// or
_ = OneSignal.init(launchOptions: launchOptions,
                   appId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
                   {
                       (s:String?, t:[AnyHashable : Any]?, u:Bool) in
                       self.textToSpeechWithMessage(message: "OneDignal", "en-US")
                   }

textToSpeechWithMessage 几乎没有改动,为了完整起见,它在 Swift 3 中:

import AVFoundation
...
let synthesizer = AVSpeechSynthesizer()
func textToSpeechWithMessage(message:String, _ languageCode:String)
{
    let audioSession = AVAudioSession.sharedInstance()

    print("Activating audio session")
    do {
        try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord,
                                     with: [AVAudioSessionCategoryOptions.defaultToSpeaker,
                                            AVAudioSessionCategoryOptions.mixWithOthers]
        )
        try audioSession.setActive(true)

        let utterance = AVSpeechUtterance(string:message)
        utterance.rate = 0.5
        utterance.volume = 0.8
        utterance.voice = AVSpeechSynthesisVoice(language: languageCode)
        self.synthesizer.speak(utterance)

    } catch {
        print("Unable to set audio session category: %@", error);
    }
}

【讨论】:

  • 您好,感谢您的回复。在我的情况下,当用户点击通知时,一切都会起作用。但是即使用户没有点击它,我也需要播放声音。是的,我正在针对最新的 SDK 构建并在运行最新 iOS 的真实设备上进行测试......
  • 从我的经验来看,didReceiveRemoteNotification 只有在用户采取某种行动后才会被调用。您能否在用户响应之前获得didReceiveRemoteNotification 触发?
  • didReceiveRemoteNotification 即使用户不点击通知也会被执行(所以是的,它会在用户响应之前触发,这不是问题)。我可以产生这个并通过简单地在这个方法中放置一个断点来进行测试。尽管如此,手机在连接/未连接到电源时的行为并不相同,所以我必须在没有将手机连接到计算机的情况下通过记录事情来测试这一点......将向您回复结果。
  • 我明白了。我所能提供的就是显而易见的:stackoverflow.com/questions/39382852/…stackoverflow.com/questions/5056689/…
  • 谢谢,我没有使用包含completionHandlerapplicationDidReceiveRemoteNotification: 方法的变体,所以这可能是个问题。我没有机会对此进行广泛的测试,但我注意到这个方法是:1)如果应用程序被用户终止,并且通知到达,这个方法将不会被执行。 2)如果应用程序附加到调试器,但在后台,此方法会触发。第三种可能性是在后台断开电话与电源的连接。这也可能行不通,因为文档说这种方法只能在前台工作。
【解决方案2】:

请执行

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler; 

方法。您将在后台获得回调以播放音频。

【讨论】:

  • 是的,我知道这一点...只是尝试。
  • 出现和我上面描述的一样的错误:Error activating audio session: Error Domain=NSOSStatusErrorDomain Code=561015905 "(null)".
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多