1.网络请求

第一种实现方式:

  功能:GET POST 请求

缓存逻辑:

  1.是否要刷新本地缓存,不需要就直接发起无缓存的网络请求,否则直接读取本地数据

  2.需要刷新本地缓存,先读取本地数据,有就返回,没有就发起缓存的网络请求

  3.无网络时直接读取本地缓存

#import "AFHTTPSessionManager.h"

/**
*该类默认只要导入头文件就会自动检测网络状态,且会在没有网络和未知网络的时候,自动从本地数据库中读取缓存。
*数据库网络缓存是基于猿题库公司对FMDB进行封装的轻量级 key-value 存储框架
*详情请见 https://github.com/yuantiku/YTKKeyValueStore
*对该类如有疑问可以拉个issues
*/
@interface JMHttpRequestMethod : AFHTTPSessionManager typedef NS_ENUM(NSUInteger, JMRequestSerializer) {
JMRequestSerializerJSON, // 设置请求数据为JSON格式
JMRequestSerializerPlainText // 设置请求数据为普通 text/html
}; typedef NS_ENUM(NSUInteger, JMResponseSerializer) {
JMResponseSerializerJSON, // 设置响应数据为JSON格式
JMResponseSerializerHTTP, // 设置响应数据为二进制格式
JMResponseSerializerXML // 设置响应数据为XML格式
}; #pragma mark - 程序入口设置网络请求头API 一般调用一次即可 /**
设置 请求和响应类型和超时时间
@param requestType 默认为请求类型为JSON格式
@param responseType 默认响应格式为JSON格式
@param timeOut 请求超时时间 默认为20秒
*/
+(void)setTimeOutWithTime:(NSTimeInterval)timeOut
requestType:(JMRequestSerializer)requestType
responseType:(JMResponseSerializer)responseType; /**
设置 请求头
@param httpBody 根据服务器要求 配置相应的请求体
*/
+ (void)setHttpBodyWithDic:(NSDictionary *)httpBody; #pragma mark - 网络工具 API
/**
获取当前的网络状态 @return YES 有网 NO 没有联网
*/
+(BOOL)getCurrentNetWorkStatus; /**
获取网络缓存 文件大小
@return size 单位M 默认保留两位小数 如: 0.12M
*/
+ (NSString *)fileSizeWithDBPath;
/**
清除所有网络缓存
*/
+ (void)cleanNetWorkRefreshCache; #pragma mark - GET 请求API /**
GET 请求 不用传参 API
@param url 请求的url
@param refreshCache 是否对该页面进行缓存
@param success 请求成功回调
@param fail 请求失败回调
@return self
*/
+ (JMHttpRequestMethod *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail; /**
GET 请求 传参数的API
@param url 请求的url
@param refreshCache 是否对该页面进行缓存
@param params 请求数据向服务器传的参数
@param success 请求成功回调
@param fail 请求失败回调
@return self
*/
+ (JMHttpRequestMethod *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail; /**
GET 请求 带有进度回调的 API
@param url 请求的url
@param refreshCache 是否对该页面进行缓存
@param params 请求数据向服务器传的参数
@param progress 请求进度回调
@param success 请求成功回调
@param fail 请求失败回调
@return self
*/
+ (JMHttpRequestMethod *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(void(^)(int64_t bytesRead, int64_t totalBytesRead))progress
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail; #pragma mark - POST 请求API /**
POST 请求API
@param url 请求的url
@param refreshCache 是否对该页面进行缓存
@param params 请求数据向服务器传的参数
@param success 请求成功回调
@param fail 请求失败回调
@return self
*/
+ (JMHttpRequestMethod *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail; /**
POST 请求 带有进度回调的 API @param url 请求的url
@param refreshCache 是否对该页面进行缓存
@param params 请求数据向服务器传的参数
@param progress 请求进度回调
@param success 请求成功回调
@param fail 请求失败回调 @return self
*/
+ (JMHttpRequestMethod *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(void(^)(int64_t bytesRead, int64_t totalBytesRead))progress
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail; @end
#import "JMHttpRequestMethod.h"
#import "YTKKeyValueStore.h" #define PATH_OF_NetWork [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] typedef NS_ENUM(NSUInteger, JMNetworkStatus) {
JMNetworkStatusUnknown, //未知的网络
JMNetworkStatusNotNetWork, //没有网络
JMNetworkStatusReachableViaWWAN,//手机蜂窝数据网络
JMNetworkStatusReachableViaWiFi //WIFI 网络
}; @interface JMHttpRequestMethod ()
@end @implementation JMHttpRequestMethod static NSString *const httpCache = @"NetworkCache";
static YTKKeyValueStore *_store;
static JMNetworkStatus _status;
static BOOL _isHasNetWork; + (void)load
{
JMHttpRequestMethod *httpMethod;
httpMethod.requestSerializer = [AFJSONRequestSerializer serializer];
//设置请求的超时时间
httpMethod.requestSerializer.timeoutInterval = .f;
//设置服务器返回结果的类型:JSON (AFJSONResponseSerializer,AFHTTPResponseSerializer)
httpMethod.responseSerializer = [AFJSONResponseSerializer serializer]; httpMethod.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:
@"application/json",
@"text/html",
@"text/json",
@"text/plain",
@"text/javascript",
@"text/xml", @"image/*", nil];
[self startMonitoringNetworkStatus];
} /**
监测网络状态 (在程序入口,调用一次即可)
*/
+ (void)startMonitoringNetworkStatus
{
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status)
{
case AFNetworkReachabilityStatusUnknown:
_isHasNetWork = NO;
_status = JMNetworkStatusUnknown;
break;
case AFNetworkReachabilityStatusNotReachable:
_isHasNetWork = NO;
_status = JMNetworkStatusNotNetWork;
NSLog(@"没有网的状态");
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
_isHasNetWork = YES;
_status = JMNetworkStatusReachableViaWWAN;
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
_isHasNetWork = YES;
_status = JMNetworkStatusReachableViaWiFi;
NSLog(@"现在是有网状态");
break;
}
}];
[manager startMonitoring];
}
/**
设置 请求和响应类型和超时时间 @param requestType 默认为请求类型为JSON格式
@param responseType 默认响应格式为JSON格式
@param timeOut 请求超时时间 默认为20秒
*/
+(void)setTimeOutWithTime:(NSTimeInterval)timeOut
requestType:(JMRequestSerializer)requestType
responseType:(JMResponseSerializer)responseType
{ JMHttpRequestMethod *httpMethod;
httpMethod.requestSerializer.timeoutInterval = timeOut;
switch (requestType) {
case JMRequestSerializerJSON:
httpMethod.requestSerializer = [AFJSONRequestSerializer serializer];
break;
case JMRequestSerializerPlainText:
httpMethod.requestSerializer = [AFHTTPRequestSerializer serializer];
break;
default:
break;
}
switch (responseType) {
case JMResponseSerializerJSON:
httpMethod.responseSerializer = [AFJSONResponseSerializer serializer];
break;
case JMResponseSerializerHTTP:
httpMethod.responseSerializer = [AFHTTPResponseSerializer serializer];
break;
case JMResponseSerializerXML:
httpMethod.responseSerializer = [AFXMLParserResponseSerializer serializer];
default:
break;
}
}
/**
设置 请求头 @param httpBody 根据服务器要求 配置相应的请求体
*/
+ (void)setHttpBodyWithDic:(NSDictionary *)httpBody
{
JMHttpRequestMethod *httpMethod;
for (NSString *key in httpBody.allKeys) {
if (httpBody[key] != nil) {
[httpMethod.requestSerializer setValue:httpBody[key] forHTTPHeaderField:key];
}
}
} /**
获取当前的网络状态 @return YES 有网 NO 没有联网
*/
+(BOOL)getCurrentNetWorkStatus
{
return _isHasNetWork;
} /**
获取网络缓存 文件大小 @return size 单位M
*/
+ (NSString *)fileSizeWithDBPath
{
NSFileManager* manager = [NSFileManager defaultManager];
if ([manager fileExistsAtPath:[PATH_OF_NetWork stringByAppendingPathComponent:httpCache]]){
unsigned long long fileSize = [[manager attributesOfItemAtPath:[PATH_OF_NetWork stringByAppendingPathComponent:httpCache] error:nil] fileSize];
NSString *size = [NSString stringWithFormat:@"%.2fM",fileSize/1024.0/1024.0];
return size;
}else {
return @"0M";
}
return ;
} /**
清除所有网络缓存
*/
+ (void)cleanNetWorkRefreshCache
{
NSError *error;
BOOL isSuccess = [[NSFileManager defaultManager]removeItemAtPath:[PATH_OF_NetWork stringByAppendingPathComponent:httpCache] error:&error];
if (isSuccess) {
NSLog(@"clean cache file is success");
}else {
if ([PATH_OF_NetWork stringByAppendingPathComponent:httpCache]) {
NSLog(@"error:%@",error.description);
}else {
NSLog(@"error: cache file is not exist");
} }
} #pragma mark - /**************GET 请求API ******************/ + (JMHttpRequestMethod *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail
{
return [self getWithUrl:url refreshCache:refreshCache params:nil success:success fail:fail];
}
// 多一个params参数
+ (JMHttpRequestMethod *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail
{
return [self getWithUrl:url refreshCache:refreshCache params:params progress:nil success:success fail:fail];
}
// 多一个带进度回调
+ (JMHttpRequestMethod *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(void(^)(int64_t bytesRead, int64_t totalBytesRead))progress
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail
{
_store = [[YTKKeyValueStore alloc] initDBWithName:httpCache];
[_store createTableWithName:httpCache];
JMHttpRequestMethod *request = nil;
if ([JMHttpRequestMethod getCurrentNetWorkStatus]) {
if (!refreshCache) {
[self requestNotCacheWithHttpMethod: url:url params:params progress:progress success:success fail:fail];
}else {
NSDictionary *dict = [_store getObjectById:url fromTable:httpCache];
if (dict) {
success(dict);
}else {
[[self manager] GET:url parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[_store putObject:responseObject withId:url intoTable:httpCache];
success(responseObject);
NSLog(@"\nRequest success, URL: %@\n params:%@\n response:%@\n\n",url,params,responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
fail(error);
NSLog(@"error = %@",error.description);
}];
}
}
} else {
NSDictionary *dict = [_store getObjectById:url fromTable:httpCache];
if (dict) {
success(dict);
}else {
NSLog(@"当前为无网络状态,本地也没有缓存数据");
}
}
return request;
}
#pragma mark - /*********************** POST 请求API **********************/ + (JMHttpRequestMethod *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail
{
return [self postWithUrl:url refreshCache:refreshCache params:params progress:nil success:success fail:fail];
} + (JMHttpRequestMethod *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(void(^)(int64_t bytesRead, int64_t totalBytesRead))progress
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail
{
JMHttpRequestMethod *request = nil;
if ([JMHttpRequestMethod getCurrentNetWorkStatus]) {
if (!refreshCache) {
[self requestNotCacheWithHttpMethod: url:url params:params progress:progress success:success fail:fail];
}else {
NSDictionary *dict = [_store getObjectById:url fromTable:httpCache];
if (dict) {
success(dict);
}else {
[[self manager] POST:url parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[_store putObject:responseObject withId:url intoTable:httpCache];
success(responseObject);
NSLog(@"\nRequest success, URL: %@\n params:%@\n response:%@\n\n",url,params,responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
fail(error);
}];
}
}
} else {
NSDictionary *dict = [_store getObjectById:url fromTable:httpCache];
if (dict) {
success(dict);
}else {
NSLog(@"当前为无网络状态,本地也没有缓存数据");
}
} return request;
}
+ (void)requestNotCacheWithHttpMethod:(NSInteger)httpMethod
url:(NSString *)url
params:(NSDictionary *)params
progress:(void(^)(int64_t bytesRead, int64_t totalBytesRead))progress
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail
{
if (httpMethod == ) {
[[self manager]GET:url parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
success(responseObject);
NSLog(@"\nRequest success, URL: %@\n params:%@\n response:%@\n\n",url,params,responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
fail(error);
}];
}else {
[[self manager ]POST:url parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {
progress(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
success(responseObject);
NSLog(@"\nRequest success, URL: %@\n params:%@\n response:%@\n\n",url,params,responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
fail(error);
}];
}
} @end

2.数据缓存(FMDB)

#import <Foundation/Foundation.h>

@interface YTKKeyValueItem : NSObject

@property (strong, nonatomic) NSString *itemId;
@property (strong, nonatomic) id itemObject;
@property (strong, nonatomic) NSDate *createdTime; @end @interface YTKKeyValueStore : NSObject - (id)initDBWithName:(NSString *)dbName; - (id)initWithDBWithPath:(NSString *)dbPath; - (void)createTableWithName:(NSString *)tableName; - (void)clearTable:(NSString *)tableName; - (void)close; ///************************ Put&Get methods ***************************************** - (void)putObject:(id)object withId:(NSString *)objectId intoTable:(NSString *)tableName; - (id)getObjectById:(NSString *)objectId fromTable:(NSString *)tableName; - (YTKKeyValueItem *)getYTKKeyValueItemById:(NSString *)objectId fromTable:(NSString *)tableName; - (void)putString:(NSString *)string withId:(NSString *)stringId intoTable:(NSString *)tableName; - (NSString *)getStringById:(NSString *)stringId fromTable:(NSString *)tableName; - (void)putNumber:(NSNumber *)number withId:(NSString *)numberId intoTable:(NSString *)tableName; - (NSNumber *)getNumberById:(NSString *)numberId fromTable:(NSString *)tableName; - (NSArray *)getAllItemsFromTable:(NSString *)tableName; - (void)deleteObjectById:(NSString *)objectId fromTable:(NSString *)tableName; - (void)deleteObjectsByIdArray:(NSArray *)objectIdArray fromTable:(NSString *)tableName; - (void)deleteObjectsByIdPrefix:(NSString *)objectIdPrefix fromTable:(NSString *)tableName; @end
#import "YTKKeyValueStore.h"
#import "FMDatabase.h"
#import "FMDatabaseQueue.h" #ifdef DEBUG
#define debugLog(s, ...) NSLog( @"[%@ in line %d] ➡️➡️➡️%@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#define debugMethod() NSLog(@"%s", __func__)
#define debugError(s, ...) NSLog( @"[%@ in line %d] ➡️➡️➡️%@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
#define debugLog(s,...)
#define debugMethod()
#define debugError(s, ...)
#endif #define PATH_OF_DOCUMENT [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] @implementation YTKKeyValueItem - (NSString *)description {
return [NSString stringWithFormat:@"id=%@, value=%@, timeStamp=%@", _itemId, _itemObject, _createdTime];
} @end @interface YTKKeyValueStore() @property (strong, nonatomic) FMDatabaseQueue * dbQueue; @end @implementation YTKKeyValueStore static NSString *const DEFAULT_DB_NAME = @"database.sqlite"; static NSString *const CREATE_TABLE_SQL =
@"CREATE TABLE IF NOT EXISTS %@ ( \
id TEXT NOT NULL, \
json TEXT NOT NULL, \
createdTime TEXT NOT NULL, \
PRIMARY KEY(id)) \
"; static NSString *const UPDATE_ITEM_SQL = @"REPLACE INTO %@ (id, json, createdTime) values (?, ?, ?)"; static NSString *const QUERY_ITEM_SQL = @"SELECT json, createdTime from %@ where id = ? Limit 1"; static NSString *const SELECT_ALL_SQL = @"SELECT * from %@"; static NSString *const CLEAR_ALL_SQL = @"DELETE from %@"; static NSString *const DELETE_ITEM_SQL = @"DELETE from %@ where id = ?"; static NSString *const DELETE_ITEMS_SQL = @"DELETE from %@ where id in ( %@ )"; static NSString *const DELETE_ITEMS_WITH_PREFIX_SQL = @"DELETE from %@ where id like ? "; + (BOOL)checkTableName:(NSString *)tableName {
if (tableName == nil || tableName.length == || [tableName rangeOfString:@" "].location != NSNotFound) {
debugLog(@"ERROR, table name: %@ format error.", tableName);
return NO;
}
return YES;
} - (id)init {
return [self initDBWithName:DEFAULT_DB_NAME];
} - (id)initDBWithName:(NSString *)dbName {
self = [super init];
if (self) {
NSString * dbPath = [PATH_OF_DOCUMENT stringByAppendingPathComponent:dbName];
debugLog(@"dbPath = %@", dbPath);
if (_dbQueue) {
[self close];
}
_dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
}
return self;
} - (id)initWithDBWithPath:(NSString *)dbPath {
self = [super init];
if (self) {
debugLog(@"dbPath = %@", dbPath);
if (_dbQueue) {
[self close];
}
_dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
}
return self;
} - (void)createTableWithName:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return;
}
NSString * sql = [NSString stringWithFormat:CREATE_TABLE_SQL, tableName];
__block BOOL result;
[_dbQueue inDatabase:^(FMDatabase *db) {
result = [db executeUpdate:sql];
}];
if (!result) {
debugLog(@"ERROR, failed to create table: %@", tableName);
}
} - (void)clearTable:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return;
}
NSString * sql = [NSString stringWithFormat:CLEAR_ALL_SQL, tableName];
__block BOOL result;
[_dbQueue inDatabase:^(FMDatabase *db) {
result = [db executeUpdate:sql];
}];
if (!result) {
debugLog(@"ERROR, failed to clear table: %@", tableName);
}
} - (void)putObject:(id)object withId:(NSString *)objectId intoTable:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return;
}
NSError * error;
NSData * data = [NSJSONSerialization dataWithJSONObject:object options: error:&error];
if (error) {
debugLog(@"ERROR, faild to get json data");
return;
}
NSString * jsonString = [[NSString alloc] initWithData:data encoding:(NSUTF8StringEncoding)];
NSDate * createdTime = [NSDate date];
NSString * sql = [NSString stringWithFormat:UPDATE_ITEM_SQL, tableName];
__block BOOL result;
[_dbQueue inDatabase:^(FMDatabase *db) {
result = [db executeUpdate:sql, objectId, jsonString, createdTime];
}];
if (!result) {
debugLog(@"ERROR, failed to insert/replace into table: %@", tableName);
}
} - (id)getObjectById:(NSString *)objectId fromTable:(NSString *)tableName {
YTKKeyValueItem * item = [self getYTKKeyValueItemById:objectId fromTable:tableName];
if (item) {
return item.itemObject;
} else {
return nil;
}
} - (YTKKeyValueItem *)getYTKKeyValueItemById:(NSString *)objectId fromTable:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return nil;
}
NSString * sql = [NSString stringWithFormat:QUERY_ITEM_SQL, tableName];
__block NSString * json = nil;
__block NSDate * createdTime = nil;
[_dbQueue inDatabase:^(FMDatabase *db) {
FMResultSet * rs = [db executeQuery:sql, objectId];
if ([rs next]) {
json = [rs stringForColumn:@"json"];
createdTime = [rs dateForColumn:@"createdTime"];
}
[rs close];
}];
if (json) {
NSError * error;
id result = [NSJSONSerialization JSONObjectWithData:[json dataUsingEncoding:NSUTF8StringEncoding]
options:(NSJSONReadingAllowFragments) error:&error];
if (error) {
debugLog(@"ERROR, faild to prase to json");
return nil;
}
YTKKeyValueItem * item = [[YTKKeyValueItem alloc] init];
item.itemId = objectId;
item.itemObject = result;
item.createdTime = createdTime;
return item;
} else {
return nil;
}
} - (void)putString:(NSString *)string withId:(NSString *)stringId intoTable:(NSString *)tableName {
if (string == nil) {
debugLog(@"error, string is nil");
return;
}
[self putObject:@[string] withId:stringId intoTable:tableName];
} - (NSString *)getStringById:(NSString *)stringId fromTable:(NSString *)tableName {
NSArray * array = [self getObjectById:stringId fromTable:tableName];
if (array && [array isKindOfClass:[NSArray class]]) {
return array[];
}
return nil;
} - (void)putNumber:(NSNumber *)number withId:(NSString *)numberId intoTable:(NSString *)tableName {
if (number == nil) {
debugLog(@"error, number is nil");
return;
}
[self putObject:@[number] withId:numberId intoTable:tableName];
} - (NSNumber *)getNumberById:(NSString *)numberId fromTable:(NSString *)tableName {
NSArray * array = [self getObjectById:numberId fromTable:tableName];
if (array && [array isKindOfClass:[NSArray class]]) {
return array[];
}
return nil;
} - (NSArray *)getAllItemsFromTable:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return nil;
}
NSString * sql = [NSString stringWithFormat:SELECT_ALL_SQL, tableName];
__block NSMutableArray * result = [NSMutableArray array];
[_dbQueue inDatabase:^(FMDatabase *db) {
FMResultSet * rs = [db executeQuery:sql];
while ([rs next]) {
YTKKeyValueItem * item = [[YTKKeyValueItem alloc] init];
item.itemId = [rs stringForColumn:@"id"];
item.itemObject = [rs stringForColumn:@"json"];
item.createdTime = [rs dateForColumn:@"createdTime"];
[result addObject:item];
}
[rs close];
}];
// parse json string to object
NSError * error;
for (YTKKeyValueItem * item in result) {
error = nil;
id object = [NSJSONSerialization JSONObjectWithData:[item.itemObject dataUsingEncoding:NSUTF8StringEncoding]
options:(NSJSONReadingAllowFragments) error:&error];
if (error) {
debugLog(@"ERROR, faild to prase to json.");
} else {
item.itemObject = object;
}
}
return result;
} - (void)deleteObjectById:(NSString *)objectId fromTable:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return;
}
NSString * sql = [NSString stringWithFormat:DELETE_ITEM_SQL, tableName];
__block BOOL result;
[_dbQueue inDatabase:^(FMDatabase *db) {
result = [db executeUpdate:sql, objectId];
}];
if (!result) {
debugLog(@"ERROR, failed to delete item from table: %@", tableName);
}
} - (void)deleteObjectsByIdArray:(NSArray *)objectIdArray fromTable:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return;
}
NSMutableString *stringBuilder = [NSMutableString string];
for (id objectId in objectIdArray) {
NSString *item = [NSString stringWithFormat:@" '%@' ", objectId];
if (stringBuilder.length == ) {
[stringBuilder appendString:item];
} else {
[stringBuilder appendString:@","];
[stringBuilder appendString:item];
}
}
NSString *sql = [NSString stringWithFormat:DELETE_ITEMS_SQL, tableName, stringBuilder];
__block BOOL result;
[_dbQueue inDatabase:^(FMDatabase *db) {
result = [db executeUpdate:sql];
}];
if (!result) {
debugLog(@"ERROR, failed to delete items by ids from table: %@", tableName);
}
} - (void)deleteObjectsByIdPrefix:(NSString *)objectIdPrefix fromTable:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return;
}
NSString *sql = [NSString stringWithFormat:DELETE_ITEMS_WITH_PREFIX_SQL, tableName];
NSString *prefixArgument = [NSString stringWithFormat:@"%@%%", objectIdPrefix];
__block BOOL result;
[_dbQueue inDatabase:^(FMDatabase *db) {
result = [db executeUpdate:sql, prefixArgument];
}];
if (!result) {
debugLog(@"ERROR, failed to delete items by id prefix from table: %@", tableName);
}
} - (void)close {
[_dbQueue close];
_dbQueue = nil;
} @end

3.使用示例

[JMHttpRequestMethod getWithUrl:url1 refreshCache:YES success:^(id responseObject) {

        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:responseObject options:NSJSONWritingPrettyPrinted error:nil];
self.textView.text = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; // [JMHttpRequestMethod cleanNetWorkRefreshCache];
NSLog(@"缓存大小为%@", [JMHttpRequestMethod fileSizeWithDBPath]);
} fail:^(NSError *error) { }];

第二种实现方式:

  功能:POST GET Upload download

#import "HYBNetworking.h"
#import "AFNetworkActivityIndicatorManager.h"
#import "AFNetworking.h"
#import "AFHTTPSessionManager.h" #import <CommonCrypto/CommonDigest.h> @interface NSString (md5) + (NSString *)hybnetworking_md5:(NSString *)string; @end @implementation NSString (md5) + (NSString *)hybnetworking_md5:(NSString *)string {
if (string == nil || [string length] == ) {
return nil;
} unsigned char digest[CC_MD5_DIGEST_LENGTH], i;
CC_MD5([string UTF8String], (int)[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding], digest);
NSMutableString *ms = [NSMutableString string]; for (i = ; i < CC_MD5_DIGEST_LENGTH; i++) {
[ms appendFormat:@"%02x", (int)(digest[i])];
} return [ms copy];
} @end static NSString *sg_privateNetworkBaseUrl = nil;
static BOOL sg_isEnableInterfaceDebug = NO;
static BOOL sg_shouldAutoEncode = NO;
static NSDictionary *sg_httpHeaders = nil;
static HYBResponseType sg_responseType = kHYBResponseTypeJSON;
static HYBRequestType sg_requestType = kHYBRequestTypePlainText;
static HYBNetworkStatus sg_networkStatus = kHYBNetworkStatusReachableViaWiFi;
static NSMutableArray *sg_requestTasks;
static BOOL sg_cacheGet = YES;
static BOOL sg_cachePost = NO;
static BOOL sg_shouldCallbackOnCancelRequest = YES;
static NSTimeInterval sg_timeout = 60.0f;
static BOOL sg_shoulObtainLocalWhenUnconnected = NO;
static BOOL sg_isBaseURLChanged = YES;
static AFHTTPSessionManager *sg_sharedManager = nil;
static NSUInteger sg_maxCacheSize = ; @implementation HYBNetworking + (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 尝试清除缓存
if (sg_maxCacheSize > && [self totalCacheSize] > * * sg_maxCacheSize) {
[self clearCaches];
}
});
} + (void)autoToClearCacheWithLimitedToSize:(NSUInteger)mSize {
sg_maxCacheSize = mSize;
} + (void)cacheGetRequest:(BOOL)isCacheGet shoulCachePost:(BOOL)shouldCachePost {
sg_cacheGet = isCacheGet;
sg_cachePost = shouldCachePost;
} + (void)updateBaseUrl:(NSString *)baseUrl {
if (![baseUrl isEqualToString:sg_privateNetworkBaseUrl] && baseUrl && baseUrl.length) {
sg_isBaseURLChanged = YES;
} else {
sg_isBaseURLChanged = NO;
} sg_privateNetworkBaseUrl = baseUrl;
} + (NSString *)baseUrl {
return sg_privateNetworkBaseUrl;
} + (void)setTimeout:(NSTimeInterval)timeout {
sg_timeout = timeout;
} + (void)obtainDataFromLocalWhenNetworkUnconnected:(BOOL)shouldObtain {
sg_shoulObtainLocalWhenUnconnected = shouldObtain;
if (sg_shoulObtainLocalWhenUnconnected && (sg_cacheGet || sg_cachePost)) {
[self detectNetwork];
}
} + (void)enableInterfaceDebug:(BOOL)isDebug {
sg_isEnableInterfaceDebug = isDebug;
} + (BOOL)isDebug {
return sg_isEnableInterfaceDebug;
} static inline NSString *cachePath() {
return [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/HYBNetworkingCaches"];
} + (void)clearCaches {
NSString *directoryPath = cachePath(); if ([[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) {
NSError *error = nil;
[[NSFileManager defaultManager] removeItemAtPath:directoryPath error:&error]; if (error) {
NSLog(@"HYBNetworking clear caches error: %@", error);
} else {
NSLog(@"HYBNetworking clear caches ok");
}
}
} + (unsigned long long)totalCacheSize {
NSString *directoryPath = cachePath();
BOOL isDir = NO;
unsigned long long total = ; if ([[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:&isDir]) {
if (isDir) {
NSError *error = nil;
NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryPath error:&error]; if (error == nil) {
for (NSString *subpath in array) {
NSString *path = [directoryPath stringByAppendingPathComponent:subpath];
NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:path
error:&error];
if (!error) {
total += [dict[NSFileSize] unsignedIntegerValue];
}
}
}
}
} return total;
} + (NSMutableArray *)allTasks {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (sg_requestTasks == nil) {
sg_requestTasks = [[NSMutableArray alloc] init];
}
}); return sg_requestTasks;
} + (void)cancelAllRequest {
@synchronized(self) {
[[self allTasks] enumerateObjectsUsingBlock:^(HYBURLSessionTask * _Nonnull task, NSUInteger idx, BOOL * _Nonnull stop) {
if ([task isKindOfClass:[HYBURLSessionTask class]]) {
[task cancel];
}
}]; [[self allTasks] removeAllObjects];
};
} + (void)cancelRequestWithURL:(NSString *)url {
if (url == nil) {
return;
} @synchronized(self) {
[[self allTasks] enumerateObjectsUsingBlock:^(HYBURLSessionTask * _Nonnull task, NSUInteger idx, BOOL * _Nonnull stop) {
if ([task isKindOfClass:[HYBURLSessionTask class]]
&& [task.currentRequest.URL.absoluteString hasSuffix:url]) {
[task cancel];
[[self allTasks] removeObject:task];
return;
}
}];
};
} + (void)configRequestType:(HYBRequestType)requestType
responseType:(HYBResponseType)responseType
shouldAutoEncodeUrl:(BOOL)shouldAutoEncode
callbackOnCancelRequest:(BOOL)shouldCallbackOnCancelRequest {
sg_requestType = requestType;
sg_responseType = responseType;
sg_shouldAutoEncode = shouldAutoEncode;
sg_shouldCallbackOnCancelRequest = shouldCallbackOnCancelRequest;
} + (BOOL)shouldEncode {
return sg_shouldAutoEncode;
} + (void)configCommonHttpHeaders:(NSDictionary *)httpHeaders {
sg_httpHeaders = httpHeaders;
} + (HYBURLSessionTask *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
return [self getWithUrl:url
refreshCache:refreshCache
params:nil
success:success
fail:fail];
} + (HYBURLSessionTask *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
return [self getWithUrl:url
refreshCache:refreshCache
params:params
progress:nil
success:success
fail:fail];
} + (HYBURLSessionTask *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(HYBGetProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
return [self _requestWithUrl:url
refreshCache:refreshCache
httpMedth:
params:params
progress:progress
success:success
fail:fail];
} + (HYBURLSessionTask *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
return [self postWithUrl:url
refreshCache:refreshCache
params:params
progress:nil
success:success
fail:fail];
} + (HYBURLSessionTask *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(HYBPostProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
return [self _requestWithUrl:url
refreshCache:refreshCache
httpMedth:
params:params
progress:progress
success:success
fail:fail];
} + (HYBURLSessionTask *)_requestWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
httpMedth:(NSUInteger)httpMethod
params:(NSDictionary *)params
progress:(HYBDownloadProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
if ([self shouldEncode]) {
url = [self encodeUrl:url];
} AFHTTPSessionManager *manager = [self manager];
NSString *absolute = [self absoluteUrlWithPath:url]; if ([self baseUrl] == nil) {
if ([NSURL URLWithString:url] == nil) {
HYBAppLog(@"URLString无效,无法生成URL。可能是URL中有中文,请尝试Encode URL");
return nil;
}
} else {
NSURL *absoluteURL = [NSURL URLWithString:absolute]; if (absoluteURL == nil) {
HYBAppLog(@"URLString无效,无法生成URL。可能是URL中有中文,请尝试Encode URL");
return nil;
}
} HYBURLSessionTask *session = nil; if (httpMethod == ) {
if (sg_cacheGet) {
if (sg_shoulObtainLocalWhenUnconnected) {
if (sg_networkStatus == kHYBNetworkStatusNotReachable || sg_networkStatus == kHYBNetworkStatusUnknown ) {
id response = [HYBNetworking cahceResponseWithURL:absolute
parameters:params];
if (response) {
if (success) {
[self successResponse:response callback:success]; if ([self isDebug]) {
[self logWithSuccessResponse:response
url:absolute
params:params];
}
}
return nil;
}
}
}
if (!refreshCache) {// 获取缓存
id response = [HYBNetworking cahceResponseWithURL:absolute
parameters:params];
if (response) {
if (success) {
[self successResponse:response callback:success]; if ([self isDebug]) {
[self logWithSuccessResponse:response
url:absolute
params:params];
}
}
return nil;
}
}
} session = [manager GET:url parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
if (progress) {
progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
}
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[self successResponse:responseObject callback:success]; if (sg_cacheGet) {
[self cacheResponseObject:responseObject request:task.currentRequest parameters:params];
} [[self allTasks] removeObject:task]; if ([self isDebug]) {
[self logWithSuccessResponse:responseObject
url:absolute
params:params];
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[[self allTasks] removeObject:task]; if ([error code] < && sg_cacheGet) {// 获取缓存
id response = [HYBNetworking cahceResponseWithURL:absolute
parameters:params];
if (response) {
if (success) {
[self successResponse:response callback:success]; if ([self isDebug]) {
[self logWithSuccessResponse:response
url:absolute
params:params];
}
}
} else {
[self handleCallbackWithError:error fail:fail]; if ([self isDebug]) {
[self logWithFailError:error url:absolute params:params];
}
}
} else {
[self handleCallbackWithError:error fail:fail]; if ([self isDebug]) {
[self logWithFailError:error url:absolute params:params];
}
}
}];
} else if (httpMethod == ) {
if (sg_cachePost ) {// 获取缓存
if (sg_shoulObtainLocalWhenUnconnected) {
if (sg_networkStatus == kHYBNetworkStatusNotReachable || sg_networkStatus == kHYBNetworkStatusUnknown ) {
id response = [HYBNetworking cahceResponseWithURL:absolute
parameters:params];
if (response) {
if (success) {
[self successResponse:response callback:success]; if ([self isDebug]) {
[self logWithSuccessResponse:response
url:absolute
params:params];
}
}
return nil;
}
}
}
if (!refreshCache) {
id response = [HYBNetworking cahceResponseWithURL:absolute
parameters:params];
if (response) {
if (success) {
[self successResponse:response callback:success]; if ([self isDebug]) {
[self logWithSuccessResponse:response
url:absolute
params:params];
}
}
return nil;
}
}
} session = [manager POST:url parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
if (progress) {
progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
}
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[self successResponse:responseObject callback:success]; if (sg_cachePost) {
[self cacheResponseObject:responseObject request:task.currentRequest parameters:params];
} [[self allTasks] removeObject:task]; if ([self isDebug]) {
[self logWithSuccessResponse:responseObject
url:absolute
params:params];
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[[self allTasks] removeObject:task]; if ([error code] < && sg_cachePost) {// 获取缓存
id response = [HYBNetworking cahceResponseWithURL:absolute
parameters:params]; if (response) {
if (success) {
[self successResponse:response callback:success]; if ([self isDebug]) {
[self logWithSuccessResponse:response
url:absolute
params:params];
}
}
} else {
[self handleCallbackWithError:error fail:fail]; if ([self isDebug]) {
[self logWithFailError:error url:absolute params:params];
}
}
} else {
[self handleCallbackWithError:error fail:fail]; if ([self isDebug]) {
[self logWithFailError:error url:absolute params:params];
}
}
}];
} if (session) {
[[self allTasks] addObject:session];
} return session;
} + (HYBURLSessionTask *)uploadFileWithUrl:(NSString *)url
uploadingFile:(NSString *)uploadingFile
progress:(HYBUploadProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
if ([NSURL URLWithString:uploadingFile] == nil) {
HYBAppLog(@"uploadingFile无效,无法生成URL。请检查待上传文件是否存在");
return nil;
} NSURL *uploadURL = nil;
if ([self baseUrl] == nil) {
uploadURL = [NSURL URLWithString:url];
} else {
uploadURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", [self baseUrl], url]];
} if (uploadURL == nil) {
HYBAppLog(@"URLString无效,无法生成URL。可能是URL中有中文或特殊字符,请尝试Encode URL");
return nil;
} AFHTTPSessionManager *manager = [self manager];
NSURLRequest *request = [NSURLRequest requestWithURL:uploadURL];
HYBURLSessionTask *session = nil; [manager uploadTaskWithRequest:request fromFile:[NSURL URLWithString:uploadingFile] progress:^(NSProgress * _Nonnull uploadProgress) {
if (progress) {
progress(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
}
} completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
[[self allTasks] removeObject:session]; [self successResponse:responseObject callback:success]; if (error) {
[self handleCallbackWithError:error fail:fail]; if ([self isDebug]) {
[self logWithFailError:error url:response.URL.absoluteString params:nil];
}
} else {
if ([self isDebug]) {
[self logWithSuccessResponse:responseObject
url:response.URL.absoluteString
params:nil];
}
}
}]; if (session) {
[[self allTasks] addObject:session];
} return session;
} + (HYBURLSessionTask *)uploadWithImage:(UIImage *)image
url:(NSString *)url
filename:(NSString *)filename
name:(NSString *)name
mimeType:(NSString *)mimeType
parameters:(NSDictionary *)parameters
progress:(HYBUploadProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
if ([self baseUrl] == nil) {
if ([NSURL URLWithString:url] == nil) {
HYBAppLog(@"URLString无效,无法生成URL。可能是URL中有中文,请尝试Encode URL");
return nil;
}
} else {
if ([NSURL URLWithString:[NSString stringWithFormat:@"%@%@", [self baseUrl], url]] == nil) {
HYBAppLog(@"URLString无效,无法生成URL。可能是URL中有中文,请尝试Encode URL");
return nil;
}
} if ([self shouldEncode]) {
url = [self encodeUrl:url];
} NSString *absolute = [self absoluteUrlWithPath:url]; AFHTTPSessionManager *manager = [self manager];
HYBURLSessionTask *session = [manager POST:url parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
NSData *imageData = UIImageJPEGRepresentation(image, ); NSString *imageFileName = filename;
if (filename == nil || ![filename isKindOfClass:[NSString class]] || filename.length == ) {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyyMMddHHmmss";
NSString *str = [formatter stringFromDate:[NSDate date]];
imageFileName = [NSString stringWithFormat:@"%@.jpg", str];
} // 上传图片,以文件流的格式
[formData appendPartWithFileData:imageData name:name fileName:imageFileName mimeType:mimeType];
} progress:^(NSProgress * _Nonnull uploadProgress) {
if (progress) {
progress(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
}
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[[self allTasks] removeObject:task];
[self successResponse:responseObject callback:success]; if ([self isDebug]) {
[self logWithSuccessResponse:responseObject
url:absolute
params:parameters];
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[[self allTasks] removeObject:task]; [self handleCallbackWithError:error fail:fail]; if ([self isDebug]) {
[self logWithFailError:error url:absolute params:nil];
}
}]; [session resume];
if (session) {
[[self allTasks] addObject:session];
} return session;
} + (HYBURLSessionTask *)downloadWithUrl:(NSString *)url
saveToPath:(NSString *)saveToPath
progress:(HYBDownloadProgress)progressBlock
success:(HYBResponseSuccess)success
failure:(HYBResponseFail)failure {
if ([self baseUrl] == nil) {
if ([NSURL URLWithString:url] == nil) {
HYBAppLog(@"URLString无效,无法生成URL。可能是URL中有中文,请尝试Encode URL");
return nil;
}
} else {
if ([NSURL URLWithString:[NSString stringWithFormat:@"%@%@", [self baseUrl], url]] == nil) {
HYBAppLog(@"URLString无效,无法生成URL。可能是URL中有中文,请尝试Encode URL");
return nil;
}
} NSURLRequest *downloadRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
AFHTTPSessionManager *manager = [self manager]; HYBURLSessionTask *session = nil; session = [manager downloadTaskWithRequest:downloadRequest progress:^(NSProgress * _Nonnull downloadProgress) {
if (progressBlock) {
progressBlock(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
}
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
return [NSURL fileURLWithPath:saveToPath];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
[[self allTasks] removeObject:session]; if (error == nil) {
if (success) {
success(filePath.absoluteString);
} if ([self isDebug]) {
HYBAppLog(@"Download success for url %@",
[self absoluteUrlWithPath:url]);
}
} else {
[self handleCallbackWithError:error fail:failure]; if ([self isDebug]) {
HYBAppLog(@"Download fail for url %@, reason : %@",
[self absoluteUrlWithPath:url],
[error description]);
}
}
}]; [session resume];
if (session) {
[[self allTasks] addObject:session];
} return session;
} #pragma mark - Private
+ (AFHTTPSessionManager *)manager {
@synchronized (self) {
// 只要不切换baseurl,就一直使用同一个session manager
if (sg_sharedManager == nil || sg_isBaseURLChanged) {
// 开启转圈圈
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES; AFHTTPSessionManager *manager = nil;;
if ([self baseUrl] != nil) {
    #warning session 容易造成循环引用 delegate 是强引用类型 解决方法:1.通过单例的方式创建 2.delloc中释放delegate
manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:[self baseUrl]]];
} else {
manager = [AFHTTPSessionManager manager];
} switch (sg_requestType) {
case kHYBRequestTypeJSON: {
manager.requestSerializer = [AFJSONRequestSerializer serializer];
break;
}
case kHYBRequestTypePlainText: {
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
break;
}
default: {
break;
}
} switch (sg_responseType) {
case kHYBResponseTypeJSON: {
manager.responseSerializer = [AFJSONResponseSerializer serializer];
break;
}
case kHYBResponseTypeXML: {
manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
break;
}
case kHYBResponseTypeData: {
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
break;
}
default: {
break;
}
} manager.requestSerializer.stringEncoding = NSUTF8StringEncoding; for (NSString *key in sg_httpHeaders.allKeys) {
if (sg_httpHeaders[key] != nil) {
[manager.requestSerializer setValue:sg_httpHeaders[key] forHTTPHeaderField:key];
}
} manager.responseSerializer.acceptableContentTypes = [NSSet setWithArray:@[@"application/json",
@"text/html",
@"text/json",
@"text/plain",
@"text/javascript",
@"text/xml",
@"image/*"]]; manager.requestSerializer.timeoutInterval = sg_timeout; // 设置允许同时最大并发数量,过大容易出问题
manager.operationQueue.maxConcurrentOperationCount = ;
sg_sharedManager = manager;
}
} return sg_sharedManager;
} + (void)detectNetwork {
AFNetworkReachabilityManager *reachabilityManager = [AFNetworkReachabilityManager sharedManager]; [reachabilityManager startMonitoring];
[reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
if (status == AFNetworkReachabilityStatusNotReachable){
sg_networkStatus = kHYBNetworkStatusNotReachable;
} else if (status == AFNetworkReachabilityStatusUnknown){
sg_networkStatus = kHYBNetworkStatusUnknown;
} else if (status == AFNetworkReachabilityStatusReachableViaWWAN){
sg_networkStatus = kHYBNetworkStatusReachableViaWWAN;
} else if (status == AFNetworkReachabilityStatusReachableViaWiFi){
sg_networkStatus = kHYBNetworkStatusReachableViaWiFi;
}
}];
} + (void)logWithSuccessResponse:(id)response url:(NSString *)url params:(NSDictionary *)params {
HYBAppLog(@"\n");
HYBAppLog(@"\nRequest success, URL: %@\n params:%@\n response:%@\n\n",
[self generateGETAbsoluteURL:url params:params],
params,
[self tryToParseData:response]);
} + (void)logWithFailError:(NSError *)error url:(NSString *)url params:(id)params {
NSString *format = @" params: ";
if (params == nil || ![params isKindOfClass:[NSDictionary class]]) {
format = @"";
params = @"";
} HYBAppLog(@"\n");
if ([error code] == NSURLErrorCancelled) {
HYBAppLog(@"\nRequest was canceled mannully, URL: %@ %@%@\n\n",
[self generateGETAbsoluteURL:url params:params],
format,
params);
} else {
HYBAppLog(@"\nRequest error, URL: %@ %@%@\n errorInfos:%@\n\n",
[self generateGETAbsoluteURL:url params:params],
format,
params,
[error localizedDescription]);
}
} // 仅对一级字典结构起作用
+ (NSString *)generateGETAbsoluteURL:(NSString *)url params:(id)params {
if (params == nil || ![params isKindOfClass:[NSDictionary class]] || [params count] == ) {
return url;
} NSString *queries = @"";
for (NSString *key in params) {
id value = [params objectForKey:key]; if ([value isKindOfClass:[NSDictionary class]]) {
continue;
} else if ([value isKindOfClass:[NSArray class]]) {
continue;
} else if ([value isKindOfClass:[NSSet class]]) {
continue;
} else {
queries = [NSString stringWithFormat:@"%@%@=%@&",
(queries.length == ? @"&" : queries),
key,
value];
}
} if (queries.length > ) {
queries = [queries substringToIndex:queries.length - ];
} if (([url hasPrefix:@"http://"] || [url hasPrefix:@"https://"]) && queries.length > ) {
if ([url rangeOfString:@"?"].location != NSNotFound
|| [url rangeOfString:@"#"].location != NSNotFound) {
url = [NSString stringWithFormat:@"%@%@", url, queries];
} else {
queries = [queries substringFromIndex:];
url = [NSString stringWithFormat:@"%@?%@", url, queries];
}
} return url.length == ? queries : url;
} + (NSString *)encodeUrl:(NSString *)url {
return [self hyb_URLEncode:url];
} + (id)tryToParseData:(id)responseData {
if ([responseData isKindOfClass:[NSData class]]) {
// 尝试解析成JSON
if (responseData == nil) {
return responseData;
} else {
NSError *error = nil;
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseData
options:NSJSONReadingMutableContainers
error:&error]; if (error != nil) {
return responseData;
} else {
return response;
}
}
} else {
return responseData;
}
} + (void)successResponse:(id)responseData callback:(HYBResponseSuccess)success {
if (success) {
success([self tryToParseData:responseData]);
}
} + (NSString *)hyb_URLEncode:(NSString *)url {
return [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; // 采用下面的方式反而不能请求成功
// NSString *newString =
// CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
// (CFStringRef)url,
// NULL,
// CFSTR(":/?#[]@!$ &'()*+,;=\"<>%{}|\\^~`"), CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)));
// if (newString) {
// return newString;
// }
//
// return url;
} + (id)cahceResponseWithURL:(NSString *)url parameters:params {
id cacheData = nil; if (url) {
// Try to get datas from disk
NSString *directoryPath = cachePath();
NSString *absoluteURL = [self generateGETAbsoluteURL:url params:params];
NSString *key = [NSString hybnetworking_md5:absoluteURL];
NSString *path = [directoryPath stringByAppendingPathComponent:key]; NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];
if (data) {
cacheData = data;
HYBAppLog(@"Read data from cache for url: %@\n", url);
}
} return cacheData;
} + (void)cacheResponseObject:(id)responseObject request:(NSURLRequest *)request parameters:params {
if (request && responseObject && ![responseObject isKindOfClass:[NSNull class]]) {
NSString *directoryPath = cachePath(); NSError *error = nil; if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) {
[[NSFileManager defaultManager] createDirectoryAtPath:directoryPath
withIntermediateDirectories:YES
attributes:nil
error:&error];
if (error) {
HYBAppLog(@"create cache dir error: %@\n", error);
return;
}
} NSString *absoluteURL = [self generateGETAbsoluteURL:request.URL.absoluteString params:params];
NSString *key = [NSString hybnetworking_md5:absoluteURL];
NSString *path = [directoryPath stringByAppendingPathComponent:key];
NSDictionary *dict = (NSDictionary *)responseObject; NSData *data = nil;
if ([dict isKindOfClass:[NSData class]]) {
data = responseObject;
} else {
data = [NSJSONSerialization dataWithJSONObject:dict
options:NSJSONWritingPrettyPrinted
error:&error];
} if (data && error == nil) {
BOOL isOk = [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
if (isOk) {
HYBAppLog(@"cache file ok for request: %@\n", absoluteURL);
} else {
HYBAppLog(@"cache file error for request: %@\n", absoluteURL);
}
}
}
} + (NSString *)absoluteUrlWithPath:(NSString *)path {
if (path == nil || path.length == ) {
return @"";
} if ([self baseUrl] == nil || [[self baseUrl] length] == ) {
return path;
} NSString *absoluteUrl = path; if (![path hasPrefix:@"http://"] && ![path hasPrefix:@"https://"]) {
if ([[self baseUrl] hasSuffix:@"/"]) {
if ([path hasPrefix:@"/"]) {
NSMutableString * mutablePath = [NSMutableString stringWithString:path];
[mutablePath deleteCharactersInRange:NSMakeRange(, )];
absoluteUrl = [NSString stringWithFormat:@"%@%@",
[self baseUrl], mutablePath];
} else {
absoluteUrl = [NSString stringWithFormat:@"%@%@",[self baseUrl], path];
}
} else {
if ([path hasPrefix:@"/"]) {
absoluteUrl = [NSString stringWithFormat:@"%@%@",[self baseUrl], path];
} else {
absoluteUrl = [NSString stringWithFormat:@"%@/%@",
[self baseUrl], path];
}
}
} return absoluteUrl;
} + (void)handleCallbackWithError:(NSError *)error fail:(HYBResponseFail)fail {
if ([error code] == NSURLErrorCancelled) {
if (sg_shouldCallbackOnCancelRequest) {
if (fail) {
fail(error);
}
}
} else {
if (fail) {
fail(error);
}
}
} @end
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h> // 项目打包上线都不会打印日志,因此可放心。
#ifdef DEBUG
#define HYBAppLog(s, ... ) NSLog( @"[%@ in line %d] ===============>%@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
#define HYBAppLog(s, ... )
#endif /*!
* @author 黄, 16-01-08 14:01:26
*
* 下载进度
*
* @param bytesRead 已下载的大小
* @param totalBytesRead 文件总大小
* @param totalBytesExpectedToRead 还有多少需要下载
*/
typedef void (^HYBDownloadProgress)(int64_t bytesRead,
int64_t totalBytesRead); typedef HYBDownloadProgress HYBGetProgress;
typedef HYBDownloadProgress HYBPostProgress; /*!
* @author 黄, 16-01-08 14:01:26
*
* 上传进度
*
* @param bytesWritten 已上传的大小
* @param totalBytesWritten 总上传大小
*/
typedef void (^HYBUploadProgress)(int64_t bytesWritten,
int64_t totalBytesWritten); typedef NS_ENUM(NSUInteger, HYBResponseType) {
kHYBResponseTypeJSON = , // 默认
kHYBResponseTypeXML = , // XML
// 特殊情况下,一转换服务器就无法识别的,默认会尝试转换成JSON,若失败则需要自己去转换
kHYBResponseTypeData =
}; typedef NS_ENUM(NSUInteger, HYBRequestType) {
kHYBRequestTypeJSON = , // 默认
kHYBRequestTypePlainText = // 普通text/html
}; typedef NS_ENUM(NSInteger, HYBNetworkStatus) {
kHYBNetworkStatusUnknown = -,//未知网络
kHYBNetworkStatusNotReachable = ,//网络无连接
kHYBNetworkStatusReachableViaWWAN = ,//2,3,4G网络
kHYBNetworkStatusReachableViaWiFi = ,//WIFI网络
}; @class NSURLSessionTask; // 请勿直接使用NSURLSessionDataTask,以减少对第三方的依赖
// 所有接口返回的类型都是基类NSURLSessionTask,若要接收返回值
// 且处理,请转换成对应的子类类型
typedef NSURLSessionTask HYBURLSessionTask;
typedef void(^HYBResponseSuccess)(id response);
typedef void(^HYBResponseFail)(NSError *error); /*!
* @author huangyibiao, 15-11-15 13:11:31
*
* 基于AFNetworking的网络层封装类.
*
* @note 这里只提供公共api
*/
@interface HYBNetworking : NSObject /*!
* @author 黄, 15-11-15 13:11:45
*
* 用于指定网络请求接口的基础url,如:
* http://henishuo.com或者http://101.200.209.244
* 通常在AppDelegate中启动时就设置一次就可以了。如果接口有来源
* 于多个服务器,可以调用更新
*
* @param baseUrl 网络接口的基础url
*/
+ (void)updateBaseUrl:(NSString *)baseUrl;
+ (NSString *)baseUrl; /**
* 设置请求超时时间,默认为60秒
*
* @param timeout 超时时间
*/
+ (void)setTimeout:(NSTimeInterval)timeout; /**
* 当检查到网络异常时,是否从从本地提取数据。默认为NO。一旦设置为YES,当设置刷新缓存时,
* 若网络异常也会从缓存中读取数据。同样,如果设置超时不回调,同样也会在网络异常时回调,除非
* 本地没有数据!
*
* @param shouldObtain YES/NO
*/
+ (void)obtainDataFromLocalWhenNetworkUnconnected:(BOOL)shouldObtain; /**
* @author 黄
*
* 默认只缓存GET请求的数据,对于POST请求是不缓存的。如果要缓存POST获取的数据,需要手动调用设置
* 对JSON类型数据有效,对于PLIST、XML不确定!
*
* @param isCacheGet 默认为YES
* @param shouldCachePost 默认为NO
*/
+ (void)cacheGetRequest:(BOOL)isCacheGet shoulCachePost:(BOOL)shouldCachePost; /**
* @author 黄
*
* 获取缓存总大小/bytes
*
* @return 缓存大小
*/
+ (unsigned long long)totalCacheSize; /**
* 默认不会自动清除缓存,如果需要,可以设置自动清除缓存,并且需要指定上限。当指定上限>0M时,
* 若缓存达到了上限值,则每次启动应用则尝试自动去清理缓存。
*
* @param mSize 缓存上限大小,单位为M(兆),默认为0,表示不清理
*/
+ (void)autoToClearCacheWithLimitedToSize:(NSUInteger)mSize; /**
* @author 黄
*
* 清除缓存
*/
+ (void)clearCaches; /*!
* @author 黄, 15-11-15 14:11:40
*
* 开启或关闭接口打印信息
*
* @param isDebug 开发期,最好打开,默认是NO
*/
+ (void)enableInterfaceDebug:(BOOL)isDebug; /*!
* @author 黄, 15-12-25 15:12:45
*
* 配置请求格式,默认为JSON。如果要求传XML或者PLIST,请在全局配置一下
*
* @param requestType 请求格式,默认为JSON
* @param responseType 响应格式,默认为JSO,
* @param shouldAutoEncode YES or NO,默认为NO,是否自动encode url
* @param shouldCallbackOnCancelRequest 当取消请求时,是否要回调,默认为YES
*/
+ (void)configRequestType:(HYBRequestType)requestType
responseType:(HYBResponseType)responseType
shouldAutoEncodeUrl:(BOOL)shouldAutoEncode
callbackOnCancelRequest:(BOOL)shouldCallbackOnCancelRequest; /*!
* @author 黄, 15-11-16 13:11:41
*
* 配置公共的请求头,只调用一次即可,通常放在应用启动的时候配置就可以了
*
* @param httpHeaders 只需要将与服务器商定的固定参数设置即可
*/
+ (void)configCommonHttpHeaders:(NSDictionary *)httpHeaders; /**
* @author 黄
*
* 取消所有请求
*/
+ (void)cancelAllRequest;
/**
* @author 黄
*
* 取消某个请求。如果是要取消某个请求,最好是引用接口所返回来的HYBURLSessionTask对象,
* 然后调用对象的cancel方法。如果不想引用对象,这里额外提供了一种方法来实现取消某个请求
*
* @param url URL,可以是绝对URL,也可以是path(也就是不包括baseurl)
*/
+ (void)cancelRequestWithURL:(NSString *)url; /*!
* @author 黄, 15-11-15 13:11:50
*
* GET请求接口,若不指定baseurl,可传完整的url
*
* @param url 接口路径,如/path/getArticleList
* @param refreshCache 是否刷新缓存。由于请求成功也可能没有数据,对于业务失败,只能通过人为手动判断
* @param params 接口中所需要的拼接参数,如@{"categoryid" : @(12)}
* @param success 接口成功请求到数据的回调
* @param fail 接口请求数据失败的回调
*
* @return 返回的对象中有可取消请求的API
*/
+ (HYBURLSessionTask *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail;
// 多一个params参数
+ (HYBURLSessionTask *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail;
// 多一个带进度回调
+ (HYBURLSessionTask *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(HYBGetProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail; /*!
* @author 黄, 15-11-15 13:11:50
*
* POST请求接口,若不指定baseurl,可传完整的url
*
* @param url 接口路径,如/path/getArticleList
* @param params 接口中所需的参数,如@{"categoryid" : @(12)}
* @param success 接口成功请求到数据的回调
* @param fail 接口请求数据失败的回调
*
* @return 返回的对象中有可取消请求的API
*/
+ (HYBURLSessionTask *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail;
+ (HYBURLSessionTask *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(HYBPostProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail;
/**
* @author 黄, 16-01-31 00:01:40
*
* 图片上传接口,若不指定baseurl,可传完整的url
*
* @param image 图片对象
* @param url 上传图片的接口路径,如/path/images/
* @param filename 给图片起一个名字,默认为当前日期时间,格式为"yyyyMMddHHmmss",后缀为`jpg`
* @param name 与指定的图片相关联的名称,这是由后端写接口的人指定的,如imagefiles
* @param mimeType 默认为image/jpeg
* @param parameters 参数
* @param progress 上传进度
* @param success 上传成功回调
* @param fail 上传失败回调
*
* @return
*/
+ (HYBURLSessionTask *)uploadWithImage:(UIImage *)image
url:(NSString *)url
filename:(NSString *)filename
name:(NSString *)name
mimeType:(NSString *)mimeType
parameters:(NSDictionary *)parameters
progress:(HYBUploadProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail; /**
* @author 黄, 16-01-31 00:01:59
*
* 上传文件操作
*
* @param url 上传路径
* @param uploadingFile 待上传文件的路径
* @param progress 上传进度
* @param success 上传成功回调
* @param fail 上传失败回调
*
* @return
*/
+ (HYBURLSessionTask *)uploadFileWithUrl:(NSString *)url
uploadingFile:(NSString *)uploadingFile
progress:(HYBUploadProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail; /*!
* @author 黄, 16-01-08 15:01:11
*
* 下载文件
*
* @param url 下载URL
* @param saveToPath 下载到哪个路径下
* @param progressBlock 下载进度
* @param success 下载成功后的回调
* @param failure 下载失败后的回调
*/
+ (HYBURLSessionTask *)downloadWithUrl:(NSString *)url
saveToPath:(NSString *)saveToPath
progress:(HYBDownloadProgress)progressBlock
success:(HYBResponseSuccess)success
failure:(HYBResponseFail)failure; @end

使用:

  // 通常放在appdelegate就可以了
[HYBNetworking updateBaseUrl:@"http://apistore.baidu.com"];
[HYBNetworking enableInterfaceDebug:YES]; // 配置请求和响应类型,由于部分伙伴们的服务器不接收JSON传过去,现在默认值改成了plainText
[HYBNetworking configRequestType:kHYBRequestTypePlainText
responseType:kHYBResponseTypeJSON
shouldAutoEncodeUrl:YES
callbackOnCancelRequest:NO]; /*
[HYBNetworking.m:in line: 189]-->[message:
absoluteUrl: http://apistore.baidu.com/microservice/cityinfo?cityname=%E5%8C%97%E4%BA%AC
params:(null)
response:{
errNum = 0;
retData = {
cityCode = 101010100;
cityName = "\U5317\U4eac";
provinceName = "\U5317\U4eac";
telAreaCode = 010;
zipCode = 100000;
};
retMsg = success;
}
]
*/ // 设置GET、POST请求都缓存
[HYBNetworking cacheGetRequest:YES shoulCachePost:YES]; // 测试GET API
NSString *url = @"http://api.map.baidu.com/telematics/v3/weather?location=嘉兴&output=json&ak=5slgyqGDENN7Sy7pw29IUvrZ";
// 设置请求类型为text/html类型
// [HYBNetworking configRequestType:kHYBRequestTypePlainText];
// [HYBNetworking configResponseType:kHYBResponseTypeData];
// 如果请求回来的数据是业务数据,但是是失败的,这时候需要外部开发人员才能判断是业务失败。
// 内部处理是只有走failure的才能判断为无效数据,才不会缓存
// 如果设置为YES,则每次会去刷新缓存,也就是不会读取缓存,即使已经缓存起来
// 新下载的数据会重新缓存起来
[HYBNetworking getWithUrl:url refreshCache:NO params:nil progress:^(int64_t bytesRead, int64_t totalBytesRead) {
NSLog(@"progress: %f, cur: %lld, total: %lld",
(bytesRead * 1.0) / totalBytesRead,
bytesRead,
totalBytesRead);
} success:^(id response) { } fail:^(NSError *error) { }]; // 测试POST API:
// 假数据
NSDictionary *postDict = @{ @"urls": @"http://www.henishuo.com/git-use-inwork/",
@"goal" : @"site",
@"total" : @()
};
NSString *path = @"/urls?site=www.henishuo.com&token=bRidefmXoNxIi3Jp";
// 由于这里有两套基础路径,用时就需要更新
[HYBNetworking updateBaseUrl:@"http://data.zz.baidu.com"];
// 每次刷新缓存
// 如果获取到的业务数据是不正确的,则需要下次调用时设置为YES,表示要刷新缓存
// HYBURLSessionTask *task =
[HYBNetworking postWithUrl:path refreshCache:YES params:postDict success:^(id response) { } fail:^(NSError *error) { }]; // 取消全部请求
// [HYBNetworking cancelAllRequest]; // 取消单个请求方法一
// [HYBNetworking cancelRequestWithURL:path]; // 取消单个请求方法二
// [task cancel]; NSLog(@"%lld", [HYBNetworking totalCacheSize]);
// [HYBNetworking clearCaches]; path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/b.zip"];
[HYBNetworking downloadWithUrl:@"http://wiki.lbsyun.baidu.com/cms/iossdk/sdk/BaiduMap_IOSSDK_v2.10.2_All.zip" saveToPath:path progress:^(int64_t bytesRead, int64_t totalBytesRead) { } success:^(id response) { } failure:^(NSError *error) { }];
// NSLog(@"%@", task);
}

第六十二篇、AFN3.0封装网络请求框架,支持缓存的更多相关文章

  1. App 组件化/模块化之路——如何封装网络请求框架

    App 组件化/模块化之路——如何封装网络请求框架 在 App 开发中网络请求是每个开发者必备的开发库,也出现了许多优秀开源的网络请求库.例如 okhttp retrofit android-asyn ...

  2. 第六十二篇:Vue的双向绑定与按键修饰符

    好家伙,依旧是vue的基础 1.按键修饰符 假设我们在一个<input>框中输入了12345,我们希望按一下"Esc" 然后删除所有前面输入的内容,这时候,我们会用到按 ...

  3. 基于AFNetWorking 3.0封装网络请求数据的类

    对于使用 AFNetworking 的朋友来说,很多朋友都是直接调用 AFNetworking的 API ,这样不太好,无法做到全工程统一配置. 最好的方式就是对网络层再封装一层,全工程不允许直接使用 ...

  4. 解剖SQLSERVER 第十二篇 OrcaMDF 行压缩支持(译)

    解剖SQLSERVER 第十二篇   OrcaMDF 行压缩支持(译) http://improve.dk/orcamdf-row-compression-support/ 在这两个月的断断续续的开发 ...

  5. 第十二篇 Integration Services:高级日志记录

    本篇文章是Integration Services系列的第十二篇,详细内容请参考原文. 简介在前一篇文章我们配置了SSIS内置日志记录,演示了简单和高级日志配置,保存并查看日志配置,生成自定义日志消息 ...

  6. Python开发【第二十二篇】:Web框架之Django【进阶】

    Python开发[第二十二篇]:Web框架之Django[进阶]   猛击这里:http://www.cnblogs.com/wupeiqi/articles/5246483.html 博客园 首页 ...

  7. 【译】第十二篇 Integration Services:高级日志记录

    本篇文章是Integration Services系列的第十二篇,详细内容请参考原文. 简介在前一篇文章我们配置了SSIS内置日志记录,演示了简单和高级日志配置,保存并查看日志配置,生成自定义日志消息 ...

  8. 跟我学SpringCloud | 第十二篇:Spring Cloud Gateway初探

    SpringCloud系列教程 | 第十二篇:Spring Cloud Gateway初探 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 如 ...

  9. Spring Cloud第十二篇 | 消息总线Bus

    ​ ​本文是Spring Cloud专栏的第十二篇文章,了解前十一篇文章内容有助于更好的理解本文: Spring Cloud第一篇 | Spring Cloud前言及其常用组件介绍概览 Spring ...

随机推荐

  1. Unity3D-Baked Lightmapping 示例学习

    首先,看一下摄像机的Rendering Paths http://game.ceeger.com/Manual/RenderingPaths.html 可以看出,对于灯光的渲染质量 Deferred ...

  2. ListView为什么用setOnItemClick这个方法和onTouch这个方法有冲突

    因为如果onTouch方法中返回true的话,这次事件就被ListView中的item控件消费了,所以不会执行ListVIew的setOnItemClick这个方法了,如果onTouch方法返回fal ...

  3. 在Flash Builder或者Eclipse统计代码行数的方法

    在Flash  Builder或者Eclipse统计代码行数的方法如下图菜单栏--搜索--搜索文件

  4. delphi 16 网页缩放

    网页放大 网页缩小         WebBrowser1.OleObject.Document.Body.Style.Zoom := 0.50; 缩放网页 Ctrl+中键↑ 放大 Ctrl+中键↓ ...

  5. Android监听SD卡文件变化

    今天再一次使用到FileObserver,上一次使用还是很久之前了.总结一下FileObserver里留的一些“坑”   1.FileObserver只能监听一个目录下的“一级”子文件,也就是说Fil ...

  6. [Canvas] Make Canvas Responsive to Pixel Ratio

    Canvas is great for high performance graphics rendering but by default the results look blocky on ph ...

  7. Swift计算属性

    除存储属性外,类.结构体和枚举可以定义计算属性,计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值. struct Point { va ...

  8. boost.asio源码剖析(一) ---- 前 言

    * 前言 源码之前,了无秘密.                                                       ——侯捷 Boost库是一个可移植.提供源代码的C++库,作 ...

  9. android 注册、登录实现程序

    注册页面: user_register.xml: <?xml version="1.0" encoding="utf-8"?> <Linear ...

  10. Apache Shiro Architecture--官方文档

    原文地址:http://shiro.apache.org/architecture.html Apache Shiro's design goals are to simplify applicati ...