Flutter-Android返回事件分發(fā)源碼分析

一.Activity中點擊返回鍵

1、Activity響應(yīng)返回事件赠摇,由onBackPressed方法處理

  @Override
  public void onBackPressed() {
    if (stillAttachedForEvent("onBackPressed")) {
        //delegate為FlutterActivityAndFragmentDelegate實例藕帜,實際負責與Flutter交互的類
      delegate.onBackPressed();
    }
  }

delegateFlutterActivityAndFragmentDelegate的實例,代理Android與Flutter交互的所有事件洽故。

2、FlutterActivityAndFragmentDelegate.onBackPressed()

  void onBackPressed() {
    ...
    if (flutterEngine != null) {
      ...
      //getNavigationChannel為NavigationChannel的實例隘弊,把事件傳遞到Flutter端
      flutterEngine.getNavigationChannel().popRoute();
    } else {
     ...
    }
  }

3撞秋、NavigationChannel.popRoute()

  public NavigationChannel(@NonNull DartExecutor dartExecutor) {
    this.channel = new MethodChannel(dartExecutor, "flutter/navigation", JSONMethodCodec.INSTANCE);
  }

  public void popRoute() {
    ...
    //將返回事件分發(fā)到Flutter
    channel.invokeMethod("popRoute", null);
  }

二.Flutter注冊Channel

FlutterApp在啟動時會注冊與Native交互的Channel

//Flutter App的啟動入口
void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {}

mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
  @override
  void initInstances() {
    super.initInstances();
    ...
    //加載Native NavigationChannel嚣鄙,并處理Navtive傳遞過來的事件
    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
    ...
  }
}  

Flutter返回鍵處理哑子,這里popRoute對應(yīng)Native上點擊返回按鈕

  Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) {
    switch (methodCall.method) {
      case 'popRoute':
        //處理返回事件
        return handlePopRoute();
      case 'pushRoute':
        return handlePushRoute(methodCall.arguments as String);
      case 'pushRouteInformation':
        return _handlePushRouteInformation(methodCall.arguments as Map<dynamic, dynamic>);
    }
    return Future<dynamic>.value();
  }

三.Flutter事件分發(fā)

事件優(yōu)先有Flutter消耗肌割,如果Flutter沒有消耗帐要,最終把事件傳回Native。

Future<void> handlePopRoute() async {
    //Flutter內(nèi)部分發(fā)返回事件
  for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
    if (await observer.didPopRoute())
      return;
  }
  //如果Flutter不處理奋早,交由Native處理
  SystemNavigator.pop();
}

四.Flutter內(nèi)部事件分發(fā)

  for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
    if (await observer.didPopRoute())
      return;
  }

1赠橙、_observers是一個List<WidgetsBindingObserver>類型的對象

WidgetsBindingObserver代碼如下:

abstract class WidgetsBindingObserver {
 
    //在Android中點擊返回鍵時會觸發(fā)這個函數(shù)
    //如果返回true表示消費這個事件
  Future<bool> didPopRoute() => Future<bool>.value(false);
  ...
}

2、WidgetsBindingObserver子類:_WidgetsAppState - WidgetsApp

class WidgetsApp extends StatefulWidget {

    //一個好多參數(shù)的構(gòu)造方法
  WidgetsApp({
  ...,
  backButtonDispatcher = null,
  })
  
  //一個命名構(gòu)造函數(shù)
  WidgetsApp.router({
  ...,
  backButtonDispatcher = backButtonDispatcher ?? RootBackButtonDispatcher(),
  })
  
  State<WidgetsApp> createState() => _WidgetsAppState();
}

class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
  @override
  void initState() {
    ...
    //把自身注冊到_observers
    WidgetsBinding.instance!.addObserver(this);
  }  
 
  // On Android: the user has pressed the back button.
  //每個頁面的返回事件由這邊處理
  @override
  Future<bool> didPopRoute() async {
    // The back button dispatcher should handle the pop route if we use a router.
    //使用MaterialApp.router這個方法才會走到這里
    if (_usesRouter)
      return false;
    //大部分情況下都會走到這~
    final NavigatorState? navigator = _navigator?.currentState;
    if (navigator == null)
      return false;
    return navigator.maybePop();
  }
}

3掉奄、WidgetsApp被使用的時機

在使用MaterialApp時姓建,內(nèi)部會創(chuàng)建WidgetsApp對象來處理返回事件缤苫,并注冊到_observers

class MaterialApp extends StatefulWidget {

    const MaterialApp({...})
    
    const MaterialApp.router({...})
  ...
  @override
  State<MaterialApp> createState() => _MaterialAppState();  
}

class _MaterialAppState extends State<MaterialApp> {
  ...
  @override
  Widget build(BuildContext context) {
    Widget result = _buildWidgetApp(context);
    ...
    return ScrollConfiguration(
      ...
      child: HeroControllerScope(
       ...
        child: result,
      ),
    );
  }  
  
  Widget _buildWidgetApp(BuildContext context) {
    ...
    if (_usesRouter) {
      return WidgetsApp.router(...);
    }  
    return WidgetsApp(...);
  }  
}

4、NavigatorState.maybePop()處理返回事件活玲,WillPopScope也會在這個方法進行處理

  Future<bool> maybePop<T extends Object?>([ T? result ]) async {
    final _RouteEntry? lastEntry = _history.cast<_RouteEntry?>().lastWhere(
      (_RouteEntry? e) => e != null && _RouteEntry.isPresentPredicate(e),
      orElse: () => null,
    );
    if (lastEntry == null)
      return false;
    //WillPopScope會被注冊到這個地方處理
    final RoutePopDisposition disposition = await lastEntry.route.willPop(); // this is asynchronous
    if (!mounted)
      return true; // forget about this pop, we were disposed in the meantime
    final _RouteEntry? newLastEntry = _history.cast<_RouteEntry?>().lastWhere(
      (_RouteEntry? e) => e != null && _RouteEntry.isPresentPredicate(e),
      orElse: () => null,
    );
    if (lastEntry != newLastEntry)
      return true; // forget about this pop, something happened to our history in the meantime
    switch (disposition) {
      case RoutePopDisposition.bubble:
        return false;
      case RoutePopDisposition.pop:
        pop(result);
        return true;
      case RoutePopDisposition.doNotPop:
        return true;
    }
  }

五.事件交由Native處理

  //如果Flutter不處理,交由Native處理
  SystemNavigator.pop();
  
  //把事件分發(fā)到Native
  static Future<void> pop({bool? animated}) async {
    await SystemChannels.platform.invokeMethod<void>('SystemNavigator.pop', animated);
  }

Native接收到Flutter發(fā)送的SystemNavigator.pop

  //最終由Android-PlatformPlugin處理
  private void popSystemNavigator() {
    //FlutterActivity中platformPluginDelegate=null屑柔,所以不會走到此處
    if (platformPluginDelegate != null && platformPluginDelegate.popSystemNavigator()) {
      // A custom behavior was executed by the delegate. Don't execute default behavior.
      return;
    }
    //最后看到的現(xiàn)象就是Activity退出了~
    if (activity instanceof OnBackPressedDispatcherOwner) {
      ((OnBackPressedDispatcherOwner) activity).getOnBackPressedDispatcher().onBackPressed();
    } else {
      activity.finish();
    }
  }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末掸宛,一起剝皮案震驚了整個濱河市招拙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌别凤,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件求豫,死亡現(xiàn)場離奇詭異,居然都是意外死亡最疆,警方通過查閱死者的電腦和手機蚤告,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來获诈,“玉大人心褐,你說我怎么就攤上這事∶始牛” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵昼伴,是天一觀的道長镣屹。 經(jīng)常有香客問我,道長女蜈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任逸寓,我火速辦了婚禮覆山,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘勋篓。我一直安慰自己魏割,他們只是感情好,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布拜银。 她就那樣靜靜地躺著,像睡著了一般盐股。 火紅的嫁衣襯著肌膚如雪耻卡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天幌蚊,我揣著相機與錄音溃卡,去河邊找鬼。 笑死瘸羡,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的队他。 我是一名探鬼主播峻村,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼垢啼!你這毒婦竟也來了张肾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤放刨,失蹤者是張志新(化名)和其女友劉穎尸饺,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浪听,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年掉分,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片华坦。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡不从,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出歹袁,到底是詐尸還是另有隱情寝优,我是刑警寧澤条舔,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布孟抗,位于F島的核電站钻心,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏扔役。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一坯钦、第九天 我趴在偏房一處隱蔽的房頂上張望侈玄。 院中可真熱鬧,春花似錦序仙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至宾添,卻和暖如春柜裸,著一層夾襖步出監(jiān)牢的瞬間粱锐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工铐然, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留海雪,地道東北人舱殿。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像湾宙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子侠鳄,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容