5package io.flutter.embedding.engine.dart;
7import androidx.annotation.NonNull;
8import androidx.annotation.Nullable;
9import androidx.annotation.UiThread;
10import io.flutter.FlutterInjector;
12import io.flutter.embedding.engine.FlutterJNI;
13import io.flutter.plugin.common.BinaryMessenger;
14import io.flutter.util.TraceSection;
15import java.nio.ByteBuffer;
16import java.util.HashMap;
17import java.util.LinkedList;
20import java.util.WeakHashMap;
21import java.util.concurrent.ConcurrentLinkedQueue;
22import java.util.concurrent.ExecutorService;
23import java.util.concurrent.atomic.AtomicBoolean;
33 private static final String TAG =
"DartMessenger";
43 @NonNull
private final Map<String, HandlerInfo> messageHandlers =
new HashMap<>();
50 @NonNull
private Map<String, List<BufferedMessageInfo>> bufferedMessages =
new HashMap<>();
52 @NonNull
private final Object handlersLock =
new Object();
53 @NonNull
private final AtomicBoolean enableBufferingIncomingMessages =
new AtomicBoolean(
false);
56 private int nextReplyId = 1;
61 private WeakHashMap<TaskQueue, DartMessengerTaskQueue> createdTaskQueues =
62 new WeakHashMap<TaskQueue, DartMessengerTaskQueue>();
67 this.flutterJNI = flutterJNI;
68 this.taskQueueFactory = taskQueueFactory;
72 this(flutterJNI,
new DefaultTaskQueueFactory());
75 private static class TaskQueueToken
implements TaskQueue {}
86 ExecutorService executorService;
88 DefaultTaskQueueFactory() {
94 return new SerialTaskQueue(executorService);
96 return new ConcurrentTaskQueue(executorService);
105 private static class HandlerInfo {
106 @NonNull
public final BinaryMessenger.BinaryMessageHandler handler;
107 @Nullable
public final DartMessengerTaskQueue taskQueue;
110 @NonNull BinaryMessenger.BinaryMessageHandler handler,
111 @Nullable DartMessengerTaskQueue taskQueue) {
112 this.handler = handler;
113 this.taskQueue = taskQueue;
121 private static class BufferedMessageInfo {
122 @NonNull
public final ByteBuffer
message;
126 BufferedMessageInfo(@NonNull ByteBuffer
message,
int replyId,
long messageData) {
128 this.replyId = replyId;
129 this.messageData = messageData;
134 @NonNull
private final ExecutorService executor;
137 this.executor = executor;
142 executor.execute(runnable);
148 @NonNull
private final ExecutorService executor;
149 @NonNull
private final ConcurrentLinkedQueue<Runnable> queue;
150 @NonNull
private final AtomicBoolean isRunning;
153 this.executor = executor;
154 queue =
new ConcurrentLinkedQueue<>();
155 isRunning =
new AtomicBoolean(
false);
167 private void flush() {
169 if (isRunning.compareAndSet(
false,
true)) {
171 @Nullable Runnable runnable =
queue.poll();
172 if (runnable !=
null) {
176 isRunning.set(
false);
177 if (!
queue.isEmpty()) {
192 TaskQueueToken token =
new TaskQueueToken();
193 createdTaskQueues.put(token, taskQueue);
205 @NonNull String channel,
208 if (handler ==
null) {
209 Log.
v(
TAG,
"Removing handler for channel '" + channel +
"'");
210 synchronized (handlersLock) {
211 messageHandlers.remove(channel);
216 if (taskQueue !=
null) {
217 dartMessengerTaskQueue = createdTaskQueues.get(taskQueue);
218 if (dartMessengerTaskQueue ==
null) {
219 throw new IllegalArgumentException(
220 "Unrecognized TaskQueue, use BinaryMessenger to create your TaskQueue (ex makeBackgroundTaskQueue).");
223 Log.
v(
TAG,
"Setting handler for channel '" + channel +
"'");
226 synchronized (handlersLock) {
227 messageHandlers.put(channel,
new HandlerInfo(handler, dartMessengerTaskQueue));
228 list = bufferedMessages.remove(channel);
233 for (BufferedMessageInfo
info : list) {
234 dispatchMessageToQueue(
235 channel, messageHandlers.get(channel),
info.message,
info.replyId,
info.messageData);
241 enableBufferingIncomingMessages.set(
true);
246 Map<String, List<BufferedMessageInfo>> pendingMessages;
247 synchronized (handlersLock) {
248 enableBufferingIncomingMessages.set(
false);
249 pendingMessages = bufferedMessages;
250 bufferedMessages =
new HashMap<>();
253 for (BufferedMessageInfo
info : channel.getValue()) {
254 dispatchMessageToQueue(
255 channel.getKey(),
null,
info.message,
info.replyId,
info.messageData);
262 public void send(@NonNull String channel, @NonNull ByteBuffer
message) {
263 Log.
v(
TAG,
"Sending message over channel '" + channel +
"'");
269 @NonNull String channel,
273 Log.
v(
TAG,
"Sending message with callback over channel '" + channel +
"'");
274 int replyId = nextReplyId++;
276 pendingReplies.put(replyId,
callback);
279 flutterJNI.dispatchEmptyPlatformMessage(channel, replyId);
281 flutterJNI.dispatchPlatformMessage(channel,
message,
message.position(), replyId);
286 private void invokeHandler(
287 @Nullable HandlerInfo handlerInfo, @Nullable ByteBuffer
message,
final int replyId) {
289 if (handlerInfo !=
null) {
291 Log.
v(TAG,
"Deferring to registered handler to process message.");
292 handlerInfo.handler.onMessage(
message,
new Reply(flutterJNI, replyId));
293 }
catch (Exception ex) {
294 Log.
e(TAG,
"Uncaught exception in binary message listener", ex);
295 flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
296 }
catch (Error err) {
300 Log.v(TAG,
"No registered handler for message. Responding to Dart with empty reply message.");
301 flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
305 private void dispatchMessageToQueue(
306 @NonNull String channel,
307 @Nullable HandlerInfo handlerInfo,
312 final DartMessengerTaskQueue taskQueue = (handlerInfo !=
null) ? handlerInfo.taskQueue :
null;
313 TraceSection.beginAsyncSection(
"PlatformChannel ScheduleHandler on " + channel, replyId);
314 Runnable myRunnable =
316 TraceSection.endAsyncSection(
"PlatformChannel ScheduleHandler on " + channel, replyId);
317 try (TraceSection
e =
318 TraceSection.scoped(
"DartMessenger#handleMessageFromDart on " + channel)) {
319 invokeHandler(handlerInfo,
message, replyId);
327 flutterJNI.cleanupMessageData(messageData);
330 final DartMessengerTaskQueue nonnullTaskQueue =
331 taskQueue ==
null ? platformTaskQueue : taskQueue;
332 nonnullTaskQueue.dispatch(myRunnable);
337 @NonNull String channel, @Nullable ByteBuffer
message,
int replyId,
long messageData) {
339 Log.
v(
TAG,
"Received message from Dart over channel '" + channel +
"'");
341 HandlerInfo handlerInfo;
342 boolean messageDeferred;
345 synchronized (handlersLock) {
346 handlerInfo = messageHandlers.get(channel);
347 messageDeferred = (enableBufferingIncomingMessages.get() && handlerInfo ==
null);
348 if (messageDeferred) {
357 if (!bufferedMessages.containsKey(channel)) {
358 bufferedMessages.put(channel,
new LinkedList<>());
361 buffer.add(
new BufferedMessageInfo(
message, replyId, messageData));
364 if (!messageDeferred) {
365 dispatchMessageToQueue(channel, handlerInfo,
message, replyId, messageData);
371 Log.
v(
TAG,
"Received message reply from Dart.");
372 BinaryMessenger.BinaryReply
callback = pendingReplies.remove(replyId);
375 Log.
v(
TAG,
"Invoking registered callback for reply from Dart.");
377 if (reply !=
null && reply.isDirect()) {
382 }
catch (Exception ex) {
383 Log.
e(
TAG,
"Uncaught exception in binary message reply handler", ex);
384 }
catch (Error err) {
403 return pendingReplies.size();
410 private static void handleError(Error err) {
411 Thread currentThread = Thread.currentThread();
412 if (currentThread.getUncaughtExceptionHandler() ==
null) {
415 currentThread.getUncaughtExceptionHandler().uncaughtException(currentThread, err);
420 private final int replyId;
421 private final AtomicBoolean done =
new AtomicBoolean(
false);
424 this.flutterJNI = flutterJNI;
425 this.replyId = replyId;
430 if (done.getAndSet(
true)) {
431 throw new IllegalStateException(
"Reply already submitted");
434 flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
436 flutterJNI.invokePlatformMessageResponseCallback(replyId,
reply,
reply.position());
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
static void v(@NonNull String tag, @NonNull String message)
static void e(@NonNull String tag, @NonNull String message)
ConcurrentTaskQueue(ExecutorService executor)
void dispatch(@NonNull Runnable runnable)
Reply(@NonNull FlutterJNI flutterJNI, int replyId)
void reply(@Nullable ByteBuffer reply)
void dispatch(@NonNull Runnable runnable)
SerialTaskQueue(ExecutorService executor)
void setMessageHandler( @NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler)
void enableBufferingIncomingMessages()
void handleMessageFromDart( @NonNull String channel, @Nullable ByteBuffer message, int replyId, long messageData)
int getPendingChannelResponseCount()
TaskQueue makeBackgroundTaskQueue(TaskQueueOptions options)
void send(@NonNull String channel, @NonNull ByteBuffer message)
void setMessageHandler( @NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler, @Nullable TaskQueue taskQueue)
void send( @NonNull String channel, @Nullable ByteBuffer message, @Nullable BinaryMessenger.BinaryReply callback)
void handlePlatformMessageResponse(int replyId, @Nullable ByteBuffer reply)
DartMessenger(@NonNull FlutterJNI flutterJNI)
DartMessenger(@NonNull FlutterJNI flutterJNI, @NonNull TaskQueueFactory taskQueueFactory)
void disableBufferingIncomingMessages()
static TraceSection scoped(String name)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
void dispatch(@NonNull Runnable runnable)
DartMessengerTaskQueue makeBackgroundTaskQueue(TaskQueueOptions options)
default TaskQueue makeBackgroundTaskQueue()
void Log(const char *format,...) SK_PRINTF_LIKE(1
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer