Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
DartExecutor.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.dart;
6
7import android.content.res.AssetManager;
8import androidx.annotation.NonNull;
9import androidx.annotation.Nullable;
10import androidx.annotation.UiThread;
11import io.flutter.FlutterInjector;
12import io.flutter.Log;
13import io.flutter.embedding.engine.FlutterJNI;
14import io.flutter.embedding.engine.loader.FlutterLoader;
15import io.flutter.plugin.common.BinaryMessenger;
16import io.flutter.plugin.common.StringCodec;
17import io.flutter.util.TraceSection;
18import io.flutter.view.FlutterCallbackInformation;
19import java.nio.ByteBuffer;
20import java.util.List;
21
22/**
23 * Configures, bootstraps, and starts executing Dart code.
24 *
25 * <p>To specify a top-level Dart function to execute, use a {@link DartEntrypoint} to tell {@link
26 * DartExecutor} where to find the Dart code to execute, and which Dart function to use as the
27 * entrypoint. To execute the entrypoint, pass the {@link DartEntrypoint} to {@link
28 * #executeDartEntrypoint(DartEntrypoint)}.
29 *
30 * <p>To specify a Dart callback to execute, use a {@link DartCallback}. A given Dart callback must
31 * be registered with the Dart VM to be invoked by a {@link DartExecutor}. To execute the callback,
32 * pass the {@link DartCallback} to {@link #executeDartCallback(DartCallback)}.
33 *
34 * <p>Once started, a {@link DartExecutor} cannot be stopped. The associated Dart code will execute
35 * until it completes, or until the {@link io.flutter.embedding.engine.FlutterEngine} that owns this
36 * {@link DartExecutor} is destroyed.
37 */
38public class DartExecutor implements BinaryMessenger {
39 private static final String TAG = "DartExecutor";
40
41 @NonNull private final FlutterJNI flutterJNI;
42 @NonNull private final AssetManager assetManager;
43 @NonNull private final DartMessenger dartMessenger;
44 @NonNull private final BinaryMessenger binaryMessenger;
45 private boolean isApplicationRunning = false;
46 @Nullable private String isolateServiceId;
47 @Nullable private IsolateServiceIdListener isolateServiceIdListener;
48
49 private final BinaryMessenger.BinaryMessageHandler isolateChannelMessageHandler =
50 new BinaryMessenger.BinaryMessageHandler() {
51 @Override
52 public void onMessage(ByteBuffer message, final BinaryReply callback) {
53 isolateServiceId = StringCodec.INSTANCE.decodeMessage(message);
54 if (isolateServiceIdListener != null) {
55 isolateServiceIdListener.onIsolateServiceIdAvailable(isolateServiceId);
56 }
57 }
58 };
59
60 public DartExecutor(@NonNull FlutterJNI flutterJNI, @NonNull AssetManager assetManager) {
61 this.flutterJNI = flutterJNI;
62 this.assetManager = assetManager;
63 this.dartMessenger = new DartMessenger(flutterJNI);
64 dartMessenger.setMessageHandler("flutter/isolate", isolateChannelMessageHandler);
65 this.binaryMessenger = new DefaultBinaryMessenger(dartMessenger);
66 // The JNI might already be attached if coming from a spawned engine. If so, correctly report
67 // that this DartExecutor is already running.
68 if (flutterJNI.isAttached()) {
69 isApplicationRunning = true;
70 }
71 }
72
73 /**
74 * Invoked when the {@link io.flutter.embedding.engine.FlutterEngine} that owns this {@link
75 * DartExecutor} attaches to JNI.
76 *
77 * <p>When attached to JNI, this {@link DartExecutor} begins handling 2-way communication to/from
78 * the Dart execution context. This communication is facilitate via 2 APIs:
79 *
80 * <ul>
81 * <li>{@link BinaryMessenger}, which sends messages to Dart
82 * <li>{@link PlatformMessageHandler}, which receives messages from Dart
83 * </ul>
84 */
85 public void onAttachedToJNI() {
86 Log.v(
87 TAG,
88 "Attached to JNI. Registering the platform message handler for this Dart execution"
89 + " context.");
90 flutterJNI.setPlatformMessageHandler(dartMessenger);
91 }
92
93 /**
94 * Invoked when the {@link io.flutter.embedding.engine.FlutterEngine} that owns this {@link
95 * DartExecutor} detaches from JNI.
96 *
97 * <p>When detached from JNI, this {@link DartExecutor} stops handling 2-way communication to/from
98 * the Dart execution context.
99 */
100 public void onDetachedFromJNI() {
101 Log.v(
102 TAG,
103 "Detached from JNI. De-registering the platform message handler for this Dart execution"
104 + " context.");
105 flutterJNI.setPlatformMessageHandler(null);
106 }
107
108 /**
109 * Is this {@link DartExecutor} currently executing Dart code?
110 *
111 * @return true if Dart code is being executed, false otherwise
112 */
113 public boolean isExecutingDart() {
114 return isApplicationRunning;
115 }
116
117 /**
118 * Starts executing Dart code based on the given {@code dartEntrypoint}.
119 *
120 * <p>See {@link DartEntrypoint} for configuration options.
121 *
122 * @param dartEntrypoint specifies which Dart function to run, and where to find it
123 */
124 public void executeDartEntrypoint(@NonNull DartEntrypoint dartEntrypoint) {
125 executeDartEntrypoint(dartEntrypoint, null);
126 }
127
128 /**
129 * Starts executing Dart code based on the given {@code dartEntrypoint} and the {@code
130 * dartEntrypointArgs}.
131 *
132 * <p>See {@link DartEntrypoint} for configuration options.
133 *
134 * @param dartEntrypoint specifies which Dart function to run, and where to find it
135 * @param dartEntrypointArgs Arguments passed as a list of string to Dart's entrypoint function.
136 */
138 @NonNull DartEntrypoint dartEntrypoint, @Nullable List<String> dartEntrypointArgs) {
139 if (isApplicationRunning) {
140 Log.w(TAG, "Attempted to run a DartExecutor that is already running.");
141 return;
142 }
143
144 try (TraceSection e = TraceSection.scoped("DartExecutor#executeDartEntrypoint")) {
145 Log.v(TAG, "Executing Dart entrypoint: " + dartEntrypoint);
146 flutterJNI.runBundleAndSnapshotFromLibrary(
147 dartEntrypoint.pathToBundle,
148 dartEntrypoint.dartEntrypointFunctionName,
149 dartEntrypoint.dartEntrypointLibrary,
150 assetManager,
151 dartEntrypointArgs);
152
153 isApplicationRunning = true;
154 }
155 }
156
157 /**
158 * Starts executing Dart code based on the given {@code dartCallback}.
159 *
160 * <p>See {@link DartCallback} for configuration options.
161 *
162 * @param dartCallback specifies which Dart callback to run, and where to find it
163 */
164 public void executeDartCallback(@NonNull DartCallback dartCallback) {
165 if (isApplicationRunning) {
166 Log.w(TAG, "Attempted to run a DartExecutor that is already running.");
167 return;
168 }
169
170 try (TraceSection e = TraceSection.scoped("DartExecutor#executeDartCallback")) {
171 Log.v(TAG, "Executing Dart callback: " + dartCallback);
172 flutterJNI.runBundleAndSnapshotFromLibrary(
173 dartCallback.pathToBundle,
174 dartCallback.callbackHandle.callbackName,
175 dartCallback.callbackHandle.callbackLibraryPath,
176 dartCallback.androidAssetManager,
177 null);
178
179 isApplicationRunning = true;
180 }
181 }
182
183 /**
184 * Returns a {@link BinaryMessenger} that can be used to send messages to, and receive messages
185 * from, Dart code that this {@code DartExecutor} is executing.
186 */
187 @NonNull
188 public BinaryMessenger getBinaryMessenger() {
189 return binaryMessenger;
190 }
191
192 // ------ START BinaryMessenger (Deprecated: use getBinaryMessenger() instead) -----
193 /** @deprecated Use {@link #getBinaryMessenger()} instead. */
194 @Deprecated
195 @UiThread
196 @Override
197 public TaskQueue makeBackgroundTaskQueue(TaskQueueOptions options) {
198 return binaryMessenger.makeBackgroundTaskQueue(options);
199 }
200
201 /** @deprecated Use {@link #getBinaryMessenger()} instead. */
202 @Deprecated
203 @Override
204 @UiThread
205 public void send(@NonNull String channel, @Nullable ByteBuffer message) {
206 binaryMessenger.send(channel, message);
207 }
208
209 /** @deprecated Use {@link #getBinaryMessenger()} instead. */
210 @Deprecated
211 @Override
212 @UiThread
213 public void send(
214 @NonNull String channel,
215 @Nullable ByteBuffer message,
216 @Nullable BinaryMessenger.BinaryReply callback) {
217 binaryMessenger.send(channel, message, callback);
218 }
219
220 /** @deprecated Use {@link #getBinaryMessenger()} instead. */
221 @Deprecated
222 @Override
223 @UiThread
224 public void setMessageHandler(
225 @NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler) {
226 binaryMessenger.setMessageHandler(channel, handler);
227 }
228
229 /** @deprecated Use {@link #getBinaryMessenger()} instead. */
230 @Deprecated
231 @Override
232 @UiThread
233 public void setMessageHandler(
234 @NonNull String channel,
235 @Nullable BinaryMessenger.BinaryMessageHandler handler,
236 @Nullable TaskQueue taskQueue) {
237 binaryMessenger.setMessageHandler(channel, handler, taskQueue);
238 }
239
240 /** @deprecated Use {@link #getBinaryMessenger()} instead. */
241 @Deprecated
242 @Override
244 dartMessenger.enableBufferingIncomingMessages();
245 }
246
247 /** @deprecated Use {@link #getBinaryMessenger()} instead. */
248 @Deprecated
249 @Override
251 dartMessenger.disableBufferingIncomingMessages();
252 }
253 // ------ END BinaryMessenger -----
254
255 /**
256 * Returns the number of pending channel callback replies.
257 *
258 * <p>When sending messages to the Flutter application using {@link BinaryMessenger#send(String,
259 * ByteBuffer, io.flutter.plugin.common.BinaryMessenger.BinaryReply)}, developers can optionally
260 * specify a reply callback if they expect a reply from the Flutter application.
261 *
262 * <p>This method tracks all the pending callbacks that are waiting for response, and is supposed
263 * to be called from the main thread (as other methods). Calling from a different thread could
264 * possibly capture an indeterministic internal state, so don't do it.
265 *
266 * <p>Currently, it's mainly useful for a testing framework like Espresso to determine whether all
267 * the async channel callbacks are handled and the app is idle.
268 */
269 @UiThread
271 return dartMessenger.getPendingChannelResponseCount();
272 }
273
274 /**
275 * Returns an identifier for this executor's primary isolate. This identifier can be used in
276 * queries to the Dart service protocol.
277 */
278 @Nullable
279 public String getIsolateServiceId() {
280 return isolateServiceId;
281 }
282
283 /** Callback interface invoked when the isolate identifier becomes available. */
284 public interface IsolateServiceIdListener {
285 void onIsolateServiceIdAvailable(@NonNull String isolateServiceId);
286 }
287
288 /**
289 * Set a listener that will be notified when an isolate identifier is available for this
290 * executor's primary isolate.
291 */
293 isolateServiceIdListener = listener;
294 if (isolateServiceIdListener != null && isolateServiceId != null) {
295 isolateServiceIdListener.onIsolateServiceIdAvailable(isolateServiceId);
296 }
297 }
298
299 /**
300 * Notify the Dart VM of a low memory event, or that the application is in a state such that now
301 * is an appropriate time to free resources, such as going to the background.
302 *
303 * <p>This does not notify a Flutter application about memory pressure. For that, use the {@link
304 * io.flutter.embedding.engine.systemchannels.SystemChannel#sendMemoryPressureWarning}.
305 *
306 * <p>Calling this method may cause jank or latency in the application. Avoid calling it during
307 * critical periods like application startup or periods of animation.
308 */
310 if (flutterJNI.isAttached()) {
311 flutterJNI.notifyLowMemoryWarning();
312 }
313 }
314
315 /**
316 * Configuration options that specify which Dart entrypoint function is executed and where to find
317 * that entrypoint and other assets required for Dart execution.
318 */
319 public static class DartEntrypoint {
320 /**
321 * Create a DartEntrypoint pointing to the default Flutter assets location with a default Dart
322 * entrypoint.
323 */
324 @NonNull
326 FlutterLoader flutterLoader = FlutterInjector.instance().flutterLoader();
327
328 if (!flutterLoader.initialized()) {
329 throw new AssertionError(
330 "DartEntrypoints can only be created once a FlutterEngine is created.");
331 }
332 return new DartEntrypoint(flutterLoader.findAppBundlePath(), "main");
333 }
334
335 /** The path within the AssetManager where the app will look for assets. */
336 @NonNull public final String pathToBundle;
337
338 /** The library or file location that contains the Dart entrypoint function. */
339 @Nullable public final String dartEntrypointLibrary;
340
341 /** The name of a Dart function to execute. */
342 @NonNull public final String dartEntrypointFunctionName;
343
345 @NonNull String pathToBundle, @NonNull String dartEntrypointFunctionName) {
346 this.pathToBundle = pathToBundle;
348 this.dartEntrypointFunctionName = dartEntrypointFunctionName;
349 }
350
352 @NonNull String pathToBundle,
353 @NonNull String dartEntrypointLibrary,
354 @NonNull String dartEntrypointFunctionName) {
355 this.pathToBundle = pathToBundle;
356 this.dartEntrypointLibrary = dartEntrypointLibrary;
357 this.dartEntrypointFunctionName = dartEntrypointFunctionName;
358 }
359
360 @Override
361 @NonNull
362 public String toString() {
363 return "DartEntrypoint( bundle path: "
365 + ", function: "
367 + " )";
368 }
369
370 @Override
371 public boolean equals(Object o) {
372 if (this == o) return true;
373 if (o == null || getClass() != o.getClass()) return false;
374
376
377 if (!pathToBundle.equals(that.pathToBundle)) return false;
379 }
380
381 @Override
382 public int hashCode() {
383 int result = pathToBundle.hashCode();
384 result = 31 * result + dartEntrypointFunctionName.hashCode();
385 return result;
386 }
387 }
388
389 /**
390 * Configuration options that specify which Dart callback function is executed and where to find
391 * that callback and other assets required for Dart execution.
392 */
393 public static class DartCallback {
394 /** Standard Android AssetManager, provided from some {@code Context} or {@code Resources}. */
395 public final AssetManager androidAssetManager;
396
397 /** The path within the AssetManager where the app will look for assets. */
398 public final String pathToBundle;
399
400 /** A Dart callback that was previously registered with the Dart VM. */
402
404 @NonNull AssetManager androidAssetManager,
405 @NonNull String pathToBundle,
407 this.androidAssetManager = androidAssetManager;
408 this.pathToBundle = pathToBundle;
409 this.callbackHandle = callbackHandle;
410 }
411
412 @Override
413 @NonNull
414 public String toString() {
415 return "DartCallback( bundle path: "
417 + ", library path: "
418 + callbackHandle.callbackLibraryPath
419 + ", function: "
420 + callbackHandle.callbackName
421 + " )";
422 }
423 }
424
425 private static class DefaultBinaryMessenger implements BinaryMessenger {
426 private final DartMessenger messenger;
427
428 private DefaultBinaryMessenger(@NonNull DartMessenger messenger) {
429 this.messenger = messenger;
430 }
431
432 public TaskQueue makeBackgroundTaskQueue(TaskQueueOptions options) {
433 return messenger.makeBackgroundTaskQueue(options);
434 }
435
436 /**
437 * Sends the given {@code message} from Android to Dart over the given {@code channel}.
438 *
439 * @param channel the name of the logical channel used for the message.
440 * @param message the message payload, a direct-allocated {@link ByteBuffer} with the message
441 * bytes
442 */
443 @Override
444 @UiThread
445 public void send(@NonNull String channel, @Nullable ByteBuffer message) {
446 messenger.send(channel, message, null);
447 }
448
449 /**
450 * Sends the given {@code messages} from Android to Dart over the given {@code channel} and then
451 * has the provided {@code callback} invoked when the Dart side responds.
452 *
453 * @param channel the name of the logical channel used for the message.
454 * @param message the message payload, a direct-allocated {@link ByteBuffer} with the message
455 * bytes between position zero and current position, or null.
456 * @param callback a callback invoked when the Dart application responds to the message
457 */
458 @Override
459 @UiThread
460 public void send(
461 @NonNull String channel,
462 @Nullable ByteBuffer message,
463 @Nullable BinaryMessenger.BinaryReply callback) {
464 messenger.send(channel, message, callback);
465 }
466
467 /**
468 * Sets the given {@link io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler} as the
469 * singular handler for all incoming messages received from the Dart side of this Dart execution
470 * context.
471 *
472 * @param channel the name of the channel.
473 * @param handler a {@link BinaryMessageHandler} to be invoked on incoming messages, or null.
474 */
475 @Override
476 @UiThread
477 public void setMessageHandler(
478 @NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler) {
479 messenger.setMessageHandler(channel, handler);
480 }
481
482 @Override
483 @UiThread
484 public void setMessageHandler(
485 @NonNull String channel,
486 @Nullable BinaryMessenger.BinaryMessageHandler handler,
487 @Nullable TaskQueue taskQueue) {
488 messenger.setMessageHandler(channel, handler, taskQueue);
489 }
490
491 @Override
492 public void enableBufferingIncomingMessages() {
493 messenger.enableBufferingIncomingMessages();
494 }
495
496 @Override
497 public void disableBufferingIncomingMessages() {
499 }
500 }
501}
const char * options
static void v(@NonNull String tag, @NonNull String message)
Definition Log.java:40
static void w(@NonNull String tag, @NonNull String message)
Definition Log.java:76
DartCallback( @NonNull AssetManager androidAssetManager, @NonNull String pathToBundle, @NonNull FlutterCallbackInformation callbackHandle)
DartEntrypoint( @NonNull String pathToBundle, @NonNull String dartEntrypointLibrary, @NonNull String dartEntrypointFunctionName)
DartEntrypoint( @NonNull String pathToBundle, @NonNull String dartEntrypointFunctionName)
TaskQueue makeBackgroundTaskQueue(TaskQueueOptions options)
void executeDartEntrypoint(@NonNull DartEntrypoint dartEntrypoint)
void send(@NonNull String channel, @Nullable ByteBuffer message)
void send( @NonNull String channel, @Nullable ByteBuffer message, @Nullable BinaryMessenger.BinaryReply callback)
void executeDartEntrypoint( @NonNull DartEntrypoint dartEntrypoint, @Nullable List< String > dartEntrypointArgs)
void setIsolateServiceIdListener(@Nullable IsolateServiceIdListener listener)
void executeDartCallback(@NonNull DartCallback dartCallback)
DartExecutor(@NonNull FlutterJNI flutterJNI, @NonNull AssetManager assetManager)
void setMessageHandler( @NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler, @Nullable TaskQueue taskQueue)
void setMessageHandler( @NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler)
void setMessageHandler( @NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler)
TaskQueue makeBackgroundTaskQueue(TaskQueueOptions options)
void send(@NonNull String channel, @NonNull ByteBuffer message)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
GAsyncResult * result
void onIsolateServiceIdAvailable(@NonNull String isolateServiceId)
Win32Message message
#define TAG()