AVplayer开发音乐模块详解(三)
前言:
在开发每一个模块之前,一定要确定好你们的大致需求,将需求大致罗列出来,然后分析每一个需求。具体的需求的分析,我是借助MindNode实现的,就是将该需求细化到每一个属性,每一个方法,然后罗列每一个方法,根据开放-封闭原则,这些方法尽量颗粒小写,然后在后面的维护的时候便于扩展。最后强调一点一定要把需求明确之后在写代码,不然你就会浪费很多时间,换句话说需要把产品经理问吐!!(小心他揍你哈)
正文:
2、缓存的处理。
首先我们问先认识一个类 AVURLAsset,它有一个方法:
AVURLAsset * asset = [AVURLAsset URLAssetWithURL:url options:nil];
顾名思义,他是管理网络音乐的请求,并且对网络数据打包的AVAssetResourceLoader、AVAssetCache都是和他有关系的。
我的主要的处理方法是参考网上的一位作者的:
1、建立一个AVAResourceLoaderManager类,该类需要遵循AVAssetResourceLoaderDelegate的代理方法,然后在代理方法内部,根据系统的加载数据大小和状态,在进行重新自定义加载和存储。
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest
loadingRequest是AVAssetResourceLoadingRequest类,类中的方法和属性我就不做详细的介绍了,这点需要你自己摸索,只有自己的探索才能更快的掌握和应用(我就是懒!)。这个类中有一个我们需要的属性:
@property (nonatomic, readonly, nullable) AVAssetResourceLoadingDataRequest *dataRequest;
在这个属性中有我们需要的三个主要属性:1、requestedOffset 2、currentOffset 3、requestedLength。
分别对应本次请求的偏移量、本地的偏移量,请求的长度,然后我们根据这三个属性进行再次的请求设置。
看代码:
- (void)addLoadingRequest:(AVAssetResourceLoadingRequest *)loadRequest{
[self.requestList addObject:loadRequest];
@synchronized (self) {
if (self.requestTask) {
if (loadRequest.dataRequest.requestedOffset >= self.requestTask.requestOffset && loadRequest.dataRequest.requestedOffset <= self.requestTask.requestOffset + self.requestTask.cacheLength) {
/* 数据已经缓存,则直接完成 */
NSLog(@"数据已经缓存,则直接完成");
[self processRequestList];
}else{
// 数据还没有缓存,则等待数据下载; 如果是Seek操作,则重新请求
if (self.seekRequired) {
NSLog(@"Seek操作,则完成请求");
[self newTaskWithLoadingRequest:loadRequest cache:NO];}}}}
- (void)processRequestList{
NSMutableArray *finishRequestList = [NSMutableArray array];
for (AVAssetResourceLoadingRequest *loadingRequest in self.requestList) {
if ([self finishLoadingWithLoadingRequest:loadingRequest]) {
[finishRequestList addObject:loadingRequest];}}
[self.requestList removeObjectsInArray:finishRequestList];}
- (BOOL)finishLoadingWithLoadingRequest:(AVAssetResourceLoadingRequest *)loadRequest{
// 填充信息
CFStringRef contentType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)(MimeType), NULL);
loadRequest.contentInformationRequest.contentType = CFBridgingRelease(contentType);
loadRequest.contentInformationRequest.byteRangeAccessSupported = YES;
loadRequest.contentInformationRequest.contentLength = self.requestTask.fileLength;
//读文件,填充数据
NSUInteger cacheLength = self.requestTask.cacheLength;
NSUInteger requestedOffset = loadRequest.dataRequest.requestedOffset;
if (loadRequest.dataRequest.currentOffset != 0) {
requestedOffset = loadRequest.dataRequest.currentOffset;}
NSUInteger canReadLength = cacheLength - (requestedOffset - self.requestTask.requestOffset);
NSUInteger respondLength = MIN(canReadLength, loadRequest.dataRequest.requestedLength);
[loadRequest.dataRequest respondWithData:[AVFileHandle readTempFileDataWithOffset:requestedOffset - self.requestTask.requestOffset length:respondLength]];
// 如果完全响应了所需要的数据,则完成
NSUInteger nowendOffset = requestedOffset + canReadLength;
NSUInteger reqEndOffset = loadRequest.dataRequest.requestedOffset + loadRequest.dataRequest.requestedLength;
if (nowendOffset >= reqEndOffset) {
[loadRequest finishLoading];
return YES;}
return NO;}
- (void)newTaskWithLoadingRequest:(AVAssetResourceLoadingRequest *)loadRequest cache:(BOOL)cache{
NSUInteger fileLength = 0;
if (self.requestTask) {
fileLength = self.requestTask.fileLength;
self.requestTask.cancle = YES;}
self.requestTask = [[AVRequestTask alloc] init];
self.requestTask.requestURL = loadRequest.request.URL;
self.requestTask.requestOffset = loadRequest.dataRequest.requestedOffset;
self.requestTask.cache = cache;
if (fileLength > 0) {
self.requestTask.fileLength = fileLength;}
self.requestTask.delegate = self;
[self.requestTask start];
self.seekRequired = NO;}
这些是处理问题的核心代码,如果有用户全面的了解,请参考这篇文章:
http://blog.csdn.net/minggeqingchun/article/details/52210898
其他的类我就一一介绍了,这里我主要想说的是他的处理缓存的思路
1、开始播放,同时开始下载完整的文件,当文件下载完成时,保存到缓存文件夹中;
2、当seek时,
(1)如果seek到已下载到部分,直接seek成功(已经下载60%,seek进度50%)
(2)如果seek到未下载到的部分,则开始重新下载(如下载进度60%,seek进度70%)
3、当开始新的下载之后,由于文件不完整,下载完成之后不会保存到缓存文件夹中;
4、下次再播放同一首歌曲时,如果在缓存文件夹中存在,则直接播放缓存文件
在处理的过程中,我发现缓存的文件没有后缀,会导致播放失败,因此处理的方式是你在下载的时候直接加上extensionType的后缀,如果下载的文件过程你控制不了那就直接暴力点,在源文件的基础上生成hand link,再加上后缀。
好了,其他的音乐模块的东西基本上是具体需求具体分析了。有任何疑问请在下面留言。