iOS開(kāi)發(fā)筆記網(wǎng)絡(luò)篇-使用AFN的download方法實(shí)現(xiàn)文件的下載(單任務(wù)沉迹、多任務(wù)、斷點(diǎn)下載)

吐槽 o(╯□╰)o

最近在寫(xiě)的一個(gè)項(xiàng)目里面涉及到資源包的下載操作害驹,由于用戶在使用過(guò)程中有可能會(huì)同時(shí)下載多個(gè)資源包胚股,那么就需要對(duì)下載功能進(jìn)行設(shè)計(jì)和封裝。最開(kāi)始使用的是使用AFN的下載方法來(lái)進(jìn)行實(shí)現(xiàn)的裙秋,不過(guò)當(dāng)時(shí)項(xiàng)目需求要求不是太高,所以沒(méi)有處理一些特殊情況缨伊,比如任務(wù)數(shù)量的控制摘刑,優(yōu)先級(jí)還有斷點(diǎn)下載等等。就簡(jiǎn)單的在一個(gè)單例Manager里面添加了一個(gè)字典刻坊,將下載資源包的URL地址作為Key枷恕,對(duì)應(yīng)的downloadTask作為Value,這樣就可以在想要的地方獲取到相應(yīng)的下載進(jìn)度了谭胚。

@property (nonatomic, strong, nonnull) NSMutableDictionary <NSString *, NSURLSessionDownloadTask *> *downloadTaskDict;

由于現(xiàn)在需要統(tǒng)一管理下載的資源徐块,而且為了表示“用戶至上”,比如節(jié)約流量需要使用斷點(diǎn)下載灾而,包括暫停和繼續(xù)胡控、應(yīng)用重啟時(shí)的斷點(diǎn)下載;顯示下載速度和比例等等旁趟。同時(shí)為了控制下載的速度也對(duì)最大下載數(shù)量進(jìn)行了限制昼激,默認(rèn)3個(gè)等等。so.....

silence.jpg
先說(shuō)下原理

這里使用了AFN的下載方法來(lái)完成下載功能锡搜,只不過(guò)需要對(duì)其進(jìn)行一些擴(kuò)展橙困。因此我將每個(gè)下載任務(wù)封裝成了一個(gè)模型<FKNetworkingDownloadModel>,以下為部分屬性定義:


#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

NS_CLASS_AVAILABLE_IOS(7_0) @interface FKNetworkingProgressModel : NSObject 

/*
 * data length of the bytes written.
 */
@property (nonatomic, assign) int64_t totalBytesWritten;
/*
 * the total bytes of the resource data.
 */
@property (nonatomic, assign) int64_t totalBytesExpectedToWrite;
/*
 * download speed.
 */
@property (nonatomic, assign) int64_t downloadSpeed;
/*
 * download progress.
 */
@property (nonatomic, assign) float downloadProgress;
/*
 * download left time
 */
@property (nonatomic, assign) int32_t downloadLeft;
    
@end

NS_CLASS_AVAILABLE_IOS(7_0) @interface FKNetworkingDownloadModel : NSObject

/*
 * resource URLString 
 */
@property (nonatomic, copy) NSString *resourceURLString;
/*
 * fileName default is resourceURLString last component
 */
@property (nonatomic, copy) NSString *fileName;
/**
 *  file directory
 */
@property (nonatomic, copy) NSString *fileDirectory;
/**
 *  file path
 */
@property (nonatomic, copy, nullable) NSString *filePath;
/**
 *  the plist file path, the plist file include information of the download task mission.
 */
@property (nonatomic, copy, nullable) NSString *plistFilePath;
/**
 *  record the download time when receive the data from the serverce, used to calculate download speed
 */
@property (nonatomic, strong) NSDate *downloadDate;
/**
 *  check the download state when a model alloc. 
 */
//@property (nonatomic, assign) FKDownloadModelState modelState;
/**
 *  resume data, marked the position of the download mission.
 */
@property (nonatomic, strong, nullable) NSData *resumeData;
/*
 * download task
 */
@property (nonatomic, strong, nullable) NSURLSessionDownloadTask *downloadTask;
/*
 * progress
 */
@property (nonatomic, strong, nullable) FKNetworkingProgressModel *progressModel;
/**
 *  init method
 *
 *  @param URLString resourceURLString
 *
 *  @return 
 */
-(instancetype)initWithResourceURLString:(NSString *)URLString;

@end

NS_ASSUME_NONNULL_END

這樣可以將每一個(gè)下載的資源封裝成一個(gè)Model,這樣在使用管理類來(lái)控制下載的時(shí)候就會(huì)顯得比較輕松耕餐。

在使用AFN之前需要補(bǔ)充說(shuō)明一下NSURLSessionTask的一些知識(shí):
抽象類(父類):NSURLSessionTask 包含了一些屬性,其中使用到的屬性包括:

//請(qǐng)求信息凡傅,包括URL地址等。
@property (nullable, readonly, copy) NSURLRequest  *originalRequest; 
@property (nullable, readonly, copy) NSURLRequest  *currentRequest;  
//響應(yīng)信息肠缔,包括響應(yīng)數(shù)據(jù)的長(zhǎng)度和地址等等
@property (nullable, readonly, copy) NSURLResponse *response;     
//下面兩個(gè)屬性是會(huì)使用到的夏跷,一個(gè)是已經(jīng)接收到的數(shù)據(jù)長(zhǎng)度和總共需要接受的數(shù)據(jù)長(zhǎng)度,根據(jù)這個(gè)我們可以算出下載的進(jìn)度信息   
@property (readonly) int64_t countOfBytesReceived;
@property (readonly) int64_t countOfBytesExpectedToReceive;
//任務(wù)描述明未,我這里用model對(duì)應(yīng)的URL地址來(lái)賦值
@property (nullable, copy) NSString *taskDescription;
//狀態(tài)(取消拓春、完成、進(jìn)行中等)
@property (readonly) NSURLSessionTaskState state;

同時(shí)父類也提供了幾個(gè)方法

- (void)cancel;//取消下載任務(wù)
- (void)suspend;//掛起(這個(gè)一般用于程序在運(yùn)行中的時(shí)候暫停下載任務(wù))
- (void)resume;//恢復(fù)下載任務(wù)

理論上說(shuō)亚隅,如果不做應(yīng)用重啟后的斷點(diǎn)下載硼莽,通過(guò)上面的步驟已經(jīng)可以完成大部分的下載操作了,但是在程序重啟時(shí)并不能夠獲取到對(duì)應(yīng)的已下載的進(jìn)度(downloadTask會(huì)將下載的資源放入tmp路徑下面,以'CFNetworking'開(kāi)頭)懂鸵,那這個(gè)時(shí)候執(zhí)行下載任務(wù)那么會(huì)重新下載偏螺。考慮到用戶的流量匆光,就必須在重啟后的進(jìn)行斷點(diǎn)下載套像,因此我們需要使用到子類NSURLSessionDownloadTask的一個(gè)方法

- (void)cancelByProducingResumeData:(void (^)(NSData * __nullable resumeData))completionHandler;

這個(gè)方法可以講當(dāng)前的task已下載的數(shù)據(jù)使用一個(gè)data保存起來(lái), 然后使用NSURLSession的方法可以根據(jù)這個(gè)data從服務(wù)器獲取后續(xù)的數(shù)據(jù)

- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;

斷點(diǎn)下載的原理大概就是這樣,接下來(lái)是考慮管理類的設(shè)計(jì)和封裝终息。

管理類的設(shè)計(jì)和封裝

思路:使用單例來(lái)管理下載任務(wù)夺巩,同時(shí)可以指定最大的下載數(shù)和單任務(wù)和多任務(wù)模式的切換运悲。關(guān)于下載的控制(開(kāi)始西设、暫停、恢復(fù)和斷點(diǎn)下載)都可以通過(guò)AFN相應(yīng)的方法來(lái)實(shí)現(xiàn)

ANF里面關(guān)于下載有幾個(gè)方法哥遮,在<AFURLSessionManager>類里面可以找到
//這個(gè)方法是經(jīng)常用到的一個(gè)续镇,生成一個(gè)NSURLRequest美澳,然后根據(jù)這個(gè)請(qǐng)求開(kāi)始下載任務(wù)可以追蹤到下載的進(jìn)度,需要設(shè)置下載完成后的文件移動(dòng)到的位置摸航,最后是完成后的回調(diào)信息
-(NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
                                             progress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                          destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                    completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;

//該方法根據(jù)一個(gè)resumeData來(lái)進(jìn)行斷點(diǎn)下載制跟,我這里斷點(diǎn)下載的實(shí)現(xiàn)就是根據(jù)這個(gè)來(lái)的,resumeData已經(jīng)在模型里面了酱虎。
在下載cancel或者suspend的時(shí)候只需要將model的resumeData賦值雨膨,然后寫(xiě)入本地plist文件,下次下載的時(shí)候根據(jù)plist信息獲取下載的一些信息就OK了
-(NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
                                                progress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                             destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                       completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;

下面附上一頭文件中的一些代碼

#import <Foundation/Foundation.h>
#import "AFNetworking.h"
#import "FKNetworkingDownloadModel.h"

NS_ASSUME_NONNULL_BEGIN

NS_CLASS_AVAILABLE_IOS(7_0) @interface FKNetworkingManager : NSObject

/**
 *  download root file path. default is '~/cache/forkid.networking.manager1.0'
 */
@property (nonatomic, copy, readonly) NSString *downloadDirectory;

/**
 *  contains the models whose is downloading.   
 */
@property (nonatomic, strong, readonly) NSMutableArray <__kindof FKNetworkingDownloadModel *> *downloadingModels;

/**
 *  contains the models whose is waiting for download.   
 */
@property (nonatomic, strong, readonly) NSMutableArray <__kindof FKNetworkingDownloadModel *> *waitingModels;

/**
 *  max download mission number.
 */
@property (nonatomic, assign) NSInteger maxDownloadCount;

/**
 *  first in and fisrt out. 
 */
@property (nonatomic, assign) BOOL resumeTaskFIFO;

/**
 *  ignore maxDownloadCount. manager will resume all downloadTask.
 */
@property (nonatomic, assign, getter=isBatchDownload) BOOL batchDownload;

/**
  *  singleton
  */
+(FKNetworkingManager *)shareManager;

/**
 ===============> Description of download start <=============
 = when you wanna download a file from serverce, you can use next methods as you need. 
 = there have some methods such as start, resume, cancel and so on.
 = We add the 'cancel' method to pause a task instead of suspend, because we need to resume the task when the App restart.
 = all methods will use a download model who is subclass of the 'FKNetworkingDownloadModel' for convenience. 
 = and every model use a URL string as the primary key to mark a download mission or task.
 ===============> Description of download end. <=============
 */

/**
 *  this method used to start a download mission with a download model, notice that the download model can't be nil, or the download mission will not execute.
 *
 *  @param downloadModel     download model
 *  @param progress          progress of the download, track to refresh UI and ...
 *  @param completionHandler you can doing something after download mission completed, such as refresh your UI and move the file and so on.
 */
-(void)fk_startDownloadWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel 
                                progress:(void (^)(FKNetworkingDownloadModel *downloadModel))progress
                       completionHandler:(void (^)(FKNetworkingDownloadModel *downloadModel, NSError * _Nullable error))completionHandler;

/**
 *  resume a download task with a download model, it will use the download model's 'resumeData'
 *
 *  @param downloadModel download model 
 */
-(void)fk_resumeDownloadWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel;                          

/**
 *  suspend or cancel a download task
 *
 *  @param downloadModel download model
 */
-(void)fk_cancelDownloadTaskWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel;

/**
 *  check the resourece has been downloaded or not from the download model resourceURL.
 *
 *  @param downloadModel download model
 *
 *  @return YES or NO.
 */
-(BOOL)fk_hasDownloadedFileWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel; 

/**
 *  delete local file if exist.
 *
 *  @param downloadModel download model.
 */
-(void)fk_deleteDownloadedFileWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel;

/**
 *  delete all downloaded files.
 */
-(void)fk_deleteAllDownloadedFiles;

/**
 *  get a download model, which is downloading with a URLString. if there is not exist a model, will return nil.
 *
 *  @param URLString URLString.
 *
 *  @return download model
 */
-(nullable FKNetworkingDownloadModel *)fk_getDownloadingModelWithURLString:(NSString *)URLString;

/**
 *  get download progress information. such as download speed, progress and so on.
 *
 *  @param downloadModel download model.
 *
 *  @return progress model.
 */
-(nullable FKNetworkingProgressModel *)fk_getDownloadProgressModelWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel;
@end
NS_ASSUME_NONNULL_END

定義完上述的方法后读串,在實(shí)現(xiàn)文件中實(shí)現(xiàn)相應(yīng)的方法即可哥放,因?yàn)椴捎肁FN,所以我們不用寫(xiě)太多的步驟爹土,只需要關(guān)心流程控制甥雕,和線程控制就行了。

下面是部分的實(shí)現(xiàn)代碼:

#import "FKNetworkingManager.h"
#import "NSObject+FKAdd.h"

NSString *const FKNetworkingManagerFileName = @"forkid.networking.manager1.0";

@interface FKNetworkingManager ()

/**
 *  AFNetworking manager.
 */
@property (nonatomic, strong) AFHTTPSessionManager *AFManager;

/**
 *  download root directory.
 */
@property (nonatomic, copy) NSString *downloadDirectory;

/**
 *  fileManager to manage download files
 */
@property (nonatomic, strong) NSFileManager *fileManager;

/*
 * the models for waiting for download, the elements should be FKDownloadModel and it's subClasses
 */
@property (nonatomic, strong) NSMutableArray <__kindof FKNetworkingDownloadModel *> *waitingModels;

/*
 * the models whose being downloaded, the elements should be FKDownloadModel and it's subClasses
 */
@property (nonatomic, strong) NSMutableArray <__kindof FKNetworkingDownloadModel *> *downloadingModels;

/*
 *  key-values dictionary of the downloadModels, format as '<NSString *key, FKDownloadModel *model>' to make constraints
 *  used to find a downloadModel from this container, 
 *  when the program will terminate, container will be clear
 */
@property (nonatomic, strong) NSMutableDictionary <NSString *, __kindof FKNetworkingDownloadModel *> *downloadModelsDict;

@end

NSInteger const fk_timeInterval = 5;

@implementation FKNetworkingManager

#pragma mark - download methods

-(void)fk_startDownloadWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel 
                                progress:(void (^)(FKNetworkingDownloadModel * _Nonnull))progress 
                       completionHandler:(void (^)(FKNetworkingDownloadModel * _Nonnull, NSError * _Nullable))completionHandler{

    NSString *fileName = [downloadModel.fileName componentsSeparatedByString:@"."].firstObject;
    downloadModel.fileDirectory = [self.downloadDirectory stringByAppendingPathComponent:fileName];
    downloadModel.filePath = [[self.downloadDirectory stringByAppendingPathComponent:fileName] stringByAppendingPathComponent:downloadModel.fileName];
    downloadModel.plistFilePath = [downloadModel.fileDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", fileName]];
    
    if (![self canBeStartDownloadTaskWithDownloadModel:downloadModel]) return;
    
    downloadModel.resumeData = [NSData dataWithContentsOfFile:downloadModel.plistFilePath];
    
    if (downloadModel.resumeData.length == 0) {
        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadModel.resourceURLString]];
        downloadModel.downloadTask = [self.AFManager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
            
            [self setValuesForDownloadModel:downloadModel withProgress:downloadProgress.fractionCompleted];
            progress(downloadModel);
            
        } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
            
            return [NSURL fileURLWithPath:downloadModel.filePath];
            
        } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
            if (error) {
                [self fk_cancelDownloadTaskWithDownloadModel:downloadModel];
                completionHandler(downloadModel, error);
            }else{
                [self.downloadModelsDict removeObjectForKey:downloadModel.resourceURLString];
                completionHandler(downloadModel, nil);
                [self deletePlistFileWithDownloadModel:downloadModel];
            }
        }];
        
    }else{
        
        downloadModel.progressModel.totalBytesWritten = [self getResumeByteWithDownloadModel:downloadModel];
        downloadModel.downloadTask = [self.AFManager downloadTaskWithResumeData:downloadModel.resumeData progress:^(NSProgress * _Nonnull downloadProgress) {
            
            [self setValuesForDownloadModel:downloadModel withProgress:[self.AFManager downloadProgressForTask:downloadModel.downloadTask].fractionCompleted];
            progress(downloadModel);
            
        } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
            return [NSURL fileURLWithPath:downloadModel.filePath];
        } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
            if (error) {
                [self fk_cancelDownloadTaskWithDownloadModel:downloadModel];
                completionHandler(downloadModel, error);
            }else{
                [self.downloadModelsDict removeObjectForKey:downloadModel.resourceURLString];
                completionHandler(downloadModel, nil);
                [self deletePlistFileWithDownloadModel:downloadModel];
            }
        }];
    }
    
    if (![self.fileManager fileExistsAtPath:self.downloadDirectory]) {
        [self.fileManager createDirectoryAtPath:self.downloadDirectory withIntermediateDirectories:YES attributes:nil error:nil];
    }
    
    [self createFolderAtPath:[self.downloadDirectory stringByAppendingPathComponent:fileName]];
    [self fk_resumeDownloadWithDownloadModel:downloadModel];
}

-(void)fk_resumeDownloadWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
    if (downloadModel.downloadTask) {
        downloadModel.downloadDate = [NSDate date];
        [downloadModel.downloadTask resume];
        self.downloadModelsDict[downloadModel.resourceURLString] = downloadModel;
        [self.downloadingModels addObject:downloadModel];
    }
}

-(void)fk_cancelDownloadTaskWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
    if (!downloadModel) return;
    NSURLSessionTaskState state = downloadModel.downloadTask.state;
    if (state == NSURLSessionTaskStateRunning) {
        [downloadModel.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
            downloadModel.resumeData = resumeData;
            @synchronized (self) {
                BOOL isSuc = [downloadModel.resumeData writeToFile:downloadModel.plistFilePath atomically:YES];
                [self saveTotalBytesExpectedToWriteWithDownloadModel:downloadModel];
                if (isSuc) {
                    downloadModel.resumeData = nil;
                    [self.downloadModelsDict removeObjectForKey:downloadModel.resourceURLString];
                    [self.downloadingModels removeObject:downloadModel];
                }
            }
        }];
    }
}

-(void)fk_deleteDownloadedFileWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
    if ([self.fileManager fileExistsAtPath:downloadModel.fileDirectory]) {
        [self.fileManager removeItemAtPath:downloadModel.fileDirectory error:nil];
    }
}

-(void)fk_deleteAllDownloadedFiles{
    if ([self.fileManager fileExistsAtPath:self.downloadDirectory]) {
        [self.fileManager removeItemAtPath:self.downloadDirectory error:nil];
    }
}

-(BOOL)fk_hasDownloadedFileWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
    if ([self.fileManager fileExistsAtPath:downloadModel.filePath]) {
        NSLog(@"已下載的文件...");
        return YES;
    }
    return NO;
}

-(FKNetworkingDownloadModel *)fk_getDownloadingModelWithURLString:(NSString *)URLString{
    return self.downloadModelsDict[URLString];
}

-(FKNetworkingProgressModel *)fk_getDownloadProgressModelWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
    FKNetworkingProgressModel *progressModel = downloadModel.progressModel;
    progressModel.downloadProgress = [self.AFManager downloadProgressForTask:downloadModel.downloadTask].fractionCompleted;
    return progressModel;
}

#pragma mark - private methods
-(BOOL)canBeStartDownloadTaskWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
    if (!downloadModel) return NO;
    if (downloadModel.downloadTask && downloadModel.downloadTask.state == NSURLSessionTaskStateRunning) return NO;
    if ([self fk_hasDownloadedFileWithDownloadModel:downloadModel]) return NO;
    return YES;
}

-(void)setValuesForDownloadModel:(FKNetworkingDownloadModel *)downloadModel withProgress:(double)progress{
    NSTimeInterval interval = -1 * [downloadModel.downloadDate timeIntervalSinceNow];
    downloadModel.progressModel.totalBytesWritten = downloadModel.downloadTask.countOfBytesReceived;
    downloadModel.progressModel.totalBytesExpectedToWrite = downloadModel.downloadTask.countOfBytesExpectedToReceive;
    downloadModel.progressModel.downloadProgress = progress;
    downloadModel.progressModel.downloadSpeed = (int64_t)((downloadModel.progressModel.totalBytesWritten - [self getResumeByteWithDownloadModel:downloadModel]) / interval);
    if (downloadModel.progressModel.downloadSpeed != 0) {
        int64_t remainingContentLength = downloadModel.progressModel.totalBytesExpectedToWrite  - downloadModel.progressModel.totalBytesWritten;    
        int currentLeftTime = (int)(remainingContentLength / downloadModel.progressModel.downloadSpeed);
        downloadModel.progressModel.downloadLeft = currentLeftTime;
    }
}

-(int64_t)getResumeByteWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
    int64_t resumeBytes = 0;
    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:downloadModel.plistFilePath];
    if (dict) {
        resumeBytes = [dict[@"NSURLSessionResumeBytesReceived"] longLongValue]; 
    }
    return resumeBytes;
}

-(NSString *)getTmpFileNameWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
    NSString *fileName = nil;
    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:downloadModel.plistFilePath];
    if (dict) {
        fileName = dict[@"NSURLSessionResumeInfoTempFileName"]; 
    }
    return fileName;
}

-(void)createFolderAtPath:(NSString *)path{
    if ([self.fileManager fileExistsAtPath:path]) return;
    [self.fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
}

-(void)deletePlistFileWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
    if (downloadModel.downloadTask.countOfBytesReceived == downloadModel.downloadTask.countOfBytesExpectedToReceive) {
        [self.fileManager removeItemAtPath:downloadModel.plistFilePath error:nil];
        [self removeTotalBytesExpectedToWriteWhenDownloadFinishedWithDownloadModel:downloadModel];
    }
}

-(NSString *)managerPlistFilePath{
    return [self.downloadDirectory stringByAppendingPathComponent:@"ForKidManager.plist"];
}

-(nullable NSMutableDictionary <NSString *, NSString *> *)managerPlistDict{
    NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:[self managerPlistFilePath]];
    return dict;
}

-(void)saveTotalBytesExpectedToWriteWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
    NSMutableDictionary <NSString *, NSString *> *dict = [self managerPlistDict];
    [dict setValue:[NSString stringWithFormat:@"%lld", downloadModel.downloadTask.countOfBytesExpectedToReceive] forKey:downloadModel.resourceURLString];
    [dict writeToFile:[self managerPlistFilePath] atomically:YES];
}

-(void)removeTotalBytesExpectedToWriteWhenDownloadFinishedWithDownloadModel:(FKNetworkingDownloadModel *)downloadModel{
    NSMutableDictionary <NSString *, NSString *> *dict = [self managerPlistDict];
    [dict removeObjectForKey:downloadModel.resourceURLString];
    [dict writeToFile:[self managerPlistFilePath] atomically:YES];
}

#pragma mark - share instance
+(FKNetworkingManager *)shareManager{
    static FKNetworkingManager *manager = nil;
    static dispatch_once_t sigletonOnceToken;
    dispatch_once(&sigletonOnceToken, ^{
        manager = [[self alloc] init];
    });
    return manager;
}

- (instancetype)init{
    self = [super init];
    if (self) {
        _AFManager = [[AFHTTPSessionManager alloc]init];
        _AFManager.requestSerializer.timeoutInterval = 5;
        _AFManager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;//NSURLRequestUseProtocolCachePolicy;
        NSSet *typeSet = [NSSet setWithObjects:@"application/json", @"text/plain", @"text/javascript", @"text/json", @"text/html", nil];
        _AFManager.responseSerializer.acceptableContentTypes = typeSet;
        _AFManager.securityPolicy.allowInvalidCertificates = YES;
        
        _maxDownloadCount = 1;
        _resumeTaskFIFO = YES;
        _batchDownload = NO;
        _fileManager = [NSFileManager defaultManager];
        _waitingModels = [[NSMutableArray alloc] initWithCapacity:1];
        _downloadingModels = [[NSMutableArray alloc] initWithCapacity:1];
        _downloadModelsDict = [[NSMutableDictionary alloc] initWithCapacity:1];
        
        _downloadDirectory = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:FKNetworkingManagerFileName];
        [_fileManager createDirectoryAtPath:_downloadDirectory withIntermediateDirectories:YES attributes:nil error:nil];
        
        NSDictionary <NSString *, NSString *> *plistDict = [[NSDictionary alloc] init];
        NSString *managerPlistFilePath = [_downloadDirectory stringByAppendingPathComponent:@"ForKidManager.plist"];
        [plistDict writeToFile:managerPlistFilePath atomically:YES];
    }
    return self;
}
    
@end

如有問(wèn)題或者BUG 歡迎指正

前段時(shí)間由于工作上的事情耽擱了胀茵,所以很多朋友問(wèn)的Demo遲遲沒(méi)有上傳上去社露,這段時(shí)間我會(huì)完善當(dāng)前的功能整理下傳上去,簡(jiǎn)要的修復(fù)一點(diǎn)小bug和完善一下上傳和POST方法以及網(wǎng)絡(luò)請(qǐng)求管理琼娘。

未完待續(xù)...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末峭弟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子脱拼,更是在濱河造成了極大的恐慌瞒瘸,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件熄浓,死亡現(xiàn)場(chǎng)離奇詭異情臭,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)俯在,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)竟秫,“玉大人,你說(shuō)我怎么就攤上這事跷乐》拾埽” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵愕提,是天一觀的道長(zhǎng)馒稍。 經(jīng)常有香客問(wèn)我,道長(zhǎng)浅侨,這世上最難降的妖魔是什么纽谒? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮仗颈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘椎例。我一直安慰自己挨决,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布订歪。 她就那樣靜靜地躺著脖祈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪刷晋。 梳的紋絲不亂的頭發(fā)上盖高,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音眼虱,去河邊找鬼喻奥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛捏悬,可吹牛的內(nèi)容都是我干的撞蚕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼过牙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼甥厦!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起寇钉,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤刀疙,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后扫倡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體谦秧,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了油够。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚁袭。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖石咬,靈堂內(nèi)的尸體忽然破棺而出揩悄,到底是詐尸還是另有隱情,我是刑警寧澤鬼悠,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布删性,位于F島的核電站,受9級(jí)特大地震影響焕窝,放射性物質(zhì)發(fā)生泄漏蹬挺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一它掂、第九天 我趴在偏房一處隱蔽的房頂上張望巴帮。 院中可真熱鬧,春花似錦虐秋、人聲如沸榕茧。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)用押。三九已至,卻和暖如春靶剑,著一層夾襖步出監(jiān)牢的瞬間蜻拨,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工桩引, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缎讼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓坑匠,卻偏偏與公主長(zhǎng)得像休涤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子笛辟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容