简介
当我们在做性能收集时,需要全局的知道哪个页面目前在展示,哪个页面关闭了,从而做一些收集工作,在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和原生之间切换时,生命周期也可以拿到了。