In normal development, users click on the avatar and enter the personal homepage. This seemingly ordinary operation is very likely to involve multiple modules. For example: The playback page of the video module has music related to the video. Clicking on these music requires jump to the playback page of the music module. In this way, dependence or coupling will inevitably occur between the video and the music module. This problem makes people feel sad. I believe many friends have done this, writing some agents or notifications, and constantly passing events; sometimes they simply import another module directly.
Because I develop independently in the company and take less into account, I can practice the company's projects. In the process of trying to make components, I learned about routing, which is of great help to solve the above problems. Therefore, I want to summarize and share it with you.
What is the mobile routing layer:
The concept of routing layer on the server side refers to the hierarchical analysis of url requests, which distributes a request to the corresponding application handler. The routing layer of the mobile terminal refers to a logical layer that distributes page access, access requests for H5 and App access, and access requests between Apps.
Problems that need to be solved in the mobile routing layer:
1. Provide remote access to the outside world to realize cross-application call response, including H5 application calls, other App application calls, system access calls, etc.
2. Definitions of native pages, modules, components, etc. are collectively called Resources. On the premise that the business performance implemented across application calls and routing layers needs to be consistent on different ends, resources need to be defined. When routing provides internal request distribution, it can provide functions that do not rely on external resource definitions.
3. How to use Uniform to represent resources in external calls
4. How to define the access request process uniformly on the mobile side, so as to achieve the unity between the mobile side and the web side
5. How to better compatible with iOS and Android system access mechanisms, App link protocols, web routing mechanisms and front-end development specifications, etc.
6. How to be compatible with the page navigation mechanism of various platforms (Android, iOS)
7. How to solve the problem of secure access
8. Mobile terminal dynamic configuration on the client
Scenarios used for mobile routing:
0. Interaction between H5 pages and App native pages, modules and components
Intercommunication with the App
Internal page jump, module scheduling and component loading, etc.
3. The push and notification system uncoded logic, dynamically access native resources, and better support dynamic page access and logical execution through notification and push.
Dynamically call the resources of the main app
Implement more complex architecture MVVM or VIPER architecture, providing the ability to remove business interdependence
6. Engineering transformation with the purpose of componentization, isolate each business, and create separate components
Interface preview
Router
NS_ASSUME_NONNULL_BEGIN @interface SJRouter : NSObject + (instancetype)shared; - (void)handleRequest:(SJRouteRequest *)request completionHandler:(SJCompletionHandler)completionHandler; @end NS_ASSUME_NONNULL_END
RouteRequest
NS_ASSUME_NONNULL_BEGIN @interface SJRouteRequest : NSObject - (instancetype)initWithURL:(NSURL *)URL; - (instancetype)initWithPath:(NSString *)requestPath parameters:(nullable SJParameters)parameters; @property (nonatomic, strong, readonly) NSString *requestPath; @property (nonatomic, strong, readonly, nullable) SJParameters prts; - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; @end NS_ASSUME_NONNULL_END
RouteHandlerProtocol
NS_ASSUME_NONNULL_BEGIN typedef id SJParameters; @protocol SJRouteHandler + (NSString *)routePath; + (void)handleRequestWithParameters:(nullable SJParameters)parameters topViewController:(UIViewController *)topViewController completionHandler:(nullable SJCompletionHandler)completionHandler; @end NS_ASSUME_NONNULL_END
process
Simply put, in the app application, the route recognizes a request and dispatches it to the corresponding handler for processing. This process is very much like sending a network request (split parameters => initiate request => callback).
Similarly, when Router receives the following request (request video playback page):
- (void)push:(id)sender { SJRouteRequest *request = [[SJRouteRequest alloc] initWithPath:@"video/playbackInfo" parameters:@{@"video_id":@(111)}]; [ handleRequest:request completionHandler:^(id _Nullable result, NSError * _Nullable error) { #ifdef DEBUG NSLog(@"%d - %s", (int)__LINE__, __func__); #endif }]; }
It will try to identify the route, find the matching handler, and pass the necessary parameters:
@implementation SJRouter - (void)handleRequest:(SJRouteRequest *)request completionHandler:(SJCompletionHandler)completionHandler { NSParameterAssert(request); if ( !request ) return; Class<SJRouteHandler> handler = _handlersM[]; if ( handler ) { [handler handleRequestWithParameters: topViewController:_sj_get_top_view_controller() completionHandler:completionHandler]; } else { printf("\n (-_-) Unhandled request: %s", .UTF8String); } } @end
Finally, handler handles.
@implementation TestViewController + (NSString *)routePath { return @"video/playbackInfo"; } + (void)handleRequestWithParameters:(nullable SJParameters)parameters topViewController:(UIViewController *)topViewController completionHandler:(nullable SJCompletionHandler)completionHandler { TestViewController *vc = [TestViewController new]; = completionHandler; [ pushViewController:vc animated:YES]; } @end
At this point, let's look back at the example we just gave:
The play page of the video module has music related to the video. Clicking on these music requires you to jump to the play page of the music module.
At this time, the video module can rely on Router to make a jump request. This seems to be all dependencies, but in fact, the two are very different.
- Routers can not only handle requests to jump to music modules, but also depend on multiple ones to only rely on Router. . .
- When deleting a dependent module, you need to delete the dependent code, which is very annoying, right?
- Bar, bar, bar, bar, bar. . .
So click on the jump to the music module, which can be replaced by the following operation and initiate a request:
SJRouteRequest *request = [[SJRouteRequest alloc] initWithPath:@"audio/playbackInfo" parameters:@{@"audio_id":@(232)}]; [ handleRequest:request completionHandler:^(id _Nullable result, NSError * _Nullable error) { #ifdef DEBUG NSLog(@"%d - %s", (int)__LINE__, __func__); #endif }];
The router finds the corresponding handler and lets it process it.
Handler
From the beginning to now, it can be seen that Handler is the guy who finally executes the request. I believe everyone has questions, how to become a Handler?
It's simple, it's automatic (see Router), and as long as a class complies with the SJRouteHandlerProtocol, it becomes a Handler. Let's look at the agreement again.
NS_ASSUME_NONNULL_BEGIN typedef id SJParameters; @protocol SJRouteHandler + (NSString *)routePath; + (void)handleRequestWithParameters:(nullable SJParameters)parameters topViewController:(UIViewController *)topViewController completionHandler:(nullable SJCompletionHandler)completionHandler; @end NS_ASSUME_NONNULL_END
- routePath: that is, the path, which represents the path that the handler can handle. When a request is initiated, the Router will obtain the corresponding handler through the path and hand it over to it for processing.
- handleRequestWithParameters. . . : Processing performed by handler.
Router
During the entire request process, what Router does is essentially looking for the destined one among the many Handlers. How to find it? Why does it automatically become a Handler if you comply with SJRouteHandlerProtocol?
This is naturally due to the powerful power of Runtime. Let’s first look at how to achieve it.
@implementation SJRouter - (instancetype)init { self = [super init]; if ( !self ) return nil; _handlersM = [NSMutableDictionary new]; int count = objc_getClassList(NULL, 0); Class *classes = (Class *)malloc(sizeof(Class) * count); objc_getClassList(classes, count); Protocol *p_handler = @protocol(SJRouteHandler); for ( int i = 0 ; i < count ; ++ i ) { Class cls = classes[i]; for ( Class thisCls = cls ; nil != thisCls ; thisCls = class_getSuperclass(thisCls) ) { if ( !class_conformsToProtocol(thisCls, p_handler) ) continue; if ( ![(id)thisCls respondsToSelector:@selector(routePath)] ) continue; if ( ![(id)thisCls respondsToSelector:@selector(handleRequestWithParameters:topViewController:completionHandler:)] ) continue; _handlersM[[(id<SJRouteHandler>)thisCls routePath]] = thisCls; break; } } if ( classes ) free(classes); return self; } @end
- objc_getClassList: Obviously, get all the App classes.
- class_conformsToProtocol: Whether this class complies with a certain protocol.
Thanks to these two functions of Runtime, you can get many Handlers. When making a request, it would be easy to find the one destined among the many Handlers.
Request
The jump request initiated by the App is more about jumping between internal pages. What we need to pay attention to most is its path, so the entire route revolves around the path, and schemes and hosts in URLs are not very effective. At least in my project, I used SDKs like Youmeng to process it.
Therefore, Request holds the path to each request, as well as necessary parameters, and no extra operations are available afterwards.
OK, that's all.
Below is the project address. If you are interested, you can communicate with me. . .
/changsanjiang/SJRouter
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.