beginReceivingRemoteControlEvents 和 endReceivingRemoteControlEvents 的调用时机

beginReceivingRemoteControlEvents 和 endReceivingRemoteControlEvents 的调用时机
这两个方法需要在特定的生命周期节点调用,以确保远程控制功能正常工作且不会浪费系统资源。以下是详细的调用时机指南:

  1. 音频播放应用的标准调用时机
    播放开始时调用 begin
- (void)playAudio {
    // 1. 设置音频会话
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    [session setActive:YES error:nil];
    
    // 2. 开始播放前注册远程控制
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];
    
    // 3. 开始播放
    [self.audioPlayer play];
    
    // 4. 更新锁屏界面信息
    [self updateNowPlayingInfo];
}

播放停止时调用 end

- (void)stopAudio {
    // 1. 停止播放
    [self.audioPlayer stop];
    
    // 2. 停止接收远程控制事件
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    [self resignFirstResponder];
    
    // 3. 清理锁屏界面信息
    [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:nil];
}
  1. ViewController 生命周期中的调用
    视图控制器示例
@interface AudioPlayerViewController : UIViewController
@property (nonatomic, assign) BOOL isPlaying;
@end


@implementation AudioPlayerViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupAudioSession];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    // 如果进入页面时正在播放,注册远程控制
    if (self.isPlaying) {
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
        [self becomeFirstResponder];
    }
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    // 离开页面时,如果不是正在播放,停止接收
    if (!self.isPlaying) {
        [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
        [self resignFirstResponder];
    }
}

- (void)dealloc {
    // 确保对象销毁时清理资源
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
}
@end
  1. AppDelegate 中的应用状态管理
    应用生命周期回调
- (void)applicationDidBecomeActive:(UIApplication *)application {
    // 应用进入前台
    if ([self.audioManager isPlaying]) {
        // 如果正在播放,确保注册远程控制
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    }
}

- (void)applicationWillResignActive:(UIApplication *)application {
    // 应用进入后台
    if (![self.audioManager isPlaying]) {
        // 如果没有播放,停止接收以节省资源
        [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    }
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // 应用进入后台
    if ([self.audioManager isPlaying]) {
        // 后台播放时需要保持远程控制
        // 注意:需要设置后台音频模式
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
        
        // 启动后台任务
        __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
            [application endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }];
    }
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // 应用回到前台
    if (![self.audioManager isPlaying]) {
        // 如果没有播放,停止接收
        [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    }
}
  1. 具体场景的调用时机矩阵
场景 beginReceivingRemoteControlEvents endReceivingRemoteControlEvents
开始播放音频 ✅ 立即调用 ❌ 不调用
暂停播放 ❌ 不调用 ⚠️ 可选调用(如果长时间暂停)
停止播放 ❌ 不调用 ✅ 立即调用
切换歌曲 ❌ 不调用(已注册) ❌ 不调用
应用进入后台(正在播放) ✅ 确保已调用 ❌ 不调用
应用进入后台(未播放) ❌ 不调用 ✅ 确保已调用
应用回到前台(正在播放) ✅ 确保已调用 ❌ 不调用
应用回到前台(未播放) ❌ 不调用 ✅ 确保已调用
电话打断 ❌ 不调用 ✅ 建议调用
其他音频打断 ❌ 不调用 ⚠️ 根据业务逻辑决定
  1. 完整的最佳实践示例
    音频管理器完整实现
@interface AudioManager : NSObject
@property (nonatomic, strong) AVPlayer *player;
@property (nonatomic, assign) BOOL isPlaying;
@property (nonatomic, assign) BOOL isBackground;
@end

@implementation AudioManager

- (instancetype)init {
    self = [super init];
    if (self) {
        [self setupNotifications];
        [self setupAudioSession];
    }
    return self;
}

- (void)setupAudioSession {
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    [session setActive:YES error:nil];
}

- (void)setupNotifications {
    // 监听应用状态变化
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(appDidEnterBackground:)
                                                 name:UIApplicationDidEnterBackgroundNotification
                                               object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(appWillEnterForeground:)
                                                 name:UIApplicationWillEnterForegroundNotification
                                               object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(audioSessionInterrupted:)
                                                 name:AVAudioSessionInterruptionNotification
                                               object:nil];
}

#pragma mark - 播放控制
- (void)play {
    self.isPlaying = YES;
    
    // 关键时机1:播放开始时注册
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    
    [self.player play];
    [self updateNowPlayingInfo];
}

- (void)pause {
    self.isPlaying = NO;
    [self.player pause];
    
    // 暂停时:如果暂停时间较长(如超过5分钟),建议停止接收
    // 这里可以根据业务逻辑决定
    if (self.pauseDuration > 5 * 60) {
        [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    }
}

- (void)stop {
    self.isPlaying = NO;
    [self.player stop];
    
    // 关键时机2:停止播放时取消注册
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:nil];
}

#pragma mark - 应用状态处理
- (void)appDidEnterBackground:(NSNotification *)notification {
    self.isBackground = YES;
    
    if (self.isPlaying) {
        // 后台播放:保持远程控制注册
        // 不需要重复调用begin,只需确保已经调用过
    } else {
        // 关键时机3:进入后台且未播放时停止接收
        [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    }
}

- (void)appWillEnterForeground:(NSNotification *)notification {
    self.isBackground = NO;
    
    if (self.isPlaying) {
        // 回到前台且正在播放:确保远程控制注册
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    } else {
        // 关键时机4:回到前台且未播放时停止接收
        [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    }
}

- (void)audioSessionInterrupted:(NSNotification *)notification {
    NSInteger interruptionType = [notification.userInfo[AVAudioSessionInterruptionTypeKey] integerValue];
    
    if (interruptionType == AVAudioSessionInterruptionTypeBegan) {
        // 音频被打断(如来电)
        if (self.isPlaying) {
            // 关键时机5:音频打断时暂停播放并停止接收
            [self pause];
            [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
        }
    }
    else if (interruptionType == AVAudioSessionInterruptionTypeEnded) {
        // 打断结束
        // 可以根据需要恢复播放和远程控制
    }
}

- (void)dealloc {
    // 关键时机6:对象销毁时确保清理
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end
  1. 特殊情况处理
    多页面音频应用
// 当音频播放分散在多个页面时
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) AudioManager *audioManager;
@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.audioManager = [AudioManager sharedInstance];
    return YES;
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    // 统一由AppDelegate管理远程控制状态
    if ([self.audioManager isPlaying]) {
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    } else {
        [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    }
}

// 其他页面只需要控制播放,不需要单独管理远程控制
@interface PlaylistViewController : UIViewController
- (void)playSong:(Song *)song {
    [[AudioManager sharedInstance] playSong:song];
    // 不需要单独调用beginReceivingRemoteControlEvents
}
@end

错误处理和安全调用

// 安全的远程控制管理
- (void)safeBeginReceivingRemoteControlEvents {
    @try {
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    } @catch (NSException *exception) {
        NSLog(@"beginReceivingRemoteControlEvents 异常: %@", exception);
    }
}

- (void)safeEndReceivingRemoteControlEvents {
    @try {
        [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    } @catch (NSException *exception) {
        NSLog(@"endReceivingRemoteControlEvents 异常: %@", exception);
    }
}

总结

  • 必须调用 beginReceivingRemoteControlEvents 的时机:

    1. 开始播放音频时
    2. 应用进入后台且正在播放音频时
    3. 应用回到前台且正在播放音频时
  • 必须调用 endReceivingRemoteControlEvents 的时机:

    1. 完全停止播放音频时
    2. 应用进入后台且没有播放音频时
    3. 应用回到前台且没有播放音频时
    4. 音频被其他应用打断时
    5. 对象销毁时

最佳实践原则:

  • 配对使用:begin 和 end 应该成对出现
  • 状态同步:远程控制状态应与音频播放状态保持一致
  • 资源管理:及时释放不需要的远程控制资源
  • 统一管理:建议在单一位置集中管理远程控制状态
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容