Flutter Engine
The Flutter Engine
BasicMessageChannel.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.NonNull;
8import androidx.annotation.Nullable;
9import androidx.annotation.UiThread;
10import io.flutter.BuildConfig;
11import io.flutter.Log;
12import io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler;
13import io.flutter.plugin.common.BinaryMessenger.BinaryReply;
14import java.nio.ByteBuffer;
15import java.util.Arrays;
16
17/**
18 * A named channel for communicating with the Flutter application using basic, asynchronous message
19 * passing.
20 *
21 * <p>Messages are encoded into binary before being sent, and binary messages received are decoded
22 * into Java objects. The {@link MessageCodec} used must be compatible with the one used by the
23 * Flutter application. This can be achieved by creating a <a
24 * href="https://api.flutter.dev/flutter/services/BasicMessageChannel-class.html">BasicMessageChannel</a>
25 * counterpart of this channel on the Dart side. The static Java type of messages sent and received
26 * is {@code Object}, but only values supported by the specified {@link MessageCodec} can be used.
27 *
28 * <p>The logical identity of the channel is given by its name. Identically named channels will
29 * interfere with each other's communication.
30 */
31public final class BasicMessageChannel<T> {
32 private static final String TAG = "BasicMessageChannel#";
33 public static final String CHANNEL_BUFFERS_CHANNEL = "dev.flutter/channel-buffers";
34
35 @NonNull private final BinaryMessenger messenger;
36 @NonNull private final String name;
37 @NonNull private final MessageCodec<T> 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 {@link MessageCodec}.
43 *
44 * @param messenger a {@link BinaryMessenger}.
45 * @param name a channel name String.
46 * @param codec a {@link MessageCodec}.
47 */
49 @NonNull BinaryMessenger messenger, @NonNull String name, @NonNull MessageCodec<T> codec) {
50 this(messenger, name, codec, null);
51 }
52
53 /**
54 * Creates a new channel associated with the specified {@link BinaryMessenger} and with the
55 * specified name and {@link MessageCodec}.
56 *
57 * @param messenger a {@link BinaryMessenger}.
58 * @param name a channel name String.
59 * @param codec a {@link MessageCodec}.
60 * @param taskQueue a {@link BinaryMessenger.TaskQueue} that specifies what thread will execute
61 * the handler. Specifying null means execute on the platform thread. See also {@link
62 * BinaryMessenger#makeBackgroundTaskQueue()}.
63 */
65 @NonNull BinaryMessenger messenger,
66 @NonNull String name,
67 @NonNull MessageCodec<T> codec,
68 BinaryMessenger.TaskQueue taskQueue) {
69 if (BuildConfig.DEBUG) {
70 if (messenger == null) {
71 Log.e(TAG, "Parameter messenger must not be null.");
72 }
73 if (name == null) {
74 Log.e(TAG, "Parameter name must not be null.");
75 }
76 if (codec == null) {
77 Log.e(TAG, "Parameter codec must not be null.");
78 }
79 }
80 this.messenger = messenger;
81 this.name = name;
82 this.codec = codec;
83 this.taskQueue = taskQueue;
84 }
85
86 /**
87 * Sends the specified message to the Flutter application on this channel.
88 *
89 * @param message the message, possibly null.
90 */
91 public void send(@Nullable T message) {
92 send(message, null);
93 }
94
95 /**
96 * Sends the specified message to the Flutter application, optionally expecting a reply.
97 *
98 * <p>Any uncaught exception thrown by the reply callback will be caught and logged.
99 *
100 * @param message the message, possibly null.
101 * @param callback a {@link Reply} callback, possibly null.
102 */
103 @UiThread
104 public void send(@Nullable T message, @Nullable final Reply<T> callback) {
105 messenger.send(
106 name,
107 codec.encodeMessage(message),
108 callback == null ? null : new IncomingReplyHandler(callback));
109 }
110
111 /**
112 * Registers a message handler on this channel for receiving messages sent from the Flutter
113 * application.
114 *
115 * <p>Overrides any existing handler registration for (the name of) this channel.
116 *
117 * <p>If no handler has been registered, any incoming message on this channel will be handled
118 * silently by sending a null reply.
119 *
120 * @param handler a {@link MessageHandler}, or null to deregister.
121 */
122 @UiThread
123 public void setMessageHandler(@Nullable final MessageHandler<T> handler) {
124 // We call the 2 parameter variant specifically to avoid breaking changes in
125 // mock verify calls.
126 // See https://github.com/flutter/flutter/issues/92582.
127 if (taskQueue != null) {
128 messenger.setMessageHandler(
129 name, handler == null ? null : new IncomingMessageHandler(handler), taskQueue);
130 } else {
131 messenger.setMessageHandler(
132 name, handler == null ? null : new IncomingMessageHandler(handler));
133 }
134 }
135
136 /**
137 * Adjusts the number of messages that will get buffered when sending messages to channels that
138 * aren't fully set up yet. For example, the engine isn't running yet or the channel's message
139 * handler isn't set up on the Dart side yet.
140 */
141 public void resizeChannelBuffer(int newSize) {
142 resizeChannelBuffer(messenger, name, newSize);
143 }
144
145 /**
146 * Toggles whether the channel should show warning messages when discarding messages due to
147 * overflow. When 'warns' is false the channel is expected to overflow and warning messages will
148 * not be shown.
149 */
150 public void setWarnsOnChannelOverflow(boolean warns) {
151 setWarnsOnChannelOverflow(messenger, name, warns);
152 }
153
154 private static ByteBuffer packetFromEncodedMessage(ByteBuffer message) {
155 // Create a bytes array using the buffer content (messages.array() can not be used here).
156 message.flip();
157 final byte[] bytes = new byte[message.remaining()];
158 message.get(bytes);
159
160 // The current Android Java/JNI platform message implementation assumes
161 // that all buffers passed to native are direct buffers.
162 ByteBuffer packet = ByteBuffer.allocateDirect(bytes.length);
163 packet.put(bytes);
164
165 return packet;
166 }
167
168 /**
169 * Adjusts the number of messages that will get buffered when sending messages to channels that
170 * aren't fully set up yet. For example, the engine isn't running yet or the channel's message
171 * handler isn't set up on the Dart side yet.
172 */
173 public static void resizeChannelBuffer(
174 @NonNull BinaryMessenger messenger, @NonNull String channel, int newSize) {
176 Object[] arguments = {channel, newSize};
177 MethodCall methodCall = new MethodCall("resize", Arrays.asList(arguments));
178 ByteBuffer message = codec.encodeMethodCall(methodCall);
179 ByteBuffer packet = packetFromEncodedMessage(message);
180 messenger.send(BasicMessageChannel.CHANNEL_BUFFERS_CHANNEL, packet);
181 }
182
183 /**
184 * Toggles whether the channel should show warning messages when discarding messages due to
185 * overflow. When 'warns' is false the channel is expected to overflow and warning messages will
186 * not be shown.
187 */
188 public static void setWarnsOnChannelOverflow(
189 @NonNull BinaryMessenger messenger, @NonNull String channel, boolean warns) {
191 Object[] arguments = {channel, !warns};
192 MethodCall methodCall = new MethodCall("overflow", Arrays.asList(arguments));
193 ByteBuffer message = codec.encodeMethodCall(methodCall);
194 ByteBuffer packet = packetFromEncodedMessage(message);
195 messenger.send(BasicMessageChannel.CHANNEL_BUFFERS_CHANNEL, packet);
196 }
197
198 /** A handler of incoming messages. */
199 public interface MessageHandler<T> {
200
201 /**
202 * Handles the specified message received from Flutter.
203 *
204 * <p>Handler implementations must reply to all incoming messages, by submitting a single reply
205 * message to the given {@link Reply}. Failure to do so will result in lingering Flutter reply
206 * handlers. The reply may be submitted asynchronously and invoked on any thread.
207 *
208 * <p>Any uncaught exception thrown by this method, or the preceding message decoding, will be
209 * caught by the channel implementation and logged, and a null reply message will be sent back
210 * to Flutter.
211 *
212 * <p>Any uncaught exception thrown during encoding a reply message submitted to the {@link
213 * Reply} is treated similarly: the exception is logged, and a null reply is sent to Flutter.
214 *
215 * @param message the message, possibly null.
216 * @param reply a {@link Reply} for sending a single message reply back to Flutter.
217 */
218 void onMessage(@Nullable T message, @NonNull Reply<T> reply);
219 }
220
221 /**
222 * Message reply callback. Used to submit a reply to an incoming message from Flutter. Also used
223 * in the dual capacity to handle a reply received from Flutter after sending a message.
224 */
225 public interface Reply<T> {
226 /**
227 * Handles the specified message reply.
228 *
229 * @param reply the reply, possibly null.
230 */
231 void reply(@Nullable T reply);
232 }
233
234 private final class IncomingReplyHandler implements BinaryReply {
235 private final Reply<T> callback;
236
237 private IncomingReplyHandler(@NonNull Reply<T> callback) {
238 this.callback = callback;
239 }
240
241 @Override
242 public void reply(@Nullable ByteBuffer reply) {
243 try {
244 callback.reply(codec.decodeMessage(reply));
245 } catch (RuntimeException e) {
246 Log.e(TAG + name, "Failed to handle message reply", e);
247 }
248 }
249 }
250
251 private final class IncomingMessageHandler implements BinaryMessageHandler {
252 private final MessageHandler<T> handler;
253
254 private IncomingMessageHandler(@NonNull MessageHandler<T> handler) {
255 this.handler = handler;
256 }
257
258 @Override
259 public void onMessage(@Nullable ByteBuffer message, @NonNull final BinaryReply callback) {
260 try {
261 handler.onMessage(
262 codec.decodeMessage(message),
263 new Reply<T>() {
264 @Override
265 public void reply(T reply) {
266 callback.reply(codec.encodeMessage(reply));
267 }
268 });
269 } catch (RuntimeException e) {
270 Log.e(TAG + name, "Failed to handle message", e);
271 callback.reply(null);
272 }
273 }
274 }
275}
static final boolean DEBUG
static void e(@NonNull String tag, @NonNull String message)
Definition: Log.java:84
void setMessageHandler(@Nullable final MessageHandler< T > handler)
void send(@Nullable T message, @Nullable final Reply< T > callback)
static void setWarnsOnChannelOverflow( @NonNull BinaryMessenger messenger, @NonNull String channel, boolean warns)
BasicMessageChannel( @NonNull BinaryMessenger messenger, @NonNull String name, @NonNull MessageCodec< T > codec)
static void resizeChannelBuffer( @NonNull BinaryMessenger messenger, @NonNull String channel, int newSize)
BasicMessageChannel( @NonNull BinaryMessenger messenger, @NonNull String name, @NonNull MessageCodec< T > codec, BinaryMessenger.TaskQueue taskQueue)
ByteBuffer encodeMethodCall(@NonNull MethodCall methodCall)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
static ::testing::Matcher< GBytes * > MethodCall(const std::string &name, ::testing::Matcher< FlValue * > args)
void onMessage(@Nullable T message, @NonNull Reply< T > reply)
Win32Message message
void Log(const char *format,...) SK_PRINTF_LIKE(1
Definition: TestRunner.cpp:137
std::function< void(const uint8_t *message, size_t message_size, BinaryReply reply)> BinaryMessageHandler
std::function< void(const uint8_t *reply, size_t reply_size)> BinaryReply
#define T
Definition: precompiler.cc:65
#define TAG()