Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
EventChannel.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.plugin.common;
6
7import androidx.annotation.Nullable;
8import androidx.annotation.UiThread;
9import io.flutter.BuildConfig;
10import io.flutter.Log;
11import io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler;
12import io.flutter.plugin.common.BinaryMessenger.BinaryReply;
13import java.nio.ByteBuffer;
14import java.util.concurrent.atomic.AtomicBoolean;
15import java.util.concurrent.atomic.AtomicReference;
16
17/**
18 * A named channel for communicating with the Flutter application using asynchronous event streams.
19 *
20 * <p>Incoming requests for event stream setup are decoded from binary on receipt, and Java
21 * responses and events are encoded into binary before being transmitted back to Flutter. The {@link
22 * MethodCodec} used must be compatible with the one used by the Flutter application. This can be
23 * achieved by creating an <a
24 * href="https://api.flutter.dev/flutter/services/EventChannel-class.html">EventChannel</a>
25 * counterpart of this channel on the Dart side. The Java type of stream configuration arguments,
26 * events, and error details is {@code Object}, but only values supported by the specified {@link
27 * MethodCodec} can be used.
28 *
29 * <p>The logical identity of the channel is given by its name. Identically named channels will
30 * interfere with each other's communication.
31 */
32public final class EventChannel {
33 private static final String TAG = "EventChannel#";
34
35 private final BinaryMessenger messenger;
36 private final String name;
37 private final MethodCodec codec;
38 @Nullable private final BinaryMessenger.TaskQueue taskQueue;
39
40 /**
41 * Creates a new channel associated with the specified {@link BinaryMessenger} and with the
42 * specified name and the standard {@link MethodCodec}.
43 *
44 * @param messenger a {@link BinaryMessenger}.
45 * @param name a channel name String.
46 */
47 public EventChannel(BinaryMessenger messenger, String name) {
48 this(messenger, name, StandardMethodCodec.INSTANCE);
49 }
50
51 /**
52 * Creates a new channel associated with the specified {@link BinaryMessenger} and with the
53 * specified name and {@link MethodCodec}.
54 *
55 * @param messenger a {@link BinaryMessenger}.
56 * @param name a channel name String.
57 * @param codec a {@link MessageCodec}.
58 */
59 public EventChannel(BinaryMessenger messenger, String name, MethodCodec codec) {
60 this(messenger, name, codec, null);
61 }
62
63 /**
64 * Creates a new channel associated with the specified {@link BinaryMessenger} and with the
65 * specified name and {@link MethodCodec}.
66 *
67 * @param messenger a {@link BinaryMessenger}.
68 * @param name a channel name String.
69 * @param codec a {@link MessageCodec}.
70 * @param taskQueue a {@link BinaryMessenger.TaskQueue} that specifies what thread will execute
71 * the handler. Specifying null means execute on the platform thread. See also {@link
72 * BinaryMessenger#makeBackgroundTaskQueue()}.
73 */
75 BinaryMessenger messenger,
76 String name,
77 MethodCodec codec,
78 BinaryMessenger.TaskQueue taskQueue) {
79 if (BuildConfig.DEBUG) {
80 if (messenger == null) {
81 Log.e(TAG, "Parameter messenger must not be null.");
82 }
83 if (name == null) {
84 Log.e(TAG, "Parameter name must not be null.");
85 }
86 if (codec == null) {
87 Log.e(TAG, "Parameter codec must not be null.");
88 }
89 }
90 this.messenger = messenger;
91 this.name = name;
92 this.codec = codec;
93 this.taskQueue = taskQueue;
94 }
95
96 /**
97 * Registers a stream handler on this channel.
98 *
99 * <p>Overrides any existing handler registration for (the name of) this channel.
100 *
101 * <p>If no handler has been registered, any incoming stream setup requests will be handled
102 * silently by providing an empty stream.
103 *
104 * @param handler a {@link StreamHandler}, or null to deregister.
105 */
106 @UiThread
107 public void setStreamHandler(final StreamHandler handler) {
108 // We call the 2 parameter variant specifically to avoid breaking changes in
109 // mock verify calls.
110 // See https://github.com/flutter/flutter/issues/92582.
111 if (taskQueue != null) {
112 messenger.setMessageHandler(
113 name, handler == null ? null : new IncomingStreamRequestHandler(handler), taskQueue);
114 } else {
115 messenger.setMessageHandler(
116 name, handler == null ? null : new IncomingStreamRequestHandler(handler));
117 }
118 }
119
120 /**
121 * Handler of stream setup and teardown requests.
122 *
123 * <p>Implementations must be prepared to accept sequences of alternating calls to {@link
124 * #onListen(Object, EventChannel.EventSink)} and {@link #onCancel(Object)}. Implementations
125 * should ideally consume no resources when the last such call is not {@code onListen}. In typical
126 * situations, this means that the implementation should register itself with platform-specific
127 * event sources {@code onListen} and deregister again {@code onCancel}.
128 */
129 public interface StreamHandler {
130 /**
131 * Handles a request to set up an event stream.
132 *
133 * <p>Any uncaught exception thrown by this method will be caught by the channel implementation
134 * and logged. An error result message will be sent back to Flutter.
135 *
136 * @param arguments stream configuration arguments, possibly null.
137 * @param events an {@link EventSink} for emitting events to the Flutter receiver.
138 */
139 void onListen(Object arguments, EventSink events);
140
141 /**
142 * Handles a request to tear down the most recently created event stream.
143 *
144 * <p>Any uncaught exception thrown by this method will be caught by the channel implementation
145 * and logged. An error result message will be sent back to Flutter.
146 *
147 * <p>The channel implementation may call this method with null arguments to separate a pair of
148 * two consecutive set up requests. Such request pairs may occur during Flutter hot restart. Any
149 * uncaught exception thrown in this situation will be logged without notifying Flutter.
150 *
151 * @param arguments stream configuration arguments, possibly null.
152 */
153 void onCancel(Object arguments);
154 }
155
156 /**
157 * Event callback. Supports dual use: Producers of events to be sent to Flutter act as clients of
158 * this interface for sending events. Consumers of events sent from Flutter implement this
159 * interface for handling received events (the latter facility has not been implemented yet).
160 */
161 public interface EventSink {
162 /**
163 * Consumes a successful event.
164 *
165 * @param event the event, possibly null.
166 */
167 void success(Object event);
168
169 /**
170 * Consumes an error event.
171 *
172 * @param errorCode an error code String.
173 * @param errorMessage a human-readable error message String, possibly null.
174 * @param errorDetails error details, possibly null
175 */
176 void error(String errorCode, String errorMessage, Object errorDetails);
177
178 /**
179 * Consumes end of stream. Ensuing calls to {@link #success(Object)} or {@link #error(String,
180 * String, Object)}, if any, are ignored.
181 */
183 }
184
185 private final class IncomingStreamRequestHandler implements BinaryMessageHandler {
186 private final StreamHandler handler;
187 private final AtomicReference<EventSink> activeSink = new AtomicReference<>(null);
188
189 IncomingStreamRequestHandler(StreamHandler handler) {
190 this.handler = handler;
191 }
192
193 @Override
194 public void onMessage(ByteBuffer message, final BinaryReply reply) {
195 final MethodCall call = codec.decodeMethodCall(message);
196 if (call.method.equals("listen")) {
197 onListen(call.arguments, reply);
198 } else if (call.method.equals("cancel")) {
199 onCancel(call.arguments, reply);
200 } else {
201 reply.reply(null);
202 }
203 }
204
205 private void onListen(Object arguments, BinaryReply callback) {
206 final EventSink eventSink = new EventSinkImplementation();
207 final EventSink oldSink = activeSink.getAndSet(eventSink);
208 if (oldSink != null) {
209 // Repeated calls to onListen may happen during hot restart.
210 // We separate them with a call to onCancel.
211 try {
212 handler.onCancel(null);
213 } catch (RuntimeException e) {
214 Log.e(TAG + name, "Failed to close existing event stream", e);
215 }
216 }
217 try {
218 handler.onListen(arguments, eventSink);
219 callback.reply(codec.encodeSuccessEnvelope(null));
220 } catch (RuntimeException e) {
221 activeSink.set(null);
222 Log.e(TAG + name, "Failed to open event stream", e);
223 callback.reply(codec.encodeErrorEnvelope("error", e.getMessage(), null));
224 }
225 }
226
227 private void onCancel(Object arguments, BinaryReply callback) {
228 final EventSink oldSink = activeSink.getAndSet(null);
229 if (oldSink != null) {
230 try {
231 handler.onCancel(arguments);
232 callback.reply(codec.encodeSuccessEnvelope(null));
233 } catch (RuntimeException e) {
234 Log.e(TAG + name, "Failed to close event stream", e);
235 callback.reply(codec.encodeErrorEnvelope("error", e.getMessage(), null));
236 }
237 } else {
238 callback.reply(codec.encodeErrorEnvelope("error", "No active stream to cancel", null));
239 }
240 }
241
242 private final class EventSinkImplementation implements EventSink {
243 final AtomicBoolean hasEnded = new AtomicBoolean(false);
244
245 @Override
246 @UiThread
247 public void success(Object event) {
248 if (hasEnded.get() || activeSink.get() != this) {
249 return;
250 }
251 EventChannel.this.messenger.send(name, codec.encodeSuccessEnvelope(event));
252 }
253
254 @Override
255 @UiThread
256 public void error(String errorCode, String errorMessage, Object errorDetails) {
257 if (hasEnded.get() || activeSink.get() != this) {
258 return;
259 }
260 EventChannel.this.messenger.send(
261 name, codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
262 }
263
264 @Override
265 @UiThread
266 public void endOfStream() {
267 if (hasEnded.getAndSet(true) || activeSink.get() != this) {
268 return;
269 }
270 EventChannel.this.messenger.send(name, null);
271 }
272 }
273 }
274}
static final boolean DEBUG
static void e(@NonNull String tag, @NonNull String message)
Definition Log.java:84
void setStreamHandler(final StreamHandler handler)
EventChannel(BinaryMessenger messenger, String name, MethodCodec codec, BinaryMessenger.TaskQueue taskQueue)
EventChannel(BinaryMessenger messenger, String name)
EventChannel(BinaryMessenger messenger, String name, MethodCodec codec)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlKeyEvent * event
const uint8_t uint32_t uint32_t GError ** error
void setMessageHandler(@NonNull String channel, @Nullable BinaryMessageHandler handler)
void error(String errorCode, String errorMessage, Object errorDetails)
void onListen(Object arguments, EventSink events)
ByteBuffer encodeSuccessEnvelope(@Nullable Object result)
ByteBuffer encodeErrorEnvelope( @NonNull String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails)
MethodCall decodeMethodCall(@NonNull ByteBuffer methodCall)
Win32Message message
void Log(const char *format,...) SK_PRINTF_LIKE(1
#define TAG()