封裝AFN3.0網絡請求框架,使用FMDB緩存并可帶有時效性緩存

現在項目處于維護階段,以前的一部分代碼隨著用戶量的增加,不斷暴露出問題,特別是網絡請求--回調處理方面的修改比較多。每次修改對應的接口都要跳到對應控制器啊,或者是搜一發提示語,其他網絡請求操作也是分散在各個控制器內,亂七八糟。好煩的。趁著有點空閑,研究了下別人的封裝,改進了項目中使用的封裝,并做成了SDK發布在cocoaPods上,歡迎使用.
pod 'TYNetworkTool'即可使用。

項目原先的AFN封裝

也就是很簡單的按照AFN封裝了一下,.m
//
//  PPRHttpManager.m
//  PaopaoRunning
//
//  Created by 王天永 on 16/5/22.

//

#import "PPRHttpManager.h"
#import <AFNetworking.h>
#import "NSString+Paths.h"
#import "NSString+UUID.h"

@implementation PPRHttpManager
+ (NSURLSessionDataTask *)get:(NSString *)url params:(NSDictionary *)params success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure{
    
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    if (IOS11) {    //適配IOS11增加的這句
        manager.securityPolicy.allowInvalidCertificates = YES;
    }
    manager.requestSerializer.timeoutInterval = 15;
//    [manager.requestSerializer setValue:@"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    NSURLSessionDataTask *task = [manager GET:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (success){
            NSString *responseString = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
            PRLog(@"responseString---%@",responseString);
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil];
            success(dict);
        }
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        if (failure){
            failure(error);
        }
    }];
    return task;
}

+ (NSURLSessionDataTask *)post:(NSString *)url params:(NSDictionary *)params success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure{
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
//    manager.securityPolicy.allowInvalidCertificates = YES;
    manager.requestSerializer.timeoutInterval = 15;
    NSURLSessionDataTask *task = [manager POST:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (success){
            NSString *responseString = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
            PRLog(@"responseString---%@",responseString);
            
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil];

            success(dict);
        }
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        if (failure){

            failure(error);
        }
    }];
    return task;

}

+ (NSURLSessionDownloadTask *)downloadFile:(NSString *)url progress:(void (^)(CGFloat progress))progress success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure{

    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
    
    NSURL *URL = [NSURL URLWithString:url];
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];
    NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress){
//    NSLog(@"progress is %f", downloadProgress.fractionCompleted);
        progress(downloadProgress.fractionCompleted);
    } destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
        NSString *caches = [NSString cachesPath];
        NSString *videovideoCaches = [caches stringByAppendingPathComponent:@"videoCaches"];
        
        NSURL *videoFileUrl = [NSURL fileURLWithPath:videovideoCaches];
        return [videoFileUrl URLByAppendingPathComponent:[response suggestedFilename] ];
    } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
        if (!error){
            PRLog(@"%@",F(@"File downloaded to: %@", filePath));
            success(filePath);
            
            
        }else{
            PRLog(@"File download fail!");
            failure(error);
        }
    }];
    [downloadTask resume];
    return downloadTask;
    
}

@end

著手封裝新的低耦合的AFN網絡封裝

封裝的主要方向

  1. 本地緩存優化,請求可根據需求(緩存的時效性等)決定是否直接使用本地緩存,或者請求到新數據后刷新本地。(當前已完成)
  2. 增加短時緩存,避免相同的請求重復調用,浪費用戶流量。(當前已完成)
  3. 封裝請求失敗的重試機制。
  4. 將成功失敗提示的邏輯封裝。
  5. 其他比如網絡狀況監控,請求一鍵撤銷等。(當前已完成)
    感謝巨巨文章提供思路和參考
    猿題庫YTKNetwork的GitHub
    iOS 工作中封裝通用性網絡請求框架——鴻雁長飛光不度
    AFNetworking3.x與YYCache的二次封裝,和FMDB說拜拜——jkpang

封裝的結構考慮

  1. 考慮以后換網絡框架的可能。(網絡單獨封裝
  2. 考慮以后換回調處理及提示的可能。網絡請求和回調處理要低耦合,能夠單獨抽離。(回調和顯示的單獨封裝
  3. 考慮以后換緩存框架的可能。(緩存單獨封裝

封裝的結構

考慮到網絡請求的通用性,而回調處理和提示等并不通用,所以回調的封裝暫時不納入SDK,但是仍然建議將網絡請求的回調封裝起來存于一處,相同回調調用相應的統一方法,以后需要改動改這一處就夠了,如本人GitHub上的TYNetworkManage文件夾內所封裝的方式。
所以最終的SDK內的文件結構是這樣的(暫時

TYNetworkTool封裝結構

· 第一層 網絡請求入口 TYNetworkTool,.h文件對外暴露,用戶之間調用即可完成網絡請求。.m調用TYCacheTool.h的方法來完成緩存的存儲和讀取;
· 第二層 緩存的存取入口TYCacheTool,主要來完成緩存的業務邏輯
· 第三層 FMDB的實際緩存操作,實際操控SQLite文件來進行存取操作。

文件內容
//
//  TYNetworkTool.h
//  TYNetworkHelper
//
//  Created by 王天永 on 2017/7/14.
//  Copyright ? 2017年 王天永. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "TYCacheTool.h"

typedef NS_ENUM(NSUInteger, TYNetworkStatusType) {
    /// 未知網絡
    TYNetworkStatusUnknown,
    /// 無網絡
    TYNetworkStatusNotReachable,
    /// 手機網絡
    TYNetworkStatusReachableViaWWAN,
    /// WIFI網絡
    TYNetworkStatusReachableViaWiFi
};

typedef NS_ENUM(NSUInteger, TYRequestSerializer) {
    /// 設置請求數據為JSON格式
    TYRequestSerializerJSON,
    /// 設置請求數據為二進制格式
    TYRequestSerializerHTTP,
};

typedef NS_ENUM(NSUInteger, TYResponseSerializer) {
    /// 設置響應數據為JSON格式
    TYResponseSerializerJSON,
    /// 設置響應數據為二進制格式
    TYResponseSerializerHTTP,
};

/// 請求成功的Block
typedef void(^TYHttpRequestSuccess)(id responseObject);

/// 請求失敗的Block
typedef void(^TYHttpRequestFailed)(NSError *error);

/// 緩存的Block
typedef void(^TYHttpRequestCache)(id responseCache);

/// 上傳或者下載的進度, Progress.completedUnitCount:當前大小 - Progress.totalUnitCount:總大小
typedef void (^TYHttTYrogress)(NSProgress *progress);

/// 網絡狀態的Block
typedef void(^TYNetworkStatus)(TYNetworkStatusType status);

@class AFHTTPSessionManager;
@interface TYNetworkTool : NSObject

/// 有網YES, 無網:NO
+ (BOOL)isNetwork;

/// 手機網絡:YES, 反之:NO
+ (BOOL)isWWANNetwork;

/// WiFi網絡:YES, 反之:NO
+ (BOOL)isWiFiNetwork;

/// 取消所有HTTP請求
+ (void)cancelAllRequest;

/// 實時獲取網絡狀態,通過Block回調實時獲取(此方法可多次調用)
+ (void)networkStatusWithBlock:(TYNetworkStatus)networkStatus;

/// 取消指定URL的HTTP請求
+ (void)cancelRequestWithURL:(NSString *)URL;

/// 開啟日志打印 (Debug級別)
+ (void)openLog;

/// 關閉日志打印,默認關閉
+ (void)closeLog;


/**
 *  GET請求,無緩存
 *
 *  @param URL        請求地址
 *  @param parameters 請求參數
 *  @param success    請求成功的回調
 *  @param failure    請求失敗的回調
 *
 *  @return 返回的對象可取消請求,調用cancel方法
 */
+ (__kindof NSURLSessionTask *)GET:(NSString *)URL
                        parameters:(id)parameters
                           success:(TYHttpRequestSuccess)success
                           failure:(TYHttpRequestFailed)failure;

/**
 *  GET請求,自動緩存
 *
 *  @param URL           請求地址
 *  @param parameters    請求參數
 *  @param responseCache 緩存數據的回調
 *  @param success       請求成功的回調
 *  @param failure       請求失敗的回調
 *
 *  @return 返回的對象可取消請求,調用cancel方法
 */
+ (__kindof NSURLSessionTask *)GET:(NSString *)URL
                        parameters:(id)parameters
                     responseCache:(TYHttpRequestCache)responseCache
                           success:(TYHttpRequestSuccess)success
                           failure:(TYHttpRequestFailed)failure;

/**
 GET請求,帶時效自動緩存

 @param URL 請求地址
 @param parameters 請求參數
 @param life 緩存時效
 @param responseCache 緩存數據的回調
 @param success 請求成功的回調
 @param failure 請求失敗的回調
 @return 返回的對象可取消請求,調用cancel方法
 */
+ (__kindof NSURLSessionTask *)GET:(NSString *)URL
                        parameters:(id)parameters
                   userfulLifeUnit:(TYTimeUnit)timeUnit
                      userfullLife:(double)life
                     responseCache:(TYHttpRequestCache)responseCache
                           success:(TYHttpRequestSuccess)success
                           failure:(TYHttpRequestFailed)failure;
/**
 *  POST請求,無緩存
 *
 *  @param URL        請求地址``
 *  @param parameters 請求參數
 *  @param success    請求成功的回調
 *  @param failure    請求失敗的回調
 *
 *  @return 返回的對象可取消請求,調用cancel方法
 */
+ (__kindof NSURLSessionTask *)POST:(NSString *)URL
                         parameters:(id)parameters
                            success:(TYHttpRequestSuccess)success
                            failure:(TYHttpRequestFailed)failure;

/**
 *  POST請求,自動緩存
 *
 *  @param URL           請求地址
 *  @param parameters    請求參數
 *  @param responseCache 緩存數據的回調
 *  @param success       請求成功的回調
 *  @param failure       請求失敗的回調
 *
 *  @return 返回的對象可取消請求,調用cancel方法
 */
+ (__kindof NSURLSessionTask *)POST:(NSString *)URL
                         parameters:(id)parameters
                      responseCache:(TYHttpRequestCache)responseCache
                            success:(TYHttpRequestSuccess)success
                            failure:(TYHttpRequestFailed)failure;

/**
 GET請求,帶時效自動緩存
 
 @param URL 請求地址
 @param parameters 請求參數
 @param life 緩存時效
 @param responseCache 緩存數據的回調
 @param success 請求成功的回調
 @param failure 請求失敗的回調
 @return 返回的對象可取消請求,調用cancel方法
 */
+ (__kindof NSURLSessionTask *)POST:(NSString *)URL
                        parameters:(id)parameters
                   userfulLifeUnit:(TYTimeUnit)timeUnit
                      userfullLife:(double)life
                     responseCache:(TYHttpRequestCache)responseCache
                           success:(TYHttpRequestSuccess)success
                           failure:(TYHttpRequestFailed)failure;

/**
 *  上傳文件
 *
 *  @param URL        請求地址
 *  @param parameters 請求參數
 *  @param name       文件對應服務器上的字段
 *  @param filePath   文件本地的沙盒路徑
 *  @param progress   上傳進度信息
 *  @param success    請求成功的回調
 *  @param failure    請求失敗的回調
 *
 *  @return 返回的對象可取消請求,調用cancel方法
 */
+ (__kindof NSURLSessionTask *)uploadFileWithURL:(NSString *)URL
                                      parameters:(id)parameters
                                            name:(NSString *)name
                                        filePath:(NSString *)filePath
                                        progress:(TYHttTYrogress)progress
                                         success:(TYHttpRequestSuccess)success
                                         failure:(TYHttpRequestFailed)failure;

/**
 *  上傳單/多張圖片
 *
 *  @param URL        請求地址
 *  @param parameters 請求參數
 *  @param name       圖片對應服務器上的字段
 *  @param images     圖片數組
 *  @param fileNames  圖片文件名數組, 可以為nil, 數組內的文件名默認為當前日期時間"yyyyMMddHHmmss"
 *  @param imageScale 圖片文件壓縮比 范圍 (0.f ~ 1.f)
 *  @param imageType  圖片文件的類型,例:png、jpg(默認類型)....
 *  @param progress   上傳進度信息
 *  @param success    請求成功的回調
 *  @param failure    請求失敗的回調
 *
 *  @return 返回的對象可取消請求,調用cancel方法
 */
+ (__kindof NSURLSessionTask *)uploadImagesWithURL:(NSString *)URL
                                        parameters:(id)parameters
                                              name:(NSString *)name
                                            images:(NSArray<UIImage *> *)images
                                         fileNames:(NSArray<NSString *> *)fileNames
                                        imageScale:(CGFloat)imageScale
                                         imageType:(NSString *)imageType
                                          progress:(TYHttTYrogress)progress
                                           success:(TYHttpRequestSuccess)success
                                           failure:(TYHttpRequestFailed)failure;

/**
 *  下載文件
 *
 *  @param URL      請求地址
 *  @param fileDir  文件存儲目錄(默認存儲目錄為Download)
 *  @param progress 文件下載的進度信息
 *  @param success  下載成功的回調(回調參數filePath:文件的路徑)
 *  @param failure  下載失敗的回調
 *
 *  @return 返回NSURLSessionDownloadTask實例,可用于暫停繼續,暫停調用suspend方法,開始下載調用resume方法
 */
+ (__kindof NSURLSessionTask *)downloadWithURL:(NSString *)URL
                                       fileDir:(NSString *)fileDir
                                      progress:(TYHttTYrogress)progress
                                       success:(void(^)(NSString *filePath))success
                                       failure:(TYHttpRequestFailed)failure;


#pragma mark - 設置AFHTTPSessionManager相關屬性
#pragma mark 注意: 因為全局只有一個AFHTTPSessionManager實例,所以以下設置方式全局生效
/**
 在開發中,如果以下的設置方式不滿足項目的需求,就調用此方法獲取AFHTTPSessionManager實例進行自定義設置
 (注意: 調用此方法時在要導入AFNetworking.h頭文件,否則可能會報找不到AFHTTPSessionManager的?)
 @param sessionManager AFHTTPSessionManager的實例
 */
+ (void)setAFHTTPSessionManagerProperty:(void(^)(AFHTTPSessionManager *sessionManager))sessionManager;

/**
 *  設置網絡請求參數的格式:默認為二進制格式
 *
 *  @param requestSerializer TYRequestSerializerJSON(JSON格式),TYRequestSerializerHTTP(二進制格式),
 */
+ (void)setRequestSerializer:(TYRequestSerializer)requestSerializer;

/**
 *  設置服務器響應數據格式:默認為JSON格式
 *
 *  @param responseSerializer TYResponseSerializerJSON(JSON格式),TYResponseSerializerHTTP(二進制格式)
 */
+ (void)setResponseSerializer:(TYResponseSerializer)responseSerializer;

/**
 *  設置請求超時時間:默認為30S
 *
 *  @param time 時長
 */
+ (void)setRequestTimeoutInterval:(NSTimeInterval)time;

/// 設置請求頭
+ (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;

/**
 *  是否打開網絡狀態轉圈菊花:默認打開
 *
 *  @param open YES(打開), NO(關閉)
 */
+ (void)openNetworkActivityIndicator:(BOOL)open;

/**
 配置自建證書的Https請求, 參考鏈接: http://blog.csdn.net/syg90178aw/article/details/52839103
 
 @param cerPath 自建Https證書的路徑
 @param validatesDomainName 是否需要驗證域名,默認為YES. 如果證書的域名與請求的域名不一致,需設置為NO; 即服務器使用其他可信任機構頒發
 的證書,也可以建立連接,這個非常危險, 建議打開.validatesDomainName=NO, 主要用于這種情況:客戶端請求的是子域名, 而證書上的是另外
 一個域名。因為SSL證書上的域名是獨立的,假如證書上注冊的域名是www.google.com, 那么mail.google.com是無法驗證通過的.
 */
+ (void)setSecurityPolicyWithCerPath:(NSString *)cerPath validatesDomainName:(BOOL)validatesDomainName;

+ (NSString *)jsonToString:(id)data;
@end

//
//  TYCacheTool.h
//  TYNetworkTool
//
//  Created by 王天永 on 2017/7/19.
//  Copyright ? 2017年 王天永. All rights reserved.
//

#import <Foundation/Foundation.h>


typedef NS_ENUM (NSUInteger,TYTimeUnit) {
    TYNone,
    TYSecond,
    TYMinute,
    TYHour,
    TYDay,
};

@interface TYCacheTool : NSObject

/**
 無失效日期緩存

 @param httpData 緩存數據
 @param url 請求url
 @param parameters 請求參數
 */
+ (void)setHttpCache:(id)httpData URL:(NSString *)url parameters:(NSDictionary *)parameters;
/**
 帶時效的緩存

 @param httpData 緩存數據
 @param url 請求url
 @param parameters 請求參數
 @param timeUnit 時間單位,TYNone則無時效
 @param life 時效數值,具體單位與timeUnit有關,最小單位 秒
 */
+ (void)setHttpCache:(id)httpData URL:(NSString *)url parameters:(NSDictionary *)parameters userfulLifeUnit:(TYTimeUnit)timeUnit life:(double)life;
/**
 獲取緩存數據
 
 @param url 請求url
 @param parameter 請求參數
 @return 緩存數據
 */
+ (id)httpCacheWithURL:(NSString *)url parameters:(NSDictionary *)parameter;

@end

//
//  TYFMDBTool.h
//  TYNetworkTool
//
//  Created by 王天永 on 2017/7/20.
//  Copyright ? 2017年 王天永. All rights reserved.
//

#import <Foundation/Foundation.h>
@class TYCacheTool;


@interface TYFMDBTool : NSObject


/**
 創建tool實例,包含一個dmdb實例對象

 @param name 對應賬號的唯一標識符
 @return 返回fmdbtool實例
 */
+ (instancetype)fmdbWithName:(NSString *)name;

/**
 創建表單

 @param tableName 表單名
 @return FMDB實例
 */
- (instancetype)createTableWithName:(NSString *)tableName;


/**
 儲存緩存請求來的回調數據(無時效)
 
 @param httpData 回調數據
 @param cacheKey 存儲的關鍵字
 */
- (void)setHttpCache:(id)httpData forCacheKey:(NSString *)cacheKey;

/**
 帶時效的緩存
 
 @param httpData 緩存數據
 @param cacheKey 存儲的關鍵字
 @param life 時效數值,具體單位與timeUnit有關
 */
- (void)setHttpCache:(id)httpData userfulLife:(double)life forCacheKey:(NSString *)cacheKey;


/**
 獲取本地緩存

 @param cacheKey 存儲的關鍵字
 @return 緩存本地的網絡請求
 */
- (id)httpCacheForCacheKey:(NSString *)cacheKey;
@end

感覺帶時效的緩存還有改進的余地,后續再繼續改進吧,以新增方法的方式。(當前的方式適合這樣的需求:如果是有效的緩存,先讀取了顯示在界面上,等數據獲取到了再顯示獲取到的數據。但是這樣就沒有達到節省流量的目的了,需要再SDK內增加先取緩存,無有效緩存再發送網絡請求的方法
本人的這個SDK已經發布在cocoapods上,pod 'TYNetworkTool'即可使用。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,662評論 25 708
  • 點擊查看原文 Web SDK 開發手冊 SDK 概述 網易云信 SDK 為 Web 應用提供一個完善的 IM 系統...
    layjoy閱讀 13,838評論 0 15
  • iOS網絡架構討論梳理整理中。。。 其實如果沒有APIManager這一層是沒法使用delegate的,畢竟多個單...
    yhtang閱讀 5,228評論 1 23
  • AFHTTPRequestOperationManager 網絡傳輸協議UDP、TCP、Http、Socket、X...
    Carden閱讀 4,371評論 0 12
  • 作者/晏承林 有時如藥罐 苦口難開 有時似蜜糖 甜在心海 其實等待是愛最想念的姿態 其實等待是情最無力的徘徊
    晏承林閱讀 391評論 0 3