public class FlutterJNI extends Object
Flutter's engine is built with C/C++. The Android Flutter embedding is responsible for coordinating Android OS events and app user interactions with the C/C++ engine. Such coordination requires messaging from an Android app in Java code to the C/C++ engine code. This communication requires a JNI (Java Native Interface) API to cross the Java/native boundary.
The entirety of Flutter's JNI API is codified in FlutterJNI
. There are multiple
reasons that all such calls are centralized in one class. First, JNI calls are inherently static
and contain no Java implementation, therefore there is little reason to associate calls with
different classes. Second, every JNI call must be registered in C/C++ code and this registration
becomes more complicated with every additional Java class that contains JNI calls. Third, most
Android developers are not familiar with native development or JNI intricacies, therefore it is
in the interest of future maintenance to reduce the API surface that includes JNI declarations.
Thus, all Flutter JNI calls are centralized in FlutterJNI
.
Despite the fact that individual JNI calls are inherently static, there is state that exists
within FlutterJNI
. Most calls within FlutterJNI
correspond to a specific
"platform view", of which there may be many. Therefore, each FlutterJNI
instance holds
onto a "native platform view ID" after attachToNative(boolean)
, which is shared with the
native C/C++ engine code. That ID is passed to every platform-view-specific native method. ID
management is handled within FlutterJNI
so that developers don't have to hold onto that
ID.
To connect part of an Android app to Flutter's C/C++ engine, instantiate a FlutterJNI
and then attach it to the native side:
// Instantiate FlutterJNI and attach to the native side.
FlutterJNI flutterJNI = new FlutterJNI();
flutterJNI.attachToNative();
// Use FlutterJNI as desired. flutterJNI.dispatchPointerDataPacket(...);
// Destroy the connection to the native side and cleanup.
flutterJNI.detachFromNativeAndReleaseResources();
To provide a visual, interactive surface for Flutter rendering and touch events, register a
RenderSurface
with #setRenderSurface(RenderSurface)
To receive callbacks for certain events that occur on the native side, register listeners:
#addEngineLifecycleListener(EngineLifecycleListener)
addIsDisplayingFlutterUiListener(FlutterUiDisplayListener)
setPlatformMessageHandler(PlatformMessageHandler)
To invoke a native method that is not associated with a platform view, invoke it statically:
bool enabled = FlutterJNI.getIsSoftwareRenderingEnabled();
Modifier and Type | Class and Description |
---|---|
static interface |
FlutterJNI.AccessibilityDelegate
Delegate responsible for creating and updating Android-side caches of Flutter's semantics tree
and custom accessibility actions.
|
static interface |
FlutterJNI.AsyncWaitForVsyncDelegate |
Constructor and Description |
---|
FlutterJNI() |
Modifier and Type | Method and Description |
---|---|
void |
addEngineLifecycleListener(FlutterEngine.EngineLifecycleListener engineLifecycleListener)
Adds the given
engineLifecycleListener to be notified of Flutter engine lifecycle
events, e.g., FlutterEngine.EngineLifecycleListener.onPreEngineRestart() . |
void |
addIsDisplayingFlutterUiListener(FlutterUiDisplayListener listener)
Adds a
FlutterUiDisplayListener , which receives a callback when Flutter's engine
notifies FlutterJNI that Flutter is painting pixels to the Surface that was
provided to Flutter. |
void |
attachToNative(boolean isBackgroundView)
Attaches this
FlutterJNI instance to Flutter's native engine, which allows for
communication between Android code and Flutter's platform agnostic engine. |
FlutterOverlaySurface |
createOverlaySurface() |
void |
deferredComponentInstallFailure(int loadingUnitId,
String error,
boolean isTransient)
Indicates that a failure was encountered during the Android portion of downloading a dynamic
feature module and loading a dart deferred library, which is typically done by
DeferredComponentManager.
|
void |
destroyOverlaySurfaces() |
void |
detachFromNativeAndReleaseResources()
Detaches this
FlutterJNI instance from Flutter's native engine, which precludes any
further communication between Android code and Flutter's platform agnostic engine. |
void |
dispatchEmptyPlatformMessage(String channel,
int responseId)
Sends an empty reply (identified by
responseId ) from Android to Flutter over the given
channel . |
void |
dispatchPlatformMessage(String channel,
ByteBuffer message,
int position,
int responseId)
Sends a reply
message from Android to Flutter over the given channel . |
void |
dispatchPointerDataPacket(ByteBuffer buffer,
int position)
Sends a packet of pointer data to Flutter's engine.
|
void |
dispatchSemanticsAction(int id,
AccessibilityBridge.Action action)
Sends a semantics action to Flutter's engine, without any additional arguments.
|
void |
dispatchSemanticsAction(int id,
AccessibilityBridge.Action action,
Object args)
Sends a semantics action to Flutter's engine, with additional arguments.
|
void |
dispatchSemanticsAction(int id,
int action,
ByteBuffer args,
int argsPosition)
Sends a semantics action to Flutter's engine, given arguments that are already encoded for the
engine.
|
Bitmap |
getBitmap() |
boolean |
getIsSoftwareRenderingEnabled() |
static String |
getObservatoryUri() |
void |
handlePlatformMessage(String channel,
byte[] message,
int replyId) |
void |
init(Context context,
String[] args,
String bundlePath,
String appStoragePath,
String engineCachesPath,
long initTimeMillis)
Perform one time initialization of the Dart VM and Flutter engine.
|
void |
invokePlatformMessageEmptyResponseCallback(int responseId) |
void |
invokePlatformMessageResponseCallback(int responseId,
ByteBuffer message,
int position) |
boolean |
isAttached()
Returns true if this instance of
FlutterJNI is connected to Flutter's native engine via
a Java Native Interface (JNI). |
void |
loadDartDeferredLibrary(int loadingUnitId,
String[] searchPaths)
Searches each of the provided paths for a valid Dart shared library .so file and resolves
symbols to load into the dart VM.
|
void |
loadLibrary()
Loads the libflutter.so C++ library.
|
void |
markTextureFrameAvailable(long textureId)
Call this method to inform Flutter that a texture previously registered with
#registerTexture(long, SurfaceTexture) has a new frame available. |
boolean |
nativeFlutterTextUtilsIsEmoji(int codePoint) |
boolean |
nativeFlutterTextUtilsIsEmojiModifier(int codePoint) |
boolean |
nativeFlutterTextUtilsIsEmojiModifierBase(int codePoint) |
boolean |
nativeFlutterTextUtilsIsRegionalIndicator(int codePoint) |
boolean |
nativeFlutterTextUtilsIsVariationSelector(int codePoint) |
static void |
nativeInit(Context context,
String[] args,
String bundlePath,
String appStoragePath,
String engineCachesPath,
long initTimeMillis)
Deprecated.
|
static FlutterCallbackInformation |
nativeLookupCallbackInformation(long handle) |
static void |
nativeOnVsync(long frameTimeNanos,
long frameTargetTimeNanos,
long cookie) |
static void |
nativePrefetchDefaultFontManager()
Deprecated.
Use
prefetchDefaultFontManager() instead. |
void |
notifyLowMemoryWarning()
Notifies the Dart VM of a low memory event, or that the application is in a state such that now
is an appropriate time to free resources, such as going to the background.
|
void |
onBeginFrame() |
void |
onDisplayOverlaySurface(int id,
int x,
int y,
int width,
int height) |
void |
onDisplayPlatformView(int viewId,
int x,
int y,
int width,
int height,
int viewWidth,
int viewHeight,
FlutterMutatorsStack mutatorsStack) |
void |
onEndFrame() |
void |
onFirstFrame() |
void |
onSurfaceChanged(int width,
int height)
Call this method when the
Surface changes that was previously registered with onSurfaceCreated(Surface) . |
void |
onSurfaceCreated(Surface surface)
Call this method when a
Surface has been created onto which you would like Flutter to
paint. |
void |
onSurfaceDestroyed()
Call this method when the
Surface is destroyed that was previously registered with
onSurfaceCreated(Surface) . |
void |
onSurfaceWindowChanged(Surface surface)
In hybrid composition, call this method when the
Surface has changed. |
long |
performNativeAttach(FlutterJNI flutterJNI,
boolean isBackgroundView) |
void |
prefetchDefaultFontManager()
Prefetch the default font manager provided by SkFontMgr::RefDefault() which is a process-wide
singleton owned by Skia.
|
void |
registerTexture(long textureId,
SurfaceTextureWrapper textureWrapper)
Gives control of a
SurfaceTexture to Flutter so that Flutter can display that texture
within Flutter's UI. |
void |
removeEngineLifecycleListener(FlutterEngine.EngineLifecycleListener engineLifecycleListener)
Removes the given
engineLifecycleListener , which was previously added using addIsDisplayingFlutterUiListener(FlutterUiDisplayListener) . |
void |
removeIsDisplayingFlutterUiListener(FlutterUiDisplayListener listener)
Removes a
FlutterUiDisplayListener that was added with addIsDisplayingFlutterUiListener(FlutterUiDisplayListener) . |
void |
requestDartDeferredLibrary(int loadingUnitId)
Called by dart to request that a Dart deferred library corresponding to loadingUnitId be
downloaded (if necessary) and loaded into the dart vm.
|
void |
runBundleAndSnapshotFromLibrary(String bundlePath,
String entrypointFunctionName,
String pathToEntrypointFunction,
AssetManager assetManager)
Executes a Dart entrypoint.
|
void |
setAccessibilityDelegate(FlutterJNI.AccessibilityDelegate accessibilityDelegate)
Sets the
FlutterJNI.AccessibilityDelegate for the attached Flutter context. |
void |
setAccessibilityFeatures(int flags) |
static void |
setAsyncWaitForVsyncDelegate(FlutterJNI.AsyncWaitForVsyncDelegate delegate) |
void |
setDeferredComponentManager(io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager deferredComponentManager)
Sets the deferred component manager that is used to download and install split features.
|
void |
setLocalizationPlugin(io.flutter.plugin.localization.LocalizationPlugin localizationPlugin)
Sets the localization plugin that is used in various localization methods.
|
void |
setPlatformMessageHandler(PlatformMessageHandler platformMessageHandler)
Sets the handler for all platform messages that come from the attached platform view to Java.
|
void |
setPlatformViewsController(PlatformViewsController platformViewsController) |
static void |
setRefreshRateFPS(float refreshRateFPS) |
void |
setSemanticsEnabled(boolean enabled)
Instructs Flutter to enable/disable its semantics tree, which is used by Flutter to support
accessibility and related behaviors.
|
void |
setViewportMetrics(float devicePixelRatio,
int physicalWidth,
int physicalHeight,
int physicalPaddingTop,
int physicalPaddingRight,
int physicalPaddingBottom,
int physicalPaddingLeft,
int physicalViewInsetTop,
int physicalViewInsetRight,
int physicalViewInsetBottom,
int physicalViewInsetLeft,
int systemGestureInsetTop,
int systemGestureInsetRight,
int systemGestureInsetBottom,
int systemGestureInsetLeft)
Call this method to notify Flutter of the current device viewport metrics that are applies to
the Flutter UI that is being rendered.
|
FlutterJNI |
spawn(String entrypointFunctionName,
String pathToEntrypointFunction)
Spawns a new FlutterJNI instance from the current instance.
|
void |
unregisterTexture(long textureId)
Unregisters a texture that was registered with
#registerTexture(long, SurfaceTexture) . |
void |
updateJavaAssetManager(AssetManager assetManager,
String assetBundlePath)
Adds the specified AssetManager as an APKAssetResolver in the Flutter Engine's AssetManager.
|
public void loadLibrary()
This must be called before any other native methods, and can be overridden by tests to avoid loading native libraries.
This method should only be called once across all FlutterJNI instances.
public void prefetchDefaultFontManager()
This method should only be called once across all FlutterJNI instances.
public void init(@NonNull Context context, @NonNull String[] args, @Nullable String bundlePath, @NonNull String appStoragePath, @NonNull String engineCachesPath, long initTimeMillis)
This method must be called only once. Calling more than once will cause an exception.
context
- The application context.args
- Arguments to the Dart VM/Flutter engine.bundlePath
- For JIT runtimes, the path to the Dart kernel file for the application.appStoragePath
- The path to the application data directory.engineCachesPath
- The path to the application cache directory.initTimeMillis
- The time, in milliseconds, taken for initialization.@Deprecated public static void nativeInit(@NonNull Context context, @NonNull String[] args, @Nullable String bundlePath, @NonNull String appStoragePath, @NonNull String engineCachesPath, long initTimeMillis)
init(Context, String[], String, String, String, long)
instead.@Deprecated public static void nativePrefetchDefaultFontManager()
prefetchDefaultFontManager()
instead.@UiThread public boolean getIsSoftwareRenderingEnabled()
@Nullable public static String getObservatoryUri()
public static void setRefreshRateFPS(float refreshRateFPS)
public static void setAsyncWaitForVsyncDelegate(@Nullable FlutterJNI.AsyncWaitForVsyncDelegate delegate)
public static void nativeOnVsync(long frameTimeNanos, long frameTargetTimeNanos, long cookie)
@NonNull public static FlutterCallbackInformation nativeLookupCallbackInformation(long handle)
public boolean nativeFlutterTextUtilsIsEmoji(int codePoint)
public boolean nativeFlutterTextUtilsIsEmojiModifier(int codePoint)
public boolean nativeFlutterTextUtilsIsEmojiModifierBase(int codePoint)
public boolean nativeFlutterTextUtilsIsVariationSelector(int codePoint)
public boolean nativeFlutterTextUtilsIsRegionalIndicator(int codePoint)
public boolean isAttached()
FlutterJNI
is connected to Flutter's native engine via
a Java Native Interface (JNI).@UiThread public void attachToNative(boolean isBackgroundView)
FlutterJNI
instance to Flutter's native engine, which allows for
communication between Android code and Flutter's platform agnostic engine.
This method must not be invoked if FlutterJNI
is already attached to native.
public long performNativeAttach(@NonNull FlutterJNI flutterJNI, boolean isBackgroundView)
@UiThread @NonNull public FlutterJNI spawn(@Nullable String entrypointFunctionName, @Nullable String pathToEntrypointFunction)
This creates another native shell from the current shell. This causes the 2 shells to re-use some of the shared resources, reducing the total memory consumption versus creating a new FlutterJNI by calling its standard constructor.
This can only be called once the current FlutterJNI instance is attached by calling attachToNative(boolean)
.
Static methods that should be only called once such as init(Context, String[],
String, String, String, long)
or setRefreshRateFPS(float)
shouldn't be called again
on the spawned FlutterJNI instance.
@UiThread public void detachFromNativeAndReleaseResources()
FlutterJNI
instance from Flutter's native engine, which precludes any
further communication between Android code and Flutter's platform agnostic engine.
This method must not be invoked if FlutterJNI
is not already attached to native.
Invoking this method will result in the release of all native-side resources that were setup
during attachToNative(boolean)
or spawn(String, String)
, or accumulated
thereafter.
It is permissable to re-attach this instance to native after detaching it from native.
@UiThread public void addIsDisplayingFlutterUiListener(@NonNull FlutterUiDisplayListener listener)
FlutterUiDisplayListener
, which receives a callback when Flutter's engine
notifies FlutterJNI
that Flutter is painting pixels to the Surface
that was
provided to Flutter.@UiThread public void removeIsDisplayingFlutterUiListener(@NonNull FlutterUiDisplayListener listener)
FlutterUiDisplayListener
that was added with addIsDisplayingFlutterUiListener(FlutterUiDisplayListener)
.@UiThread public void onFirstFrame()
@UiThread public void onSurfaceCreated(@NonNull Surface surface)
Surface
has been created onto which you would like Flutter to
paint.
See SurfaceHolder.Callback.surfaceCreated(SurfaceHolder)
for an example
of where this call might originate.
@UiThread public void onSurfaceWindowChanged(@NonNull Surface surface)
Surface
has changed.
In hybrid composition, the root surfaces changes from SurfaceHolder.getSurface()
to ImageReader.getSurface()
when
a platform view is in the current frame.
@UiThread public void onSurfaceChanged(int width, int height)
Surface
changes that was previously registered with onSurfaceCreated(Surface)
.
See SurfaceHolder.Callback.surfaceChanged(SurfaceHolder, int, int, int)
for an example of where this call might originate.
@UiThread public void onSurfaceDestroyed()
Surface
is destroyed that was previously registered with
onSurfaceCreated(Surface)
.
See SurfaceHolder.Callback.surfaceDestroyed(SurfaceHolder)
for an
example of where this call might originate.
@UiThread public void setViewportMetrics(float devicePixelRatio, int physicalWidth, int physicalHeight, int physicalPaddingTop, int physicalPaddingRight, int physicalPaddingBottom, int physicalPaddingLeft, int physicalViewInsetTop, int physicalViewInsetRight, int physicalViewInsetBottom, int physicalViewInsetLeft, int systemGestureInsetTop, int systemGestureInsetRight, int systemGestureInsetBottom, int systemGestureInsetLeft)
This method should be invoked with initial values upon attaching to native. Then, it should
be invoked any time those metrics change while FlutterJNI
is attached to native.
@UiThread public void dispatchPointerDataPacket(@NonNull ByteBuffer buffer, int position)
@UiThread public void setPlatformViewsController(@NonNull PlatformViewsController platformViewsController)
@UiThread public void setAccessibilityDelegate(@Nullable FlutterJNI.AccessibilityDelegate accessibilityDelegate)
FlutterJNI.AccessibilityDelegate
for the attached Flutter context.
The FlutterJNI.AccessibilityDelegate
is responsible for maintaining an Android-side cache of
Flutter's semantics tree and custom accessibility actions. This cache should be hooked up to
Android's accessibility system.
See AccessibilityBridge
for an example of an FlutterJNI.AccessibilityDelegate
and the
surrounding responsibilities.
public void dispatchSemanticsAction(int id, @NonNull AccessibilityBridge.Action action)
public void dispatchSemanticsAction(int id, @NonNull AccessibilityBridge.Action action, @Nullable Object args)
@UiThread public void dispatchSemanticsAction(int id, int action, @Nullable ByteBuffer args, int argsPosition)
To send a semantics action that has not already been encoded, see dispatchSemanticsAction(int, AccessibilityBridge.Action)
and dispatchSemanticsAction(int, AccessibilityBridge.Action, Object)
.
@UiThread public void setSemanticsEnabled(boolean enabled)
@UiThread public void setAccessibilityFeatures(int flags)
@UiThread public void registerTexture(long textureId, @NonNull SurfaceTextureWrapper textureWrapper)
SurfaceTexture
to Flutter so that Flutter can display that texture
within Flutter's UI.@UiThread public void markTextureFrameAvailable(long textureId)
#registerTexture(long, SurfaceTexture)
has a new frame available.
Invoking this method instructs Flutter to update its presentation of the given texture so that the new frame is displayed.
@UiThread public void unregisterTexture(long textureId)
#registerTexture(long, SurfaceTexture)
.@UiThread public void runBundleAndSnapshotFromLibrary(@NonNull String bundlePath, @Nullable String entrypointFunctionName, @Nullable String pathToEntrypointFunction, @NonNull AssetManager assetManager)
This can only be done once per JNI attachment because a Dart isolate can only be entered once.
@UiThread public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler)
Communication between a specific Flutter context (Dart) and the host platform (Java) is
accomplished by passing messages. Messages can be sent from Java to Dart with the corresponding
FlutterJNI
methods:
FlutterJNI
is also the recipient of all platform messages sent from its attached
Flutter context. FlutterJNI
does not know what to do with these messages, so a handler
is exposed to allow these messages to be processed in whatever manner is desired:
setPlatformMessageHandler(PlatformMessageHandler)
If a message is received but no PlatformMessageHandler
is registered, that message
will be dropped (ignored). Therefore, when using FlutterJNI
to integrate a Flutter
context in an app, a PlatformMessageHandler
must be registered for 2-way Java/Dart
communication to operate correctly. Moreover, the handler must be implemented such that
fundamental platform messages are handled as expected. See FlutterNativeView
for an
example implementation.
public void handlePlatformMessage(@NonNull String channel, byte[] message, int replyId)
@UiThread public void dispatchEmptyPlatformMessage(@NonNull String channel, int responseId)
responseId
) from Android to Flutter over the given
channel
.@UiThread public void dispatchPlatformMessage(@NonNull String channel, @Nullable ByteBuffer message, int position, int responseId)
message
from Android to Flutter over the given channel
.@UiThread public void invokePlatformMessageEmptyResponseCallback(int responseId)
@UiThread public void invokePlatformMessageResponseCallback(int responseId, @Nullable ByteBuffer message, int position)
@UiThread public void addEngineLifecycleListener(@NonNull FlutterEngine.EngineLifecycleListener engineLifecycleListener)
engineLifecycleListener
to be notified of Flutter engine lifecycle
events, e.g., FlutterEngine.EngineLifecycleListener.onPreEngineRestart()
.@UiThread public void removeEngineLifecycleListener(@NonNull FlutterEngine.EngineLifecycleListener engineLifecycleListener)
engineLifecycleListener
, which was previously added using addIsDisplayingFlutterUiListener(FlutterUiDisplayListener)
.@UiThread public void onDisplayOverlaySurface(int id, int x, int y, int width, int height)
@UiThread public void onBeginFrame()
@UiThread public void onEndFrame()
@UiThread public FlutterOverlaySurface createOverlaySurface()
@UiThread public void destroyOverlaySurfaces()
@UiThread public void setLocalizationPlugin(@Nullable io.flutter.plugin.localization.LocalizationPlugin localizationPlugin)
@UiThread public void setDeferredComponentManager(@Nullable io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager deferredComponentManager)
@UiThread public void requestDartDeferredLibrary(int loadingUnitId)
This method delegates the task to DeferredComponentManager, which handles the download and loading of the dart library and any assets.
loadingUnitId
- The loadingUnitId is assigned during compile time by gen_snapshot and is
automatically retrieved when loadLibrary() is called on a dart deferred library.@UiThread public void loadDartDeferredLibrary(int loadingUnitId, @NonNull String[] searchPaths)
Successful loading of the dart library completes the future returned by loadLibrary() that triggered the install/load process.
loadingUnitId
- The loadingUnitId is assigned during compile time by gen_snapshot and is
automatically retrieved when loadLibrary() is called on a dart deferred library. This is
used to identify which Dart deferred library the resolved correspond to.searchPaths
- An array of paths in which to look for valid dart shared libraries. This
supports paths within zipped apks as long as the apks are not compressed using the
`path/to/apk.apk!path/inside/apk/lib.so` format. Paths will be tried first to last and ends
when a library is sucessfully found. When the found library is invalid, no additional paths
will be attempted.@UiThread public void updateJavaAssetManager(@NonNull AssetManager assetManager, @NonNull String assetBundlePath)
This may be used to update the engine AssetManager when a new deferred component is installed and a new Android AssetManager is created with access to new assets.
assetManager
- An android AssetManager that is able to access the newly downloaded assets.assetBundlePath
- The subdirectory that the flutter assets are stored in. The typical
value is `flutter_assets`.@UiThread public void deferredComponentInstallFailure(int loadingUnitId, @NonNull String error, boolean isTransient)
This will inform dart that the future returned by loadLibrary() should complete with an error.
loadingUnitId
- The loadingUnitId that corresponds to the dart deferred library that
failed to install.error
- The error message to display.isTransient
- When isTransient is false, new attempts to install will automatically result
in same error in Dart before the request is passed to Android.@UiThread public void onDisplayPlatformView(int viewId, int x, int y, int width, int height, int viewWidth, int viewHeight, FlutterMutatorsStack mutatorsStack)
@UiThread public Bitmap getBitmap()
@UiThread public void notifyLowMemoryWarning()
This is distinct from sending a SystemChannel message about low memory, which only notifies the running Flutter application.