Skip to content

Commit 03cd972

Browse files
author
L3475
committed
flutter与原生页面跳转如何使用同一个路由层解决问题
1 parent 2810968 commit 03cd972

File tree

2 files changed

+235
-1
lines changed

2 files changed

+235
-1
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1-
# Yee-blog
1+
22
记录遇到的问题以及如何解决的。
3+
4+
5+
* [在现有App中接入flutter,如何改造路由层代价最小](在现有App中接入flutter,如何改造路由层代价最小.md)
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
## 在现有App中接入flutter,如何改造路由层代价最小
2+
3+
我能想到的
4+
5+
* 与原生模块隔离
6+
* 与原生公用一套页面跳转逻辑
7+
* flutter如果接入了多个模块的多功能,消息规范可能还不一样,如何兼容。
8+
9+
10+
### 从页面跳转说起
11+
12+
webView,reactNative,flutter页面与原生交互无非就这两种。
13+
14+
* flutter打开原生页面
15+
* 原生页面跳转flutter页面
16+
17+
### 现有条件
18+
19+
* BCRouter路由以及拦截器
20+
* flutter端 使用的了flutterBoost组件
21+
22+
#### BCRouter路由以及拦截器
23+
24+
25+
`BCRouterService`是以URL-Block的方式实现的路由,将URL与页面建立联系,页面跳转的时候通过url找到对应的映射关系。
26+
27+
主要方法如下:
28+
29+
```
30+
//以scheme区分模块或者不同的app
31+
+ (instancetype)routesForScheme:(NSString *)scheme;
32+
//以serviceName【URL】与Block绑定,进行页面跳转
33+
- (void)registerServiceName:(NSString*)serviceName
34+
impClass:(Class)impClass
35+
handler:(nullable BCRouterCustomHandler)handler;
36+
//URL查询对应的Block,进行页面的跳转
37+
+ (BOOL)openURL:(NSURL *)URL
38+
withParams:(NSDictionary<NSString *, NSDictionary<NSString *, id> *> *)params;
39+
40+
```
41+
42+
43+
44+
```
45+
//URL查询对应的Block,进行页面的跳转
46+
+ (BOOL)openURL:(NSURL *)URL
47+
withParams:(nullable NSDictionary<NSString *, NSDictionary<NSString *, id> *> *)params
48+
andThen:(nullable void(^)(NSString *pathComponentKey, id obj, id returnValue))then completion:(void(^)(NSError *error, id response))completion{
49+
if (![self canOpenURL:URL]) {
50+
return NO;
51+
}
52+
NSString *scheme = URL.scheme;
53+
BCRouterService *router = [self routesForScheme:scheme];
54+
NSString *serviceImpl = [[router servicesDict] objectForKey:[serviceName lowercaseString]];
55+
BCServicePathComponent *component = [[router servicesDict] objectForKey:[serviceName lowercaseString]];
56+
//找到对应的实现Class
57+
Class mClass = NSClassFromString(component.impClass);
58+
}
59+
```
60+
61+
62+
63+
拦截器:通过URL查找的时候,首先询问拦截器是否能够处理 URL,如果能处理就将URL交给拦截器处理。
64+
比如登录拦截器,将需要登录的页面对应的URL都丢到loginInterceptor中,页面跳转时,url会在登录拦截器中响应。
65+
66+
67+
68+
拦截器部分方法
69+
70+
```
71+
@protocol BCRouterInterceptorProtocol <NSObject>
72+
73+
//拦截器优先级 越大越优先
74+
- (NSInteger)priority;
75+
//查询是否已经注入到拦截器
76+
- (BOOL)canOpenURL:(NSURL *)URL;
77+
//依赖注入
78+
- (void)registerServiceName:(NSString*)serviceName
79+
impClass:(Class)impClass;
80+
81+
//处理响应【返回NO代表不需要响应,返回YES说需要进行其他处理】
82+
//返回YES之后,会执行内部处理逻辑
83+
- (BOOL)dealWithURL:(NSURL*)URL parms:(NSDictionary<NSString *, id>*)parms
84+
andThen:(void(^)(void))then;
85+
- (BOOL)dealWithURL:(NSURL*)URL andThen:(void(^)(void))then;
86+
```
87+
88+
89+
90+
拦截器处理URL,不能处理,再交给路由去处理。
91+
92+
```
93+
BOOL canDealURL = BCGlobal_RouteInterceptorListDealURL(URL,allParams);
94+
if (canDealURL) {
95+
return YES;
96+
}
97+
```
98+
99+
100+
```
101+
+ (void)registerInterceptor:(id<BCRouterInterceptorProtocol>)interceptor{
102+
BCGlobal_RouteInterceptorListAdd(interceptor);
103+
}
104+
static BOOL BCGlobal_RouteInterceptorListDealURL(NSURL *URL,NSDictionary*parms) {
105+
BCGlobal_RouteInterceptorListInit();
106+
BOOL canDeal = NO;
107+
dispatch_semaphore_wait(BCGlobal_InterCeptorlistLock, DISPATCH_TIME_FOREVER);
108+
for (id<BCRouterInterceptorProtocol>object in BCGlobal_routeInterceptorList) {
109+
canDeal = [object dealWithURL:URL parms:parms andThen:^{}];
110+
if (canDeal) break;
111+
}
112+
dispatch_semaphore_signal(BCGlobal_InterCeptorlistLock);
113+
return canDeal;
114+
}
115+
```
116+
117+
#### flutterBoost在iOS端的相关实现
118+
119+
120+
`FlutterBoostDelegate`协议实现,flutter端打开、关闭原生页面,会执行到下面的具体实现。
121+
122+
123+
```
124+
///如果框架发现您输入的路由表在flutter里面注册的路由表中找不到,那么就会调用此方法来push一个纯原生页面
125+
- (void) pushNativeRoute:(NSString *) pageName arguments:(NSDictionary *) arguments;
126+
127+
///当框架的withContainer为true的时候,会调用此方法来做原生的push
128+
- (void) pushFlutterRoute:(FlutterBoostRouteOptions *)options;
129+
130+
///当pop调用涉及到原生容器的时候,此方法将会被调用
131+
- (void) popRoute:(FlutterBoostRouteOptions *)options;
132+
133+
```
134+
135+
136+
原生打开flutter页面,我们需要借助 ` [[FlutterBoost instance]open:options]`这样的操作。
137+
138+
```
139+
FlutterBoostRouteOptions *options = [[FlutterBoostRouteOptions alloc] init];
140+
options.pageName = @"flutter/testPage2";
141+
[[FlutterBoost instance]open:options];
142+
```
143+
144+
145+
### 思考问题
146+
147+
148+
如果遇到如下问题,我们该怎么办??
149+
150+
* app已经唤醒,停留在具体的某一页面 推送通知打开app内的任意页面
151+
* 某一个具体页面,点击cell时候跳转的链接由后台配置
152+
153+
154+
容易想到的解决方法是这样
155+
156+
```
157+
if (type == 原生) {
158+
if (id == "页面1"){
159+
vc = ...
160+
}else if (id == "页面2"){
161+
vc = ...
162+
}else if (id == "页面3"){
163+
vc = ...
164+
}else if (id == "..."){
165+
vc = ...
166+
}
167+
//做页面跳转
168+
[self.currentVC.navigationController pushViewController: vc animated:YES];
169+
}else{
170+
flutter页面跳转
171+
FlutterBoostRouteOptions *options = [[FlutterBoostRouteOptions alloc] init];
172+
options.pageName = @"flutter/testPage2";
173+
[[FlutterBoost instance]open:options];
174+
}
175+
```
176+
177+
想想这些代码散在app的各个角落,新增或修改的时候成本只会越来越大。<br>
178+
但是别忘了我们的路由层可以帮我们解决原生那一堆判断操作。改成下面的代码如下面。
179+
180+
```
181+
if (type == 原生) {
182+
NSURL *URL = ...
183+
[BCRouterService openURL: URL]
184+
}else{
185+
// flutter页面跳转
186+
FlutterBoostRouteOptions *options = [[FlutterBoostRouteOptions alloc] init];
187+
options.pageName = @"flutter/testPage2";
188+
[[FlutterBoost instance]open:options];
189+
}
190+
```
191+
192+
还是有问题,只要遇到原生与flutter都要这样写还是很麻烦,还没法扩展。<br>
193+
如何可以写成下面这样的,既做到多端统一,也解决了硬编码的问题。
194+
195+
```
196+
NSURL *URL = ...
197+
[BCRouterService openURL: URL]
198+
```
199+
200+
这样写,就代表着我们需要在router层里面这样写.
201+
202+
```
203+
if (URL == 原生) {
204+
//处理原生部分
205+
}else{
206+
// flutter页面跳转
207+
FlutterBoostRouteOptions *options = [[FlutterBoostRouteOptions alloc] init];
208+
options.pageName = @"URL";
209+
[[FlutterBoost instance]open:options];
210+
}
211+
```
212+
上面的写法,已经解决了我们90%的问题,代码只会在路由层,维护路由层就行。<br>
213+
但是我们要思考这样写是不是:用到路由组件的应用都要导入flutter组件,显然这违反了一个公共组件上下依赖关系【怎么形容,就是不应该这样写】。那我们又不能省略这段代码,有没有方法把这段代码隔离出来,有的,我们的拦截器。
214+
215+
216+
217+
#### flutter拦截器
218+
219+
我刚开始写的时候想到这个组件肯定要满足下面的条件:
220+
221+
1. 将flutter业务与原生业务隔离,可插拔
222+
2. flutter业务可继续扩展,flutter中的多个模块可细分,只需要在这个全局的拦截器中做文章
223+
224+
225+
flutter牵涉到原生的逻辑该怎么办??,比如登录逻辑,我要写在那个模块合适?
226+
227+
flutter模块的全部功能原则上都是独立,登录必须写在这个拦截器里面。不能与原生的拦截器搞在一块。当然写在一块功能肯定能实现,但是后续的维护成本可是不小,必须在开始的时候就把规则定好。
228+
229+
230+
231+

0 commit comments

Comments
 (0)