J'écris une application de lecteur de musique de base, mais j'ai quelques problèmes à gérer les transitions d'état des applications.
J'utilise Swift 3 et MPMusicPlayerController.systemMusicPlayer ()
Le but est ceci:
1) Continuez à écouter de la musique lorsque l'utilisateur appuie sur le bouton Accueil et que l'application entre en bg (fonctionne)
2) Arrêtez le lecteur (myMP.stop ()) si l'utilisateur quitte l'application (fonctionne parfois, génère des erreurs d'autres fois)
J'ai tracé les flux en utilisant des instructions d'impression basées sur des actions possibles et j'ai obtenu ceci:
Le flux 2 correspond à ce à quoi je m'attendais, mais le flux 1 génère une erreur lorsque l'application est fermée - je suppose que "se terminera" ici.
EDIT: Le problème principal est que lorsque vous quittez l'application à l'aide du flux 1, "se terminera" n'est jamais appelé - par conséquent, "myMP.stop ()" n'est jamais appelé et le lecteur continue à jouer une fois l'application terminée.
Le comportement diffère nettement si vous cliquez une fois sur l’accueil (flux 1) ou le double (flux 2) tant que l’application est active.
Pourquoi ai-je deux réponses différentes de ce que devrait être les mêmes actions?
EDIT: Plus important encore, comment puis-je arrêter MediaPlayer pour le flux 1 s'il n'arrive jamais à "se terminer"?
MODIFIER:
Voici un exemple de code qui reproduit le problème:
AppDelegate.Swift
//
// AppDelegate.Swift
// Jumbo Player
//
import UIKit
//import MediaPlayer
//doesn't matter where this is declared - here or in ViewController - same results
//let myMP:MPMusicPlayerController = MPMusicPlayerController.systemMusicPlayer()
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
print("applicationWillResignActive")
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
print("applicationDidEnterBackground")
}
func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
print("applicationDidReceiveMemoryWarning")
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
print("applicationWillEnterForeground")
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
print("applicationDidBecomeActive")
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
print("applicationWillTerminate")
myMP.stop();
}
}
ViewController.Swift
//
// ViewController.Swift
// junkplayer
//
import UIKit
import MediaPlayer
let myMP:MPMusicPlayerController = MPMusicPlayerController.systemMusicPlayer()
class ViewController: UIViewController {
@IBOutlet weak var xxx: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let qrySongs = MPMediaQuery.songs()
myMP.setQueue(with: qrySongs)
}
@IBAction func playbut(_ sender: UIButton) {
myMP.play()
}
}
Téléchargez le projet ici: www.NextCoInc.com/public/junkplayer.Zip
Le message "terminé en raison du signal 9" signifie simplement que votre application a été fermée par un signal SIGKILL. Le système d'exploitation envoie ce signal chaque fois que votre application est arrêtée involontairement, que ce soit en raison de la pression de la mémoire (ou de plusieurs autres raisons non pertinentes pour cette discussion), ou de l'utilisateur qui tue explicitement votre application en appuyant deux fois sur le bouton Accueil et en le balayant.
Dans votre cas, l'utilisateur tue explicitement votre application. Le message "Terminé en raison du signal 9" est donc tout à fait attendu. Si votre application est l'application de premier plan actuelle, votre méthode applicationWillTerminate sera appelée, comme indiqué dans le schéma du flux de logique ci-dessus (flux 2). Si votre application n'est PAS l'application de premier plan actuelle (flux 1), votre méthode applicationWillTerminate
ne sera PAS appelée si votre application est dans un état suspendu. C'est le comportement attendu. Notez également la distinction entre "état d'arrière-plan" et "état suspendu". Ils ne sont pas la même chose .
Donc, si je vous ai bien compris, le problème vient du fait que le son continue de jouer après la fermeture de votre application par l'utilisateur (flux 1). Cela signifie que vous faites quelque chose de mal en manipulant MPMusicPlayerController
, car il devrait gérer cette transition d’état automatiquement.
Assurez-vous que vous avez défini le correct IBackgroundMode pour votre application. Si vous définissez un mode d’arrière-plan incorrect, votre application risque de mal se comporter, car le système d’exploitation autorise uniquement certaines opérations en arrière-plan, selon le mode d’arrière-plan que vous avez défini. Si vous définissez le mauvais mode (ou si vous essayez de faire des choses explicitement interdites dans le mode que vous avez défini), votre application sera suspendue ou résiliée.
Assurez-vous d'avoir correctement configuré votre session audio.
Assurez-vous de répondre correctement à notifications du lecteur de musique - en particulier, assurez-vous que vous appelez correctement beginGeneratingPlaybackNotifications
et endGeneratingPlaybackNotifications
, et que vous gérez ces notifications. correctement. Vérifiez vos gestionnaires de notifications pour vous assurer que vous ne faites pas de bêtises là-dedans. Assurez-vous que votre contrôleur ne soit pas hors de portée ou autrement libéré avant d'avoir appelé endGeneratingPlaybackNotifications
.
Si vous avez tout fait correctement, un MPMusicPlayerController
se gère tout seul. Vous ne devriez donc rien faire de spécial pour que cela fonctionne lorsque votre application passe en arrière-plan (à part définir le bon fichier UIBackgroundMode
, bien sûr). En dernier recours, commencez à commenter le code jusqu'à ce que votre application devienne simplement une application "ouvrir-un-fichier-audio-and-play-it" à nu, et voyez si elle se ferme correctement à ce moment-là. Si tel est le cas, vous pouvez commencer à supprimer le code un à un jusqu'à ce qu'il échoue. Vous saurez ainsi quelle partie de votre application est à l'origine du problème et vous pourrez ensuite la réduire.
J'avais trois tâches de fond pour l'application.
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
<string>location</string>
<string>remote-notification</string>
</array>
Message from debugger: Terminated due to signal 9
Ce message survient lorsque l'application s'exécute en arrière-plan et qu'elle consomme plus de mémoire que la mémoire allouée par le système d'exploitation d'un iPhone pour les applications en cours d'exécution en arrière-plan.
Dans mon cas, je mettais à jour l'emplacement de l'utilisateur et exécutais l'api d'emplacement de l'utilisateur sur le serveur en permanence. Il consommait beaucoup de mémoire. Pour cette raison, l'OS a tué l'application.
Nous avons reçu ce message en raison de la pression de la mémoire sur le système d'exploitation et avons tué l'application en arrière-plan.
J'ai optimisé le code et chaque fois que nous avons besoin de mettre à jour l'emplacement de l'utilisateur, nous avons seulement tiré l'adresse api sur le serveur. J'ai aussi enable \ disable
Le drapeau allowsBackgroundLocationUpdates
if #available(iOS 9.0, *) { coreLocationManager.allowsBackgroundLocationUpdates = false }
selon nos besoins. cela a bien fonctionné.