美文网首页
(App架构优化)KVRouter,以及基于此路由器的扫一扫动态

(App架构优化)KVRouter,以及基于此路由器的扫一扫动态

作者: 佳木秀林 | 来源:发表于2018-04-04 15:35 被阅读0次

    前言

    关于架构优化每个人都有不同的理解,这一点我深有体会,身边的例子就是我跟一个同样做iOS开发的小伙伴的分歧,大家都是你说服不了我,我也说服不了你的状态,去年我疯狂痴迷于Casa Toloyum架构师的理论(现在应该是在天猫架构师组),我记得有一段时间他是跟另一个架构师在iOS路由器的设计上有一些不同的意见,两人分别在网上写文章来阐述自己的观点,你来我往,好不热闹。


    我借这两个例子就是来说明,对于架构设计,一千个人心中有一千种架构,KVRouter也只是基于我目前不太成熟的架构理解开发出来的,我觉得这种架构就是适合我的,以我目前的能力来说,我都能看到KVRouter某些不足的地方(好 > 坏),所以我希望能够和你们一起分享,一起学习,一起进步。


    尽管在架构优化之路上肯定会需要抛弃一些东西,也许会牺牲少量运行性能来优化项目的架构,也许会在开发上让开发者多付出一些劳动来迎合这个新的架构,但我觉得这都是值得的,毕竟当一个成熟的框架搭建起来之后,不管是在维护还是开发都能够给予不小的帮助,就像当初我在开发KVRouter的时候,公司的项目已经算是成熟了,集成KVRouter的成本非常大,但是我还是决定更换上去,事实证明,KVRouter在后续的开发中让我节省了很多力气,包括后来的页面动态定向方案也是基于KVRouter设计的。


    这篇文章目的在于帮大家提供一种App架构优化的思路,不一定是最好的方案,但却是我在架构设计方向上前进的一个小jio步。

    1 KVRouter介绍

    Git地址
    KVRouter是一个可以解决传统界面跳转耦合的一个开源项目,在2017年初已经在GitHub上开源,在我的公司iOS项目上已经运行了一年,可用性和稳定性都得到了验证,期间解决了一些问题,现在我重新对该项目进行重构,代码和逻辑已经优化了不少,API设计也更加友好(由于API改了,我花了不少时间将原项目调整过来(╥╯^╰╥))。
    备注:花了一个早上的时间熟悉了Swift,然后将KVRouter用Swift重写了一遍,对于Swift还是深有体会的,Swift版本的KVRouter将会在文章最后简单介绍一下。

    1.1 传统界面跳转

    我们先来看一段代码。

    #import "OneViewController.h"
    #import "PushOneController.h"  //需要引入目标界面文件
    
    @interface OneViewController ()
    
    @end
    
    @implementation OneViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        self.view.backgroundColor = [UIColor redColor];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        PushOneController * vc = [[PushOneController alloc] init];
        vc.user = @"kevin";  //传统赋值方式,如果该界面所需参数比较多,参数的增删都会比较难维护
        [self.navigationController pushViewController:vc animated:YES];
    }
    

    可以看到,传统方式的界面跳转对于下个界面基本上是保持着耦合关系,并且也不灵活,如果遇到可以灵活跳转界面的需求基本上需要做很多逻辑去解决这个耦合关系,而且传统的界面传参方式也比较死板,无法做到灵活传参。

    1.2 使用了KVRouter的界面跳转

    还是先看代码吧。

    #import "OneViewController.h"
    //不需要引入目标界面文件
    
    @interface OneViewController ()
    
    @end
    
    @implementation OneViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        self.view.backgroundColor = [UIColor redColor];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        //当目标界面注册了pushone这个url后,可以直接使用该API进行push跳转,参数通过url携带或者后接参数字典进行传递
        [KVRouter openUrl:@"kv://pushone?id=1" parameter:@{@"user" : @"kevin"}];
    }
    

    可以看到,引入KVRouter后,整个跳转逻辑变得简洁,传值也变得灵活。

    1.3 KVRouter实现原理

    KVRouter没有使用一些高大上的技术去实现,只是一种页面解耦方案,而且我觉得有时候不需要刻意地使用比较生僻的技术去实现某些功能,能够使用基础库的功能去实现的尽量不依靠一些黑科技。

    1.3.1 注册过程

    无图无真相


    注册

    注册比较简单,主要是将URL跟页面进行匹配,然后将这个关系保存起来,其实内部仅仅使用了一个字典去保存这个映射关系。

    1.3.2 跳转过程

    同样上图


    跳转

    同样没有用到很复杂的技术,只是使用基础库,更多的是对内部的逻辑处理过程进行优化,保证不出错,以及方便使用。

    2 集成KVRouter

    KVRouter已经在GitHub和Cocoapod上发布。

    2.1 Git集成

    Git地址
    开源项目包含一个Demo,以及Swift版本的KVRouter,都已经在Demo里面描述清楚用法。

    2.2 pod集成

    使用以下命令集成。PS:仅对Object-C版本进行了pod上传,Swift版本仅限通过GitHub下载。

    pod 'KVRouter', ' 1.0.0'
    

    3 使用KVRouter

    3.1 注册

    KVRouter的运行前提就是页面必须提前注册进KVRouter,KVRouter提供两种方式进行注册。
    1 本地配置文件注册
    该方式能够批量注册页面,方便开发者本身项目已成熟的情况下集成KVRouter降低集成成本(不需要每个页面都去执行一遍注册API),缺点是该种方式注册的页面仅能通过默认方式初始化(init),并且无法添加自己的处理逻辑。
    注册信息保存在以下文件,请严格按照该格式输入,也可以查看KVRouter初始化那部分文件解析代码,修改解析规则。

    2 API注册(比较灵活,可以添加自己的处理逻辑)
    API注册,是我推荐你使用的方式,尽管初步看每个页面都需要写这么一段代码去注册页面,但这确实能够做到页面的充分解耦,例如你删除了这个页面,不需要再去删除配置文件的内容,而且API注册允许你参与到页面的初始化过程来,由你本身来处理页面的初始化,并且可以添加一些业务逻辑。
    说明:在Object-C里面,当类被加载(不是初始化)的时候会触发load方法,KVRouter运用了该特性进行API注册。

    • 简单注册
    + (void)load {
        //正常注册,内部使用init进行页面的初始化
        [KVRouter registerUrl:@"pushone" withClass:[self class]];
    }
    
    • 自定义注册
    + (void)load {
        //自定义注册
        [KVRouter registerUrl:@"pushone" withClass:[self class] toHandler:^UIViewController *(NSDictionary *parameter) {
            //如果该页面是需要登录才能访问,可以在这里加一个判定,然后返回nil,避免逻辑错误
    //        if (未登录) {
    //            return nil;
    //        }
            //实现自定义初始化
            PushOneController * vc = [PushOneController new];
            //也可以在这里进行赋值或者其他初始化逻辑
            return vc;
        }];
    }
    

    3.2 跳转

    KVRouter跳转页面支持push和present方式(其实也就这两种方式)。

    • push
        //获取到页面实例后再跳转
        UIViewController * vc = [KVRouter getObjectWithUrl:@"pushone?id=1"];
        if (vc) {
            [self.navigationController pushViewController:vc animated:YES];
        }
        //以下为快速push,需要提前设置全局导航控制器
    //    [KVRouter openUrl:@"pushone"];
        //携带参数
    //    [KVRouter openUrl:@"pushone?id=1"];
        //携带多参数
    //    [KVRouter openUrl:@"pushone?id=1" parameter:@{@"user" : @"kevin"}];
        //传入导航控制器进行跳转
    //    [KVRouter openUrl:@"pushone" withNavigationController:self.navigationController];
    

    PS:iOS的push界面需要一个导航控制器,如果你想使用快速跳转的话需要提前设置KVRouter的全局导航控制器,内部将会使用这个导航控制器进行push跳转,如果不设置,那么KVRouter是不做跳转的,另外,有些App是通过TabbarController来管理多页面的,并且也是通过多个导航控制器来管理,那么每切换一个不同的导航控制器都需要更新KVRouter的导航控制器,这一点在Demo里面有体现

    • present
        //这个页面是通过配置文件进行注册的,仅能通过init方法初始化
        //如果需要在该控制器在包装一层导航栏,可以获取到该控制器实例后再自行包装
    //    UIViewController * vc = [KVRouter getObjectWithUrl:@"presenttwo"];
    //    if (vc) {
    //        [self presentViewController:vc animated:YES completion:nil];
    //    }
        //快速present
    //    [KVRouter presentUrl:@"presenttwo"];
        //携带参数
    //    [KVRouter presentUrl:@"presenttwo?id=1"];
        //携带多参数,设置代理,协议代理是一种强耦合关系,无法取消
        PresentTwoController * vc = (PresentTwoController*)[KVRouter presentUrl:@"presenttwo?id=1" parameter:@{@"user" : @"kevin"}];
        vc.delegate = self;
        //传入来源控制器进行present
    //    [KVRouter presentUrl:@"presenttwo" sourceViewController:self];
    

    PS:快速present操作也需要提前设置来源控制器,一般使用App的根控制器即可,不需要频繁更换,因为present本来就是一种完全沉浸式的用户体验

    3.3 获取参数

    一般在跳转页面的时候都是需要传值的,KVRouter使用一个很巧妙的设计来实现页面的传值,同样遵循解耦原则。

    @interface NSObject (KVRouter)
    
    /**
     发送参数
    
     @param parameter 参数
     */
    - (void)routerSendParameter:(NSDictionary *)parameter;
    
    /**
     用于传参的分类方法
     使用方法:
     在控制器内部重写这个方法,如果有传参,那么会调用这个方法
     
     @param router 路由器
     @param parameter 传递的参数
     */
    - (void)router:(KVRouter *)router getParameter:(NSDictionary *)parameter;
    
    @end
    

    KVRouter使用了类别来给每个NSObject类都增加了一个方法,当获取到参数的时候会调用该方法传值,我们都知道在Object-C所有类都继承自NSObject,所以只需要在需要接受参数的页面重写该方法就能接受到参数。
    接受参数的示例代码如下:

    - (void)router:(KVRouter *)router getParameter:(NSDictionary *)parameter {
        NSLog(@"%@\n接收参数%@", NSStringFromClass([self class]), parameter);
    }
    

    4 关于Swift版本的介绍

    Swift版本的KVRouter仅仅是使用了Swift语言重写,各个方法的命名,实现原理以及使用方法都与Object-C一致,但是由于Swift与Object-C还是存在一些差异,所以Swift版本的KVRouter无法跟OC一样使用API注册方式进行注册页面。
    KVRouter的API注册依赖于OC的类load方法进行注册,但是Swift不让使用load方法,那么KVRouter基本上是被断了一臂。


    但是KVRouter的另一种注册方式还是可用的,就是通过配置文件进行批量注册。
    其实只是因为无法在load里面注册页面而已,但是KVRouter的注册API还是可以使用,只不过目前我没有找到一种好的时机去注册页面,如果你们找到了,务必联系我,让我也学习一下。
    另外一个注意点就是,Swift版本需要提前设置一下项目名称,这是由于Swift命名空间的特性,如果不设置,那么无法动态获取类,也就无法初始化页面了。
    let projectname = "KVRouter_Swift"; //项目名称(命名空间),不改的话获取不到控制器实例,
    

    至此,KVRouter已经介绍完毕,接下来讲一下基于KVRouter的扫一扫页面动态定向方案,该方案同样适用于web页面点击需要动态跳转到原生页面的需求,我将会在另一篇文章描述清楚,请戳以下文章链接传送。

    基于KVRouter的页面动态定向方案

    相关文章

      网友评论

          本文标题:(App架构优化)KVRouter,以及基于此路由器的扫一扫动态

          本文链接:https://www.haomeiwen.com/subject/gcnmcftx.html