Android上的back键
跟踪Android端back调用链
FlutterActivity->eventDelegate.onBackPressed()->flutterView.popRoute()->navigationChannel.popRoute()
public void onBackPressed() {
if (!this.eventDelegate.onBackPressed()) {
super.onBackPressed();
}
}
看popRoute中做了什么操作:
public void popRoute() {
Log.v("NavigationChannel", "Sending message to pop route.");
this.channel.invokeMethod("popRoute", (Object)null);
}
这里的channel对应为
public NavigationChannel(@NonNull DartExecutor dartExecutor) {
this.channel = new MethodChannel(dartExecutor, "flutter/navigation", JSONMethodCodec.INSTANCE);
}
可以看出,Android端back调用最后会使用flutter/navigation的channel向dart端发送popRoute消息。
dart端处理
在flutter/lib/src/services/system_channels.dart中找到flutter/navigation的channel定义
static const MethodChannel navigation = MethodChannel(
'flutter/navigation',
JSONMethodCodec(),
);
继续跟踪一下flutter/lib/src/widgets/binding.dart中
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) {
switch (methodCall.method) {
case 'popRoute':
return handlePopRoute();
case 'pushRoute':
return handlePushRoute(methodCall.arguments);
}
return Future<dynamic>.value();
}
Future<void> handlePopRoute() async {
for (WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
if (await observer.didPopRoute())
return;
}
SystemNavigator.pop();
}
可以看到handlePopRoute
的处理主要分为两部分
-
WidgetsBindingObserver
的处理 -
SystemNavigator.pop()
先看第一步,跟踪下WidgetsBindingObserver
中observer是什么时候注册的,flutter/packages/flutter/lib/src/widgets/app.dart中
class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserver {
......
@override
void initState() {
super.initState();
......
WidgetsBinding.instance.addObserver(this);
}
@override
Future<bool> didPopRoute() async {
assert(mounted);
final NavigatorState navigator = _navigator?.currentState;
if (navigator == null)
return false;
return await navigator.maybePop();
}
Future<bool> maybePop<T extends Object>([ T result ]) async {
final Route<T> route = _history.last;
assert(route._navigator == this);
final RoutePopDisposition disposition = await route.willPop();
if (disposition != RoutePopDisposition.bubble && mounted) {
if (disposition == RoutePopDisposition.pop)
pop(result);
return true;
}
return false;
}
如果route.willPop
有能pop的东西,则执行pop操作。关注下这里的route.willPop()
Future<RoutePopDisposition> willPop() async {
return isFirst ? RoutePopDisposition.bubble : RoutePopDisposition.pop;
}
这个方法1)在ModelRoute中会被复写成:
Future<RoutePopDisposition> willPop() async {
final _ModalScopeState<T> scope = _scopeKey.currentState;
assert(scope != null);
for (WillPopCallback callback in List<WillPopCallback>.from(_willPopCallbacks)) {
if (!await callback())
return RoutePopDisposition.doNotPop;
}
return await super.willPop();
}
可以看出这里主要是处理_willPopCallbacks
,那这个callback什么时候会被赋值呢,这里就引申出来了WillPopScope这个Widget
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (widget.onWillPop != null)
_route?.removeScopedWillPopCallback(widget.onWillPop);
_route = ModalRoute.of(context);
if (widget.onWillPop != null)
_route?.addScopedWillPopCallback(widget.onWillPop);
}
@override
void didUpdateWidget(WillPopScope oldWidget) {
super.didUpdateWidget(oldWidget);
assert(_route == ModalRoute.of(context));
if (widget.onWillPop != oldWidget.onWillPop && _route != null) {
if (oldWidget.onWillPop != null)
_route.removeScopedWillPopCallback(oldWidget.onWillPop);
if (widget.onWillPop != null)
_route.addScopedWillPopCallback(widget.onWillPop);
}
}
所以我们平时用WillPopScope可以进行back的拦截处理,判断要不要做页面返回。
WillPopScope({ Key key, @required this.child, @required this.onWillPop, })
2)在LocalHistoryRoute中会被复写成:
@override
Future<RoutePopDisposition> willPop() async {
if (willHandlePopInternally)
return RoutePopDisposition.pop;
return await super.willPop();
}
@override
bool get willHandlePopInternally {
return _localHistory != null && _localHistory.isNotEmpty;
}
@override
bool didPop(T result) {
if (_localHistory != null && _localHistory.isNotEmpty) {
final LocalHistoryEntry entry = _localHistory.removeLast();
assert(entry._owner == this);
entry._owner = null;
entry._notifyRemoved();
if (_localHistory.isEmpty)
changedInternalState();
return false;
}
return super.didPop(result);
}
这里的_localHistory
通过addLocalHistoryEntry进行添加
void addLocalHistoryEntry(LocalHistoryEntry entry) {
assert(entry._owner == null);
entry._owner = this;
_localHistory ??= <LocalHistoryEntry>[];
final bool wasEmpty = _localHistory.isEmpty;
_localHistory.add(entry);
if (wasEmpty)
changedInternalState();
}
可以看出如果_localHistory中有entry,会先处理entry中设置的onRemove
事件,常见的有drawer侧边栏中:
_historyEntry = LocalHistoryEntry(onRemove: _handleHistoryEntryRemoved);
route.addLocalHistoryEntry(_historyEntry);
void _handleHistoryEntryRemoved() {
_historyEntry = null;
close();
}
即,侧边栏展开操作会向route中添加localHistoryEntry,表示展开状态下,back键会触发drawer的close。
回到之前flutter/lib/src/widgets/binding.dart中handlePopRoute
方法,分析完WidgetsBindingObserver
的处理后,再看看SystemNavigator.pop()
。
这个方法很简单,通过channel发送SystemNavigator.pop方法给Nativie端。
static Future<void> pop() async {
await SystemChannels.platform.invokeMethod<void>('SystemNavigator.pop');
}
其中SystemChannels.platform为
static const MethodChannel platform = OptionalMethodChannel(
'flutter/platform',
JSONMethodCodec(),
);
这时候又回到Native端,找到channel对应的Native端类PlatformChannel,
case "SystemNavigator.pop":
platformMessageHandler.popSystemNavigator();
result.success(null);
break;
@Override
public void popSystemNavigator() {
PlatformPlugin.this.popSystemNavigator();
}
PlatformPlugin中popSystemNavigator
方法为
private void popSystemNavigator() {
this.activity.finish();
}
channel双端定义
插一句,上文中提到的一些通用channel Native侧是在flutterview中初始化的
public class FlutterView extends SurfaceView implements BinaryMessenger, TextureRegistry {
private static final String TAG = "FlutterView";
private final DartExecutor dartExecutor;
private final FlutterRenderer flutterRenderer;
private final NavigationChannel navigationChannel;
private final KeyEventChannel keyEventChannel;
private final LifecycleChannel lifecycleChannel;
private final LocalizationChannel localizationChannel;
private final PlatformChannel platformChannel;
private final SettingsChannel settingsChannel;
private final SystemChannel systemChannel;
dart侧是在WidgetsBinding中进行设置
mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
......
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
SystemChannels.system.setMessageHandler(_handleSystemMessage);
}
总结
总结一下基本流程:
- back事件由native端onBackPressed监听到,通navigation channel 传递到dart端,
- dart端先判断WidgetsBindingObserver列表中有无处理didPopRoute的observer,这里didPopRoute方法会调用
route.willPop
方法判断是否能pop,注意这里的willPop
可以通过WillPopScope
,LocalHistoryEntry
进行自己处理。 - 如果没做特殊处理,则会判断dart端有无可弹出的route,有则弹出,没有则会通过
SystemChannels.platform
通知到Native端。