Commit 8a556f764036db1d7164b9afd3828bdf4f034e8d

Authored by 吴启风
1 parent 0427feeb

feat:性能优化-首页'wow精选'增加生命周期感知,适时暂停动画

lib/app/app.dart
... ... @@ -12,6 +12,8 @@ import 'package:wow_english/route/route.dart';
12 12  
13 13 import '../route/custom_navigator_observer.dart';
14 14  
  15 +final RouteObserver<PageRoute> customerRouteObserver = CustomNavigatorObserver();
  16 +
15 17 class App extends StatelessWidget {
16 18 const App([this._navigatorObserver]);
17 19  
... ... @@ -51,7 +53,7 @@ class App extends StatelessWidget {
51 53 navigatorObservers: <NavigatorObserver>[
52 54 // 带入ApmNavigatorObserver实例用于路由监听
53 55 // 如果不带入SDK监听器将无法获知页面(PV)入栈退栈行为,错误率(Dart异常数/FlutterPV次数)将异常攀升。
54   - _navigatorObserver ?? ApmNavigatorObserver.singleInstance, CustomNavigatorObserver()
  56 + _navigatorObserver ?? ApmNavigatorObserver.singleInstance, customerRouteObserver
55 57 ],
56 58 ),
57 59 )),
... ...
lib/pages/home/widgets/ShakeImage.dart
1   -
2 1 import 'dart:async';
  2 +import 'dart:ui';
3 3  
4 4 import 'package:flutter/cupertino.dart';
  5 +import 'package:flutter/scheduler.dart';
5 6 import 'package:wow_english/common/extension/string_extension.dart';
6 7  
  8 +import '../../../app/app.dart';
  9 +import '../../../utils/log_util.dart';
  10 +
7 11 ///带左右摇晃的wow封面
8 12 class ShakeImage extends StatefulWidget {
9 13 const ShakeImage({super.key});
... ... @@ -12,16 +16,40 @@ class ShakeImage extends StatefulWidget {
12 16 _ShakeImageState createState() => _ShakeImageState();
13 17 }
14 18  
15   -class _ShakeImageState extends State<ShakeImage> with SingleTickerProviderStateMixin,
16   - WidgetsBindingObserver {
  19 +class _ShakeImageState extends State<ShakeImage>
  20 + with SingleTickerProviderStateMixin, WidgetsBindingObserver, RouteAware {
17 21 late AnimationController _controller;
18 22 late Animation<double> _animation;
19 23 late Timer _timer;
  24 + late final AppLifecycleListener appLifecycleListener;
  25 + static const TAG = 'ShakeImage';
20 26  
21 27 @override
22 28 void initState() {
  29 + var lifecycleState = SchedulerBinding.instance.lifecycleState;
  30 + printLog(
  31 + 'lifecycleState:$lifecycleState'); // lifecycleState:AppLifecycleState.resumed
  32 +
  33 + appLifecycleListener = AppLifecycleListener(
  34 + onStateChange: onStateChange,
  35 + onResume: onResume,
  36 + onInactive: onInactive,
  37 + onHide: onHide,
  38 + onShow: onShow,
  39 + onPause: onPause,
  40 + onRestart: onRestart,
  41 + onDetach: onDetach,
  42 + onExitRequested: onExitRequested,
  43 + );
  44 +
23 45 super.initState();
24 46 WidgetsBinding.instance.addObserver(this);
  47 + WidgetsBinding.instance.addPostFrameCallback((_) {
  48 + final route = ModalRoute.of(context);
  49 + if (route is PageRoute) {
  50 + customerRouteObserver.subscribe(this, route);
  51 + }
  52 + });
25 53  
26 54 _controller = AnimationController(
27 55 duration: const Duration(seconds: 2),
... ... @@ -29,21 +57,37 @@ class _ShakeImageState extends State&lt;ShakeImage&gt; with SingleTickerProviderStateM
29 57 );
30 58  
31 59 _animation = TweenSequence([
32   - TweenSequenceItem(tween: Tween(begin: 0.0, end: 0.05).chain(CurveTween(curve: Curves.easeInOut)), weight: 1),
33   - TweenSequenceItem(tween: Tween(begin: 0.05, end: -0.1).chain(CurveTween(curve: Curves.easeInOut)), weight: 1),
34   - TweenSequenceItem(tween: Tween(begin: -0.1, end: 0.2).chain(CurveTween(curve: Curves.easeInOut)), weight: 1),
35   - TweenSequenceItem(tween: Tween(begin: 0.2, end: -0.2).chain(CurveTween(curve: Curves.easeInOut)), weight: 1),
36   - TweenSequenceItem(tween: Tween(begin: -0.2, end: 0.1).chain(CurveTween(curve: Curves.easeInOut)), weight: 1),
37   - TweenSequenceItem(tween: Tween(begin: 0.1, end: -0.05).chain(CurveTween(curve: Curves.easeInOut)), weight: 1),
38   - TweenSequenceItem(tween: Tween(begin: -0.05, end: 0.0).chain(CurveTween(curve: Curves.easeInOut)), weight: 1),
  60 + TweenSequenceItem(
  61 + tween: Tween(begin: 0.0, end: 0.05)
  62 + .chain(CurveTween(curve: Curves.easeInOut)),
  63 + weight: 1),
  64 + TweenSequenceItem(
  65 + tween: Tween(begin: 0.05, end: -0.1)
  66 + .chain(CurveTween(curve: Curves.easeInOut)),
  67 + weight: 1),
  68 + TweenSequenceItem(
  69 + tween: Tween(begin: -0.1, end: 0.2)
  70 + .chain(CurveTween(curve: Curves.easeInOut)),
  71 + weight: 1),
  72 + TweenSequenceItem(
  73 + tween: Tween(begin: 0.2, end: -0.2)
  74 + .chain(CurveTween(curve: Curves.easeInOut)),
  75 + weight: 1),
  76 + TweenSequenceItem(
  77 + tween: Tween(begin: -0.2, end: 0.1)
  78 + .chain(CurveTween(curve: Curves.easeInOut)),
  79 + weight: 1),
  80 + TweenSequenceItem(
  81 + tween: Tween(begin: 0.1, end: -0.05)
  82 + .chain(CurveTween(curve: Curves.easeInOut)),
  83 + weight: 1),
  84 + TweenSequenceItem(
  85 + tween: Tween(begin: -0.05, end: 0.0)
  86 + .chain(CurveTween(curve: Curves.easeInOut)),
  87 + weight: 1),
39 88 ]).animate(_controller);
40 89  
41   - Timer(const Duration(seconds: 1), () {
42   - _controller.forward(from: 0.0);
43   - _timer = Timer.periodic(const Duration(seconds: 4), (Timer timer) {
44   - _controller.forward(from: 0.0);
45   - });
46   - });
  90 + printLog("--initState");
47 91  
48 92 // _controller.addStatusListener((status) {
49 93 // if (status == AnimationStatus.completed) {
... ... @@ -54,6 +98,67 @@ class _ShakeImageState extends State&lt;ShakeImage&gt; with SingleTickerProviderStateM
54 98 // });
55 99 }
56 100  
  101 + void _startAnimation() {
  102 + Timer(const Duration(seconds: 1), () {
  103 + _controller.forward(from: 0.0);
  104 + _timer = Timer.periodic(const Duration(seconds: 4), (Timer timer) {
  105 + _controller.forward(from: 0.0);
  106 + });
  107 + });
  108 + printLog('_startAnimation');
  109 + }
  110 +
  111 + void _stopAnimation() {
  112 + _timer.cancel();
  113 + printLog('_stopAnimation');
  114 + }
  115 +
  116 + ///进下一页
  117 + @override
  118 + void didPushNext() {
  119 + super.didPushNext();
  120 + printLog('--didPushNext');
  121 + _stopAnimation();
  122 + }
  123 +
  124 + ///下一个页面退回到当前页
  125 + @override
  126 + void didPopNext() {
  127 + printLog('--didPopNext');
  128 + super.didPopNext();
  129 + _startAnimation();
  130 + }
  131 +
  132 + ///进入到当前页,在initState之后执行
  133 + @override
  134 + void didPush() {
  135 + super.didPush();
  136 + printLog('--didPush');
  137 + _startAnimation();
  138 + }
  139 +
  140 + ///退出当前页到上一页,监听导航栈中的路由被弹出(即当前页面被移除)的事件
  141 + @override
  142 + void didPop() {
  143 + super.didPop();
  144 + printLog('--didPop');
  145 + _stopAnimation();
  146 + }
  147 +
  148 + @override
  149 + Future<bool> didPushRouteInformation(RouteInformation routeInformation) {
  150 + printLog('--didPushRouteInformation routeInformation=$routeInformation');
  151 + return super.didPushRouteInformation(routeInformation);
  152 + }
  153 +
  154 + ///系统导航栏上的后退按钮(Android)或物理返回按钮被按下时调用,这个方法允许你拦截和处理系统的返回操作。
  155 + @override
  156 + Future<bool> didPopRoute() {
  157 + printLog('--didPopRoute');
  158 + return super.didPopRoute();
  159 + }
  160 +
  161 + ///前后台切换
57 162 @override
58 163 void didChangeAppLifecycleState(AppLifecycleState state) {
59 164 if (state == AppLifecycleState.paused) {
... ... @@ -65,6 +170,8 @@ class _ShakeImageState extends State&lt;ShakeImage&gt; with SingleTickerProviderStateM
65 170  
66 171 @override
67 172 void dispose() {
  173 + printLog('dispose');
  174 + customerRouteObserver.unsubscribe(this);
68 175 WidgetsBinding.instance.removeObserver(this);
69 176 _controller.dispose();
70 177 _timer.cancel();
... ... @@ -82,9 +189,66 @@ class _ShakeImageState extends State&lt;ShakeImage&gt; with SingleTickerProviderStateM
82 189 child: child,
83 190 );
84 191 },
85   - child: Image.asset('xe_shop'.assetPng,
86   - width: 153),
  192 + child: Image.asset('xe_shop'.assetPng, width: 153),
87 193 ),
88 194 );
89 195 }
90   -}
91 196 \ No newline at end of file
  197 +
  198 + /// 监听状态
  199 + onStateChange(AppLifecycleState state) {
  200 + printLog('app_state:$state');
  201 + }
  202 +
  203 + // =============================== 根据App状态的产生的各种回调 ===============================
  204 +
  205 + /// 可见,并且可以响应用户操作时的回调
  206 + /// 比如应用从后台调度到前台时,在 onShow() 后面 执行
  207 + /// 注意:这个回调,初始化时 不执行
  208 + onResume() {
  209 + printLog('---onResume');
  210 + _startAnimation();
  211 + }
  212 +
  213 + /// 可见,但无法响应用户操作时的回调
  214 + onInactive() {
  215 + printLog('---onInactive');
  216 + }
  217 +
  218 + /// 隐藏时的回调
  219 + onHide() {
  220 + printLog('---onHide');
  221 + }
  222 +
  223 + /// 显示时的回调,应用从后台调度到前台时
  224 + onShow() {
  225 + printLog('---onShow');
  226 + }
  227 +
  228 + /// 暂停时的回调(后台)
  229 + onPause() {
  230 + printLog('---onPause');
  231 + _stopAnimation();
  232 + }
  233 +
  234 + /// 暂停后恢复时的回调
  235 + onRestart() {
  236 + printLog('---onRestart');
  237 + }
  238 +
  239 + /// 这两个回调,不是所有平台都支持,
  240 + /// 当退出 并将所有视图与引擎分离时的回调(IOS 支持,Android 不支持)
  241 + onDetach() {
  242 + printLog('---onDetach');
  243 + }
  244 +
  245 + /// 在退出程序时,发出询问的回调(IOS、Android 都不支持)
  246 + /// 响应 [AppExitResponse.exit] 将继续终止,响应 [AppExitResponse.cancel] 将取消终止。
  247 + Future<AppExitResponse> onExitRequested() async {
  248 + printLog('---onExitRequested');
  249 + return AppExitResponse.exit;
  250 + }
  251 +
  252 + void printLog(String msg) {
  253 + // Log.d('$TAG $msg');
  254 + }
  255 +}
... ...