本框架实现思路与YTKNetwork和RTNetworking类似,相当于一个简单版,把每一个网络请求封装成对象。使用LXNetwork,你的每一个请求都需要继承LXBaseRequest类,通过覆盖父类的一些方法或者实现相关协议方法来构造指定的网络请求。这个网络库可直接在项目中使用,但是有些功能完成度不是很完美,待完善。

GitHud地址:https://github.com/CoderLXWang/LXNetwork


一、为什么要这样做?

实现思路的图在下面,可以对比着图看下面内容。

直接封装一个简易的HttpTool,里面直接调用AF,返回responseObject直接返回, 这样不行吗, 为什么要弄这么麻烦?

显而易见的优点大概有以下几点:

1,前后隔离AFNetworking,以后如果升级AF或者替换其他框架, 只需要改动直接与AF接触的LXRequestProxy和LXResponse内的代码即可,避免对项目中业务代码产生影响(半小时完成从AF2.6升级AF3.0,重度使用的三方框架一般都要隔离一下)

2,将每个接口抽象成一个类,易于管理,按每个接口的需求构造请求(比如有的接口要缓存,有的接口不要缓存)

3,所有接口调用都经过LXBaseRequest,可以方便的在基类中处理公共逻辑(比如项目全部完成了,突然要用请求参数排序,加盐等方式加密)

缺点:使用麻烦。。。。。

实现思路.png

二、思路讲解

包括缓存在内的大体思路即上图,上图中箭头颜色由浅到深即为调用顺序,大概讲解一下

1,首先要把网络请求封装成对象,即图中TestApi(继承于LXBaseRequest),在Viewcontroller中调用接口loadData

2,这时会调用到TestApi的父类LXBaseRequest中的loadData方法, 并从TestApi实现的重写或者协议方法中获取url, 请求类型, 参数等信息, 调用LXRequestProxy中的请求方法

3, LXRequestProxy内部调用AF的GET或其他方法

4, 回调之后并没有直接返回responseObject,而是转换成LXResponse,这样返回的数据经过封装, 相当于从后面也进行了隔离, 比如AF2.x的时候回调block的参数还是^(AFHTTPRequestOperation *operation, id reponseObject),AF3.x就变成了^(NSURLSessionDataTask *task, id reponseObject),如果不转换一下, 直接返回到控制器,改起来就尴尬了。。。

5,一路回调到TestApi, 再到ViewController

6,走完之后再看一下缓存如何处理,首先,缓存一定分为存和取,

存,在第5步, 一路回调到父类中的successCallApi这一步,将回调数据存起来的(用GET+登录状态或其他+url+参数转换的字符串作为key,这个随意,适合项目即可)。

取,在第2步调用父类LXBaseRequest中的loadData时会先检测该接口对应的数据是否存在, 存在直接返回父类LXBaseRequest中的successCallApi,不存在则正常发出请求


三, 举个栗子

接口如何定义?

FirstTestApi.h

#import "LXBaseRequest.h"

//要遵守什么协议取决于这个接口需要如何构造, LXBaseRequestDelegate一定要遵守,用于获取url等基本信息

@interface FirstTestApi : LXBaseRequest

//调用接口需要的参数,可以从控制器赋值传过来

@property (nonatomic, assign) int tp;

/** 是否为读取新数据(对应下拉刷新) */

@property (nonatomic, assign) BOOL isLoadNew;

/** 是否为最后一页 */

@property (nonatomic, assign) BOOL isLastPage;

/** 可以是转好的数据模型,这里只是示意一下 */

@property (nonatomic, strong) id dataModel;

@property (nonatomic, assign) NSUInteger dataLength;

@end

FirstTestApi.m

#import "FirstTestApi.h"

//基本url,可定义成全局变量或者宏, 拼接URL使用

#define BaseUrl @"http://api.zsreader.com/v2/"

@implementation FirstTestApi

{

int page;//记录页码值

int pageSize;//每页条数

NSInteger total;//记录总条数

}

//重写init方法是为了设置获取参数和请求头的代理, LXBaseRequestDelegate不用设置是因为已在父类中设置好, 如果只需要最基本的LXBaseRequestDelegate则不需要重写init

- (instancetype)init

{

self = [super init];

if (self) {

self.paramSource = self;

self.headerSource = self;

}

return self;

}

- (LXBaseRequestType)requestType {

return LXBaseRequestTypeGet;

}

//1. 如果是POST请求下面两个方法都不用写

//2. 如果接口需要缓存, 这个可以不写,默认需要

//3. 某些GET接口, 不需要缓存一定要写, 比如需要数据及时变化的

- (BOOL)shouldCache {

return YES;

}

//1. 删除缓存的时候要用来拼接缓存的key, 所以如果使用了缓存, 这个方法最好要写, 简单点就是直接返回requestUrl, 复杂一点的情况, 写各种不同的url的共同部分, 比如一个接口类里面有两个相近的url,@"pub/home/2"和@"pub/found/2", 则可以写@"pub/", 区共同部分,清缓存时都清掉

//2. 不能引用当前类的变量, 直接崩掉, self.的东西都不行

//3. 如果有不同情况, (@"情况1"|@"情况2"), 比如 @"pub/home" 和 @"pub/found" 可以写[NSString stringWithFormat:@"%@(%@|%@)", BaseUrl, @"pub/home", @"pub/found"];

- (NSString *)cacheRegexKey {

return [NSString stringWithFormat:@"%@%@", BaseUrl, @"pub/home/2"];

}

- (NSString *)requestUrl {

return [NSString stringWithFormat:@"%@%@", BaseUrl, @"pub/home/2"];

}

//伪代码, 本接口不需要header, 演示一下

- (NSDictionary *)headersForRequest:(LXBaseRequest *)request {

//    if (app.isLogin) {

//        return @{@"token":app.token};

//    }

return nil;

}

- (NSDictionary *)paramsForRequest:(LXBaseRequest *)request {

//假如是上拉刷新,取下一页

NSMutableDictionary *params = [NSMutableDictionary dictionary];

if (!self.isLoadNew) {

params[@"page"]=@(page);

}

params[@"tp"] = @(self.tp);

return [params copy];

}

//在调用API之前额外添加一些参数,但不应该在这个函数里面修改已有的参数

//如果实现这个方法, 一定要在传入的params基础上修改 , 再返回修改后的params

- (NSDictionary *)reformParams:(NSDictionary *)params {

NSMutableDictionary *mParams = [params mutableCopy];

[mParams setObject:@"test" forKey:@"test"];

return [mParams copy];

}

//获取到包装之后的LXResponse类型的返回数据,先处理一下,比如讲数据转成模型,供控制器回调使用

-(void)beforePerformSuccessWithResponse:(LXResponse *)response

{

[super beforePerformSuccessWithResponse:response];

if(self.isLoadNew){

page=1;

pageSize = [response.content[@"count"] intValue];

total = [response.content[@"total"] integerValue];

}

self.isLastPage=(total<=page*pageSize);

//可以在这转好模型, 控制器里直接用

self.dataModel = response.result;

self.dataLength = response.responseData.length;

page++;

}

@end

控制器中如何使用?

ViewController.h

#import "ViewController.h"

#import "FirstTestApi.h"

//遵守回调协议LXBaseRequestCallBackDelegate

@interface ViewController ()

//定义接口属性实现懒加载

@property (nonatomic, strong) FirstTestApi *firstApi;

@end

@implementation ViewController

#pragma mark --------- 懒加载 --------

- (FirstTestApi *)firstApi

{

if (!_firstApi) {

_firstApi = [[FirstTestApi alloc] init];

_firstApi.delegate = self;

_firstApi.tp = 1;

}

return _firstApi;

}

- (void)viewDidLoad {

[super viewDidLoad];

//添加点击调用接口的按钮

[self configBtns];

}

//firstApi调用刷新, 即page = 1

- (void)loadNew {

self.firstApi.isLoadNew=YES;

[self.firstApi loadData];

}

//firstApi调用加载更多, 即page++

- (void)loadMore {

self.firstApi.isLoadNew=NO;

[self.firstApi loadData];

}

#pragma mark --------- LXBaseRequestCallBackDelegate --------

- (void)requestDidSuccess:(LXBaseRequest *)request {

if ([request isKindOfClass:[FirstTestApi class]]) {

FirstTestApi *testApi = (FirstTestApi *)request;

NSLog(@"接口1请求成功 %lu ", testApi.dataLength);

}

}

- (void)requestDidFailed:(LXBaseRequest *)request {

NSLog(@"请求失败");

}

@end


四,还有那些功能及注意

1,如何清除缓存, 比如在A界面的某个操作导致B,C界面数据都应发生变化, 这时切换到B,C界面(缓存有效时间内,默认30秒,可在LXNetworkConfiguration中设置),如果还是使用缓存就不对了。

方式1: 如果在需要清缓存的时刻能获取到BC界面相应接口, 可调用[B.firstApi deleteCache],接口父类LXBaseRequest的deleteCache方法即可清除缓存

方式2: 一般情况下, 在A界面操作的时候可能已经获取不到BC界面的接口了,这是可以通过通知清除,让AppDelegate监听清除缓存的通知

#import "AppDelegate.h"

#import "LXNetworkConfiguration.h"

#import "LXCache.h"

@interface AppDelegate ()

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInvalidCache:) name:LXDeleteCacheNotification object:nil];

return YES;

}

-(void)handleInvalidCache:(NSNotification*) notify

{

NSDictionary* dict = notify.userInfo;

NSArray* arr = [dict objectForKey:LXDeleteCacheKey];

for (Class cls in arr) {

[[LXCache sharedInstance] deleteCacheWithClass:cls];

}

}

A界面某个操作成功之后

[[NSNotificationCenter defaultCenter] postNotificationName:LXDeleteCacheNotification

object:nil

userInfo:@{LXDeleteCacheKey

: @[NSClassFromString(@"TestApi"),

NSClassFromString(@"xxxApi"),

NSClassFromString(@"xxxApi"),

]}];

2,如果一个界面内有多个api怎么办, 都在requestDidSuccess里通过类型写if else 很乱, 实现requestDicWithClassStrAndSELStr将回调分发到单独的方法中

#pragma mark --------- LXBaseRequestCallBackDelegate --------

//@required方法, 实现一下, 不用写东西

- (void)requestDidSuccess:(LXBaseRequest *)request {

}

//如果一个控制器内存在多个接口,并使用协议代理方式回调,则可以实现下面方法,将各个请求回调分发到各自的方法里,字典key为接口类名,value为方法的selector字符串

- (NSDictionary *)requestDicWithClassStrAndSELStr {

NSMutableDictionary *dic = [NSMutableDictionary dictionary];

[dic setObject:NSStringFromSelector(@selector(handleFirstTestApi:)) forKey:@"FirstTestApi"];

[dic setObject:NSStringFromSelector(@selector(handleSecondTestApi:)) forKey:@"SecondTestApi"];

return [dic copy];

}

- (void)handleFirstTestApi:(FirstTestApi *)api {

NSLog(@"接口1请求成功 %lu ", api.dataLength);

}

- (void)handleSecondTestApi:(SecondTestApi *)api {

NSLog(@"接口2请求成功 %@ ", api.dataModel);

}

- (void)requestDidFailed:(LXBaseRequest *)request {

NSLog(@"请求失败");

}

3,如何使用block回调,注意使用这种方式回调不用遵守LXBaseRequestCallBackDelegate协议和设置self.secondApi.delegate = self

- (void)loadSecondApi {

[self.secondApi loadDataWithSuccess:^(LXBaseRequest *request) {

SecondTestApi *testApi = (SecondTestApi *)request;

NSLog(@"block 接口2请求成功 %@ ", testApi.dataModel);

} fail:^(LXBaseRequest *request) {

NSLog(@"接口2请求失败");

}];

}

4,接口有动态加密的缓存处理, 如果接口有加密, 并且融合了时间戳等,每次调用的签名都不一致的话,由于缓存的key拼接了参数, 如果把动态变化的签名也拼进去就永远找不到缓存,那么就需要修改以下代码, 将下面代码中注释部分打开, 在if条件中排除会动态变化的参数的key

NSDictionary+LXNetworkParams.m

#import "NSDictionary+LXNetworkParams.h"

#import "NSArray+LXNetworkParams.h"

@implementation NSDictionary (LXNetworkParams)

/** params 转换为NSString */

- (NSString *)lxUrlParamsToString {

//字典排序

NSArray *sortedArray = [self lxUrlParamsToArray];

//数组生成字符串

return [sortedArray lxUrlParamArrayToString];

}

- (NSArray *)lxUrlParamsToArray {

NSMutableArray *result = [[NSMutableArray alloc] init];

[self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {

if (![obj isKindOfClass:[NSString class]]) {

obj = [NSString stringWithFormat:@"%@", obj];

}

obj = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL,  (CFStringRef)obj,  NULL,  (CFStringRef)@"!*'();:@&;=+$,/?%#[]",  kCFStringEncodingUTF8));

//        if ([obj length] > 0 && (![key isEqualToString:TIMESTAMP_KEY] && ![key isEqualToString:SIGNATURE_KEY]) ) {

[result addObject:[NSString stringWithFormat:@"%@=%@", key, obj]];

//        }

}];

NSArray *sortedResult = [result sortedArrayUsingSelector:@selector(compare:)];

return sortedResult;

}

@end

5,其他功能用法见源码,有问题或者建议欢迎交流沟通


GitHud地址:https://github.com/CoderLXWang/LXNetwork

LXNetwork – 基于AF3.0封装的iOS网络请求库的更多相关文章

  1. 【转载】一步一步搭建自己的iOS网络请求库

    一步一步搭建自己的iOS网络请求库(一) 大家好,我是LastDay,很久没有写博客了,这周会分享一个的HTTP请求库的编写经验. 简单的介绍 介绍一下,NSURLSession是iOS7中新的网络接 ...

  2. 自己动手写一个iOS 网络请求库的三部曲[转]

    代码示例:https://github.com/johnlui/Swift-On-iOS/blob/master/BuildYourHTTPRequestLibrary 开源项目:Pitaya,适合大 ...

  3. 造轮子 | 怎样设计一个面向协议的 iOS 网络请求库

    近期开源了一个面向协议设计的网络请求库 MBNetwork,基于 Alamofire 和 ObjectMapper 实现,目的是简化业务层的网络请求操作. 须要干些啥 对于大部分 App 而言,业务层 ...

  4. android基于开源网络框架asychhttpclient,二次封装为通用网络请求组件

    网络请求是全部App都不可缺少的功能,假设每次开发都重写一次网络请求或者将曾经的代码拷贝到新的App中,不是非常合理,出于此目的,我希望将整个网络请求框架独立出来,与业务逻辑分隔开,这样就能够避免每次 ...

  5. 最简单的iOS网络请求

    做iOS开发,说到网络请求,大家可能都不约而同的提到AFN,可以说大家的网络请求都是用AFN封装而成,AFN的强大易用的确很好. 但是版本升级就会出现一些问题,所以就自己基于iOS原生封装了一个网络请 ...

  6. 基于Retrofit+RxJava的Android分层网络请求框架

    目前已经有不少Android客户端在使用Retrofit+RxJava实现网络请求了,相比于xUtils,Volley等网络访问框架,其具有网络访问效率高(基于OkHttp).内存占用少.代码量小以及 ...

  7. springmvc接口ios网络请求

    springmvc:   application/json;charset=utf-8的ios网络请求: 后台使用 @RequestBody注解参数接收:

  8. 浅论Android网络请求库——android-async-http

    在iOS开发中有大名鼎鼎的ASIHttpRequest库,用来处理网络请求操作,今天要介绍的是一个在Android上同样强大的网络请求库android-async-http,目前非常火的应用Insta ...

  9. [转]Android各大网络请求库的比较及实战

    自己学习android也有一段时间了,在实际开发中,频繁的接触网络请求,而网络请求的方式很多,最常见的那么几个也就那么几个.本篇文章对常见的网络请求库进行一个总结. HttpUrlConnection ...

随机推荐

  1. bzoj4578: [Usaco2016 OPen]Splitting the Field

    2365: Splitting the Field 题意:n个点,求用两个矩形面积覆盖完所有点和一个矩形覆盖完少多少面积 思路:枚举两个矩形的分割线,也就是把所有点分成两个部分,枚举分割点:先预处理每 ...

  2. Tkinter教程之Button篇(2)

    本文转载自:http://blog.csdn.net/jcodeer/article/details/1811300 # Tkinter教程之Button篇(2)'''5.指定Button的宽度与高度 ...

  3. 超简单fedora20(linux)下JDK1.8的安装

    (博客园-番茄酱原创) 去官网下载linux版本的jdk,如果你的fedora是64位,就选择64位的jdk,jdk-8u20-linux-x64.tar.gz. 将下载好的jdk解压到当前目录下,解 ...

  4. 【hadoop代码笔记】Hadoop作业提交中EagerTaskInitializationListener的作用

    在整理FairScheduler实现的task调度逻辑时,注意到EagerTaskInitializationListener类.差不多应该是job提交相关的逻辑代码中最简单清楚的一个了. todo: ...

  5. SQL Server 中的三种分页方式

    USE tempdb GO SET NOCOUNT ON --创建表结构 IF OBJECT_ID(N'ClassB', N'U') IS NOT NULL DROP TABLE ClassB GO ...

  6. (转)UML序列图总结

    序列图主要用于展示对象之间交互的顺序. 序列图将交互关系表示为一个二维图.纵向是时间轴,时间沿竖线向下延伸.横向轴代表了在协作中各独立对象的类元角色.类元角色用生命线表示.当对象存在时,角色用一条虚线 ...

  7. Winter is coming Just have a little faith. JSF框架简介与实例

    JSF 体系结构: JSF 的主要优势之一就是它既是Java Web应用程序的用户界面标准又是严格遵循模型-视图-控制器 (MVC) 设计模式的框架.用户界面代码(视图)与应用程序数据和逻辑(模型)的 ...

  8. 最新的支持DELPHI XE6的开发框架

    支持负载均衡集群的中间件 主界面 插件管理 角色与权限 用户与权限

  9. MySQL支持Emoji表情

    让MySQL支持Emoji表情,涉及无线相关的 MySQL 数据库建议都提前采用 utf8mb4 字符集. utf8mb4和utf8到底有什么区别呢?原来以往的mysql的utf8一个字符最多3字节, ...

  10. ASP.NET MVC 前端(View)向后端(Controller)中传值

    在MVC中,要把前端View中的值传递给后端Controller, 主要有两种方法 1. 利用Request.Form 或者 Request.QueryString public ActionResu ...