JLRoutes是一个调用极少代码 , 可以很方便的处理不同URL schemes以及解析它们的参数,并通过回调block来处理URL对应的操作 , 可以用于处理复杂跳转逻辑的三方库.

原理

JLRoutes本质可以理解为:保存一个全局的Map,scheme为Key,JLRoutes为Value,Value是对应存放block的数组,url和block都会常驻在内存中,当打开一个URL时,JLRoutes就可以遍历 , 这个全局的map,通过url来执行对应的block。
image.png

  1. routeControllersMap 是全局的单例字典 , 你可以想象成一个大的盒子 .
    2 . 这个盒子里装了很多的字典 , 而字典的key值对应 一个标识 , 源码中称之为 scheme ,为了不混淆 , 咱们就叫其为JLRoutes对象标识 . 这个标识对应的value值 为JLRoutes类的对象 .
    3. JLRoutes对象有很多属性 , 常用的有两个 , 一个是 scheme 也就是是上述所说的JLRoutes对象标识 , 也就是说 , 此value值记录了自己的key值 . 另外一个属性为 routes数组 , 此数组中存放了 JLRRouteDefinition 对象 .
    4. JLRRouteDefinition对象为最终的具体模型 , 也就是说 你注册的跳转逻辑的所有信息 , 都存在于这个模型中 ,包括要实施操作的block代码块 , JLRoutes对象标识 , 取url内容值的标识

在每个JLRoutes的数组里面,会按照路由的优先级进行排列,优先级高的排列在前面。

  1. - (void)_registerRoute:(NSString *)routePattern priority:(NSUInteger)priority handler:(BOOL (^)(NSDictionary *parameters))handlerBlock
  2. {
  3. JLRRouteDefinition *route = [[JLRRouteDefinition alloc] initWithScheme:self.scheme pattern:routePattern priority:priority handlerBlock:handlerBlock];
  4. if (priority == 0 || self.routes.count == 0) {
  5. [self.routes addObject:route];
  6. } else {
  7. NSUInteger index = 0;
  8. BOOL addedRoute = NO;
  9. // 找到当前已经存在的一条优先级比当前待插入的路由低的路由
  10. for (JLRRouteDefinition *existingRoute in [self.routes copy]) {
  11. if (existingRoute.priority < priority) {
  12. // 如果找到,就插入数组
  13. [self.routes insertObject:route atIndex:index];
  14. addedRoute = YES;
  15. break;
  16. }
  17. index++;
  18. }
  19. // 如果没有找到任何一条路由比当前待插入的路由低的路由,或者最后一条路由优先级和当前路由一样,那么就只能插入到最后。
  20. if (!addedRoute) {
  21. [self.routes addObject:route];
  22. }
  23. }
  24. }

由于这个数组里面的路由是一个单调队列,所以查找优先级的时候只用从高往低遍历即可。
具体查找路由的过程如下:
image.png
首先根据外部传进来的URL初始化一个JLRRouteRequest,然后用这个JLRRouteRequest在当前的路由数组里面依次request,每个规则都会生成一个response,但是只有符合条件的response才会match,最后取出匹配的JLRRouteResponse拿出其字典parameters里面对应的参数就可以了。查找和匹配过程中重要的代码如下:

  1. - (BOOL)_routeURL:(NSURL *)URL withParameters:(NSDictionary *)parameters executeRouteBlock:(BOOL)executeRouteBlock
  2. {
  3. if (!URL) {
  4. return NO;
  5. }
  6. [self _verboseLog:@"Trying to route URL %@", URL];
  7. BOOL didRoute = NO;
  8. JLRRouteRequest *request = [[JLRRouteRequest alloc] initWithURL:URL];
  9. for (JLRRouteDefinition *route in [self.routes copy]) {
  10. // 检查每一个route,生成对应的response
  11. JLRRouteResponse *response = [route routeResponseForRequest:request decodePlusSymbols:shouldDecodePlusSymbols];
  12. if (!response.isMatch) {
  13. continue;
  14. }
  15. [self _verboseLog:@"Successfully matched %@", route];
  16. if (!executeRouteBlock) {
  17. // 如果我们被要求不允许执行,但是又找了匹配的路由response。
  18. return YES;
  19. }
  20. // 装配最后的参数
  21. NSMutableDictionary *finalParameters = [NSMutableDictionary dictionary];
  22. [finalParameters addEntriesFromDictionary:response.parameters];
  23. [finalParameters addEntriesFromDictionary:parameters];
  24. [self _verboseLog:@"Final parameters are %@", finalParameters];
  25. didRoute = [route callHandlerBlockWithParameters:finalParameters];
  26. if (didRoute) {
  27. // 调用Handler成功
  28. break;
  29. }
  30. }
  31. if (!didRoute) {
  32. [self _verboseLog:@"Could not find a matching route"];
  33. }
  34. // 如果在当前路由规则里面没有找到匹配的路由,当前路由不是global 的,并且允许降级到global里面去查找,那么我们继续在global的路由规则里面去查找。
  35. if (!didRoute && self.shouldFallbackToGlobalRoutes && ![self _isGlobalRoutesController]) {
  36. [self _verboseLog:@"Falling back to global routes..."];
  37. didRoute = [[JLRoutes globalRoutes] _routeURL:URL withParameters:parameters executeRouteBlock:executeRouteBlock];
  38. }
  39. // 最后,依旧没有找到任何能匹配的,如果有unmatched URL handler,调用这个闭包进行最后的处理。
  40. if, after everything, we did not route anything and we have an unmatched URL handler, then call it
  41. if (!didRoute && executeRouteBlock && self.unmatchedURLHandler) {
  42. [self _verboseLog:@"Falling back to the unmatched URL handler"];
  43. self.unmatchedURLHandler(self, URL, parameters);
  44. }
  45. return didRoute;
  46. }

举例

注册

  1. [[JLRoutes globalRoutes] addRoute:@"/:object/:action" handler:^BOOL(NSDictionary *parameters) {
  2. NSString *object = parameters[@"object"];
  3. NSString *action = parameters[@"action"];
  4. // stuff
  5. return YES;
  6. }];
  7. // 1. 参数传递需要进行一一对应
  8. [[JLRoutes routesForScheme:@"JLRoutesOne"]addRoute:@"/:ViewController/:userID/:pass"handler:^BOOL(NSDictionary * _Nonnull parameters){
  9. Class class = NSClassFromString(parameters[@"ViewController"]);
  10. NSLog(@"-----------userID : %@",parameters[@"userID"]);
  11. NSLog(@"-----------pass : %@",parameters[@"pass"]);
  12. [navVc pushViewController:[[class alloc]init] animated:YES];
  13. return YES;
  14. }];

我们传入一个URL,让Router进行处理。

跳转点击

  1. - (void)touch{
  2. //中文传输需要进行转义
  3. NSString*url = @"JLRoutesOne://OneNextViewController/我是userID/我是pwd";//中文传输需要进行转义
  4. url = [url stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
  5. [[UIApplication sharedApplication]openURL:[NSURL URLWithString:url] options:nil completionHandler:nil];
  6. }

匹配成功之后,我们会得到下面这样一个字典:

  1. {
  2. "object": "post",
  3. "action": "halfrost",
  4. "debug": "true",
  5. "foo": "bar",
  6. "JLRouteURL": "ele://post/halfrost?debug=true&foo=bar",
  7. "JLRoutePattern": "/:object/:action",
  8. "JLRouteScheme": "JLRoutesGlobalRoutesScheme"
  9. }

把上述过程图解出来,见下图:
image.png
JLRoutes还可以支持Optional的路由规则,假如定义一条路由规则:

  1. /the(/foo/:a)(/bar/:b)

JLRoutes 会帮我们默认注册如下4条路由规则:

  1. /the/foo/:a/bar/:b
  2. /the/foo/:a
  3. /the/bar/:b
  4. /the