Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
LifecycleChannel.java
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package io.flutter.embedding.engine.systemchannels;
6
7import androidx.annotation.NonNull;
8import androidx.annotation.VisibleForTesting;
9import io.flutter.Log;
10import io.flutter.embedding.engine.dart.DartExecutor;
11import io.flutter.plugin.common.BasicMessageChannel;
12import io.flutter.plugin.common.StringCodec;
13import java.util.Locale;
14
15/**
16 * A {@link BasicMessageChannel} that communicates lifecycle events to the framework.
17 *
18 * <p>The activity listens to the Android lifecycle events, in addition to the focus events for
19 * windows, and this channel combines that information to decide if the application is the inactive,
20 * resumed, paused, or detached state.
21 */
22public class LifecycleChannel {
23 private static final String TAG = "LifecycleChannel";
24 private static final String CHANNEL_NAME = "flutter/lifecycle";
25
26 // This enum should match the Dart enum of the same name.
27 //
28 // HIDDEN isn't used on Android (it's synthesized in the Framework code). It's
29 // only listed here so that apicheck_test.dart can make sure that the states here
30 // match the Dart code.
31 private enum AppLifecycleState {
32 DETACHED,
33 RESUMED,
34 INACTIVE,
35 HIDDEN,
36 PAUSED,
37 };
38
39 private AppLifecycleState lastAndroidState = null;
40 private AppLifecycleState lastFlutterState = null;
41 private boolean lastFocus = true;
42
43 @NonNull private final BasicMessageChannel<String> channel;
44
45 public LifecycleChannel(@NonNull DartExecutor dartExecutor) {
46 this(new BasicMessageChannel<String>(dartExecutor, CHANNEL_NAME, StringCodec.INSTANCE));
47 }
48
49 @VisibleForTesting
50 public LifecycleChannel(@NonNull BasicMessageChannel<String> channel) {
51 this.channel = channel;
52 }
53
54 // Here's the state table this implements:
55 //
56 // | Android State | Window focused | Flutter state |
57 // |---------------|----------------|---------------|
58 // | Resumed | true | resumed |
59 // | Resumed | false | inactive |
60 // | Paused | true | inactive |
61 // | Paused | false | inactive |
62 // | Stopped | true | paused |
63 // | Stopped | false | paused |
64 // | Detached | true | detached |
65 // | Detached | false | detached |
66 //
67 // The hidden state isn't used on Android, it's synthesized in the Framework
68 // code when transitioning between paused and inactive in either direction.
69 private void sendState(AppLifecycleState state, boolean hasFocus) {
70 if (lastAndroidState == state && hasFocus == lastFocus) {
71 // No inputs changed, so Flutter state could not have changed.
72 return;
73 }
74 if (state == null && lastAndroidState == null) {
75 // If we're responding to a focus change before the state is set, just
76 // keep the last reported focus state and don't send anything to the
77 // framework. This could happen if focus events and lifecycle events are
78 // delivered out of the expected order.
79 lastFocus = hasFocus;
80 return;
81 }
82 AppLifecycleState newState = null;
83 switch (state) {
84 case RESUMED:
85 // Focus is only taken into account when the Android state is "Resumed".
86 // In all other states, focus is ignored, because we can't know what order
87 // Android lifecycle notifications and window focus notifications events
88 // will arrive in, and those states don't send input events anyhow.
89 newState = hasFocus ? AppLifecycleState.RESUMED : AppLifecycleState.INACTIVE;
90 break;
91 case INACTIVE:
92 case HIDDEN:
93 case PAUSED:
94 case DETACHED:
95 newState = state;
96 break;
97 }
98
99 // Keep the last reported values for future updates.
100 lastAndroidState = state;
101 lastFocus = hasFocus;
102 if (newState == lastFlutterState) {
103 // No change in the resulting Flutter state, so don't report anything.
104 return;
105 }
106 String message = "AppLifecycleState." + newState.name().toLowerCase(Locale.ROOT);
107 Log.v(TAG, "Sending " + message + " message.");
108 channel.send(message);
109 lastFlutterState = newState;
110 }
111
112 // Called if at least one window in the app has focus, even if the focused
113 // window doesn't contain a Flutter view.
114 public void aWindowIsFocused() {
115 sendState(lastAndroidState, true);
116 }
117
118 // Called if no windows in the app have focus.
119 public void noWindowsAreFocused() {
120 sendState(lastAndroidState, false);
121 }
122
123 public void appIsResumed() {
124 sendState(AppLifecycleState.RESUMED, lastFocus);
125 }
126
127 public void appIsInactive() {
128 sendState(AppLifecycleState.INACTIVE, lastFocus);
129 }
130
131 public void appIsPaused() {
132 sendState(AppLifecycleState.PAUSED, lastFocus);
133 }
134
135 public void appIsDetached() {
136 sendState(AppLifecycleState.DETACHED, lastFocus);
137 }
138}
LifecycleChannel(@NonNull BasicMessageChannel< String > channel)
AtkStateType state
Win32Message message