簡介
當我們在做效能收集時,需要全域性的知道哪個頁面目前在展示,哪個頁面關閉了,從而做一些收集工作,在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和原生之間切換時,生命週期也可以拿到了。