ios开发:一个音乐播放器的设计与实现案例

2020-01-18 16:44:35丽君

 就是一个单例的设计,并没有多大难度。我是用了一个字典来装播放过的歌曲了,这样如果是暂停了,然后再开始播放,就直接在缓存中加载即可。但是如果不注意,在 stopMusic:(NSString *)fileName  这个方法里面,不从字典中移除掉已经停止播放的歌曲,那么你下再播放这首歌的时候,就会在原先播放的进度上继续播放。在编码过程中,我就遇到了这个Bug,然后发现,在切换歌曲(上一曲、下一曲)的时候,我调用的是stopMusic方法,但由于我没有从字典中将它移除,而导致它总是从上一次的进度开始播放,而不是从头开始播放。

如果在真机上想要后台播放歌曲,除了在appDelegate以及plist里面做相应操作之外,还得将播放模式设置为:AVAudioSessionCategoryPlayback。特别需要注意这里,我在模拟器上调试的时候,没有设置这种模式也是可以进行后台播放的,但是在真机上却不行了。后来在StackOverFlow上找到了对应的答案,需要设置播放模式。

这个单例类,在整个demo中是至关重要的,要保证它是没有错误的,所以我写了这个类的XCTest进行单元测试,代码如下:


#import <XCTest/XCTest.h>
#import "ZYAudioManager.h"
#import <AVFoundation/AVFoundation.h>
 
@interface ZYAudioManagerTests : XCTestCase
@property (nonatomic, strong) AVAudioPlayer *player;
@end
static NSString *_fileName = @"10405520.mp3";
@implementation ZYAudioManagerTests
 
- (void)setUp {
  [super setUp];
  // Put setup code here. This method is called before the invocation of each test method in the class.
}
 
- (void)tearDown {
  // Put teardown code here. This method is called after the invocation of each test method in the class.
  [super tearDown];
}
 
- (void)testExample {
  // This is an example of a functional test case.
  // Use XCTAssert and related functions to verify your tests produce the correct results.
}
 
/**
 * 测试是否为单例,要在并发条件下测试
 */
- (void)testAudioManagerSingle
{
  NSMutableArray *managers = [NSMutableArray array];
   
  dispatch_group_t group = dispatch_group_create();
   
  dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    ZYAudioManager *tempManager = [[ZYAudioManager alloc] init];
    [managers addObject:tempManager];
  });
   
  dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    ZYAudioManager *tempManager = [[ZYAudioManager alloc] init];
    [managers addObject:tempManager];
  });
   
  dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    ZYAudioManager *tempManager = [[ZYAudioManager alloc] init];
    [managers addObject:tempManager];
  });
   
  dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    ZYAudioManager *tempManager = [[ZYAudioManager alloc] init];
    [managers addObject:tempManager];
  });
   
  dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    ZYAudioManager *tempManager = [[ZYAudioManager alloc] init];
    [managers addObject:tempManager];
  });
   
  ZYAudioManager *managerOne = [ZYAudioManager defaultManager];
   
  dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     
    [managers enumerateObjectsUsingBlock:^(ZYAudioManager *obj, NSUInteger idx, BOOL * _Nonnull stop) {
      XCTAssertEqual(managerOne, obj, @"ZYAudioManager is not single");
    }];
     
  });
}
 
/**
 * 测试是否可以正常播放音乐
 */
- (void)testPlayingMusic
{
  self.player = [[ZYAudioManager defaultManager] playingMusic:_fileName];
  XCTAssertTrue(self.player.playing, @"ZYAudioManager is not PlayingMusic");
}
 
/**
 * 测试是否可以正常停止音乐
 */
- (void)testStopMusic
{
  if (self.player == nil) {
    self.player = [[ZYAudioManager defaultManager] playingMusic:_fileName];
  }
   
  if (self.player.playing == NO) [self.player play];
   
  [[ZYAudioManager defaultManager] stopMusic:_fileName];
  XCTAssertFalse(self.player.playing, @"ZYAudioManager is not StopMusic");
}
 
/**
 * 测试是否可以正常暂停音乐
 */
- (void)testPauseMusic
{
  if (self.player == nil) {
    self.player = [[ZYAudioManager defaultManager] playingMusic:_fileName];
  }
  if (self.player.playing == NO) [self.player play];
  [[ZYAudioManager defaultManager] pauseMusic:_fileName];
  XCTAssertFalse(self.player.playing, @"ZYAudioManager is not pauseMusic");
}
 
@end