当前位置: 首页 > 工具软件 > AspectD > 使用案例 >

Flutter aspectd(五)全局监控flutter生命周期

康烨伟
2023-12-01

简介

当我们在做性能收集时,需要全局的知道哪个页面目前在展示,哪个页面关闭了,从而做一些收集工作,在Android中我们可以通过registerActivityLifecycleCallbacks来得到任何一个正在展示页面的生命周期

如下:

applicationContext.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
  override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {

  }

  override fun onActivityStarted(activity: Activity) {
  }

  override fun onActivityResumed(activity: Activity) {
    
  }

  override fun onActivityPaused(activity: Activity) {
    
  }

  override fun onActivityStopped(activity: Activity) {
  }

  override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
  }

  override fun onActivityDestroyed(activity: Activity) {
  }
})

而在Flutter中并没有提供类似方式,当然我们可以通过一个一个页面的监听,但这样侵入性太强了,现在尝试用aspectd来hook具体执行方法实现生命周期监听。

页面启动

当我们要打开一个新的flutter页面会执行如下:

Navigator.pushNamed(context, RouteHelper.firstPage);

最终会执行navigator.dart中的handlePush方法,hook该方法:

@Execute("package:flutter/src/widgets/navigator.dart", "_RouteEntry", "-handlePush")
@pragma("vm:entry-point")
void _handlePush(PointCut pointCut) {
    print("++++_handlePush++++");
    pointCut.proceed();
    dynamic target = pointCut.target;
    dynamic previousRoute= pointCut.namedParams["previousPresent"];
    HookImpl.getInstance().handlePush(target.route,previousRoute);
}

从该方法中可以得到我们要启动页面的Route,以及当前的页面Route

接下来会调用buildPage方法:

@Execute("package:flutter/src/material/page.dart","MaterialRouteTransitionMixin", "-buildPage")
@pragma("vm:entry-point")
dynamic _buildPage(PointCut pointCut) {
    print("++++_buildPage++++");
    Route target = pointCut.target;
    Semantics widgetResult = pointCut.proceed();
    HookImpl.getInstance().buildPage(
        target, widgetResult.child, pointCut.positionalParams[0]);
    return widgetResult;
}

通过该方法能够得到要启动页面的Route,Widget(更具它能取到页面标题)及BuildContext对象。

最终会绘制这个要启动的页面:

@Execute("package:flutter/src/scheduler/binding.dart", "SchedulerBinding","-handleDrawFrame")
@pragma("vm:entry-point")
void _handleDrawFrame(PointCut pointCut) {
    print("++++_handleDrawFrame++++");
    pointCut.proceed();
    HookImpl.getInstance().handleDrawFrame();
}

当该方法执行完后,页面被绘制出来

页面关闭

当关闭一个页面时,会调用pop方法:

@Execute("package:flutter/src/widgets/navigator.dart", "_RouteEntry", "-handlePop")
@pragma("vm:entry-point")
void _handlePop(PointCut pointCut) {
    print("++++handlePop++++");
    dynamic target = pointCut.target;
    dynamic previousPresent = pointCut.namedParams["previousPresent"];
    pointCut.proceed();
    HookImpl.getInstance().handlePop(target.route, previousPresent);
}

这里的target就是当前要pop的页面对应的Route,而previousPresent是我们要回到的页面对应的Route。

接着就会再次走handleDrawFrame进行绘制我们要回到的页面。

通过整合上面的方法,就可以得到页面的打开和关闭的回调。但是,这些只是Flutter页面之间的跳转,当中间参和原生页面,或者我们切换到后台,再回来就监听不到了。

监听和原生之间切换

flutter页面本质上还是原生的壳,那么我们可以通过监听原生的生命周期,通过channel通知flutter,而在flutter中通过aspectd hook到生命周期切换,拿到当前的页面,从而实现监听

原生监听生命周期

 applicationContext.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
  override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {

  }

  override fun onActivityStarted(activity: Activity) {
  }

  override fun onActivityResumed(activity: Activity) {
    channel.invokeMethod("onActivityResumed", mapOf("activityName" to activity.javaClass.simpleName))
  }

  override fun onActivityPaused(activity: Activity) {
    channel.invokeMethod("onActivityPaused", mapOf("activityName" to activity.javaClass.simpleName))
  }

  override fun onActivityStopped(activity: Activity) {
  }

  override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
  }

  override fun onActivityDestroyed(activity: Activity) {
  }
})

上面在onActivityResumed和onActivityPaused的时候通过channel传递给flutter。

flutter这里接收:

  void init() {
    _channel.setMethodCallHandler(flutterMethod);
  }

  Future<dynamic> flutterMethod(MethodCall call) async{
    if (call.method == 'onActivityResumed') {
      onActivityResumed(call.arguments['activityName']);
    } else if (call.method == 'onActivityPaused') {
      onActivityPaused(call.arguments['activityName']);
    }
  }

  void onActivityResumed(String activityName) {
    print('onActivityResumed:::$activityName');
  }

  void onActivityPaused(String activityName) {
    print('onActivityPaused:::$activityName');
  }

这里抽出来2个方法,供aspect进行hook:

 @Execute(
      "package:lifecycle_detect/lifecycle_detect.dart", "LifecycleDetect", "-onActivityResumed")
  @pragma("vm:entry-point")
  void _onActivityResumed(PointCut pointCut) {
    print("++++onActivityResumed++++");
    pointCut.proceed();
    HookImpl.getInstance().onActivityResumed();
  }

  @Execute(
      "package:lifecycle_detect/lifecycle_detect.dart", "LifecycleDetect", "-onActivityPaused")
  @pragma("vm:entry-point")
  void _onActivityPaused(PointCut pointCut) {
    print("++++onActivityPaused++++");
    pointCut.proceed();
    HookImpl.getInstance().onActivityPaused();
  }

这样,flutter和原生之间切换时,生命周期也可以拿到了。

效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FFbLOFTj-1627289623869)(https://note.youdao.com/yws/api/personal/file/WEBc0c84864ee37b5ad9a6b7d893715f136?method=download&shareKey=3ab68073d17f92690bd577dd0f74a2c5)]

githbu地址

 类似资料: