From 26b7e87ce7238a4a477ee6db2285233adf2c2219 Mon Sep 17 00:00:00 2001 From: matthew-carroll Date: Wed, 5 Dec 2018 12:57:34 -0800 Subject: [PATCH 1/9] Android embedding refactor PR1: JNI Extraction to FlutterJNI.java --- ci/licenses_golden/licenses_flutter | 5 + shell/platform/android/.gitignore | 3 + shell/platform/android/BUILD.gn | 5 + .../embedding/engine/FlutterEngine.java | 23 ++ .../flutter/embedding/engine/FlutterJNI.java | 217 ++++++++++++++ .../engine/dart/PlatformMessageHandler.java | 15 + .../engine/renderer/FlutterRenderer.java | 275 ++++++++++++++++++ .../OnFirstFrameRenderedListener.java | 18 ++ .../io/flutter/view/FlutterNativeView.java | 196 ++++++------- .../android/io/flutter/view/FlutterView.java | 78 ++--- .../android/platform_view_android_jni.cc | 204 +++++++------ 11 files changed, 786 insertions(+), 253 deletions(-) create mode 100644 shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java create mode 100644 shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java create mode 100644 shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java create mode 100644 shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java create mode 100644 shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 63b2ac899e260..64211045803fb 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -416,6 +416,11 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivityEven FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterApplication.java FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/ActivityLifecycleListener.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BasicMessageChannel.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BinaryCodec.java diff --git a/shell/platform/android/.gitignore b/shell/platform/android/.gitignore index 2e8ec55e9bf6b..598adcf6cd3d0 100644 --- a/shell/platform/android/.gitignore +++ b/shell/platform/android/.gitignore @@ -1,2 +1,5 @@ # Generated by Intellij's Android plugin gen +android.iml +out/ + diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index aad27b2f9dd37..c7884169d6ca9 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -102,6 +102,11 @@ java_library("flutter_shell_java") { "io/flutter/app/FlutterApplication.java", "io/flutter/app/FlutterFragmentActivity.java", "io/flutter/app/FlutterPluginRegistry.java", + "io/flutter/embedding/engine/FlutterEngine.java", + "io/flutter/embedding/engine/FlutterJNI.java", + "io/flutter/embedding/engine/dart/PlatformMessageHandler.java", + "io/flutter/embedding/engine/renderer/FlutterRenderer.java", + "io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java", "io/flutter/plugin/common/ActivityLifecycleListener.java", "io/flutter/plugin/common/BasicMessageChannel.java", "io/flutter/plugin/common/BinaryCodec.java", diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java new file mode 100644 index 0000000000000..a97ef757a29b6 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.engine; + +/** + * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. + * IF YOU USE IT, WE WILL BREAK YOU. + */ +public class FlutterEngine { + // TODO(mattcarroll): bring in FlutterEngine implementation in future PR + + /** + * Lifecycle callbacks for Flutter engine lifecycle events. + */ + public interface EngineLifecycleListener { + /** + * Lifecycle callback invoked after a hot restart of the Flutter engine. + */ + void onPreEngineRestart(); + } +} diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java new file mode 100644 index 0000000000000..3509f150873bb --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -0,0 +1,217 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.engine; + +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.SurfaceTexture; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; +import android.view.Surface; + +import java.nio.ByteBuffer; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +import io.flutter.embedding.engine.dart.PlatformMessageHandler; +import io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener; +import io.flutter.embedding.engine.renderer.FlutterRenderer; +import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener; + +/** + * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. + * IF YOU USE IT, WE WILL BREAK YOU. + */ +public class FlutterJNI { + private static final String TAG = "FlutterJNI"; + + private FlutterRenderer.RenderSurface renderSurface; + private PlatformMessageHandler platformMessageHandler; + private final Set engineLifecycleListeners = new CopyOnWriteArraySet<>(); + private final Set firstFrameListeners = new CopyOnWriteArraySet<>(); + + public void setRenderSurface(@Nullable FlutterRenderer.RenderSurface renderSurface) { + this.renderSurface = renderSurface; + } + + public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler) { + this.platformMessageHandler = platformMessageHandler; + } + + public void addEngineLifecycleListener(@NonNull EngineLifecycleListener engineLifecycleListener) { + engineLifecycleListeners.add(engineLifecycleListener); + } + + public void removeEngineLifecycleListener(@NonNull EngineLifecycleListener engineLifecycleListener) { + engineLifecycleListeners.remove(engineLifecycleListener); + } + + public void addOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { + firstFrameListeners.add(listener); + } + + public void removeOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { + firstFrameListeners.remove(listener); + } + + //------ START RENDER SURFACE CALLBACKS ----- + // TODO(mattcarroll): define "update" + // Called by native to update the semantics/accessibility tree. + @SuppressWarnings("unused") + public void updateSemantics(ByteBuffer buffer, String[] strings) { + Log.d(TAG, "updateSemantics()"); + if (renderSurface != null) { + renderSurface.updateSemantics(buffer, strings); + } + } + + // TODO(mattcarroll): define "update" + // Called by native to update the custom accessibility actions. + @SuppressWarnings("unused") + public void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { + Log.d(TAG, "updateCustomAccessibilityActions()"); + if (renderSurface != null) { + renderSurface.updateCustomAccessibilityActions(buffer, strings); + } + } + + // Called by native to notify first Flutter frame rendered. + @SuppressWarnings("unused") + private void onFirstFrame() { + Log.d(TAG, "onFirstFrame()"); + if (renderSurface != null) { + renderSurface.onFirstFrameRendered(); + } + + for (OnFirstFrameRenderedListener listener : firstFrameListeners) { + listener.onFirstFrameRendered(); + } + } + //------ END RENDER SURFACE CALLBACKS ------ + + //------ START PLATFORM MESSAGE CALLBACKS ---- + @SuppressWarnings("unused") + private void handlePlatformMessage(final String channel, byte[] message, final int replyId) { + if (platformMessageHandler != null) { + platformMessageHandler.handlePlatformMessage(channel, message, replyId); + } + } + + // Called by native to respond to a platform message that we sent. + @SuppressWarnings("unused") + private void handlePlatformMessageResponse(int replyId, byte[] reply) { + if (platformMessageHandler != null) { + platformMessageHandler.handlePlatformMessageResponse(replyId, reply); + } + } + //------ END PLATFORM MESSAGE CALLBACKS ---- + + // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters + //----- Start from FlutterView ----- + public native void nativeSurfaceCreated(long nativePlatformViewAndroid, Surface surface); + + public native void nativeSurfaceChanged(long nativePlatformViewAndroid, + int width, + int height); + + public native void nativeSurfaceDestroyed(long nativePlatformViewAndroid); + + public native void nativeSetViewportMetrics(long nativePlatformViewAndroid, + float devicePixelRatio, + int physicalWidth, + int physicalHeight, + int physicalPaddingTop, + int physicalPaddingRight, + int physicalPaddingBottom, + int physicalPaddingLeft, + int physicalViewInsetTop, + int physicalViewInsetRight, + int physicalViewInsetBottom, + int physicalViewInsetLeft); + + public native Bitmap nativeGetBitmap(long nativePlatformViewAndroid); + + public native void nativeDispatchPointerDataPacket(long nativePlatformViewAndroid, + ByteBuffer buffer, + int position); + + public native void nativeDispatchSemanticsAction(long nativePlatformViewAndroid, + int id, + int action, + ByteBuffer args, + int argsPosition); + + public native void nativeSetSemanticsEnabled(long nativePlatformViewAndroid, boolean enabled); + + public native void nativeSetAccessibilityFeatures(long nativePlatformViewAndroid, int flags); + + public native boolean nativeGetIsSoftwareRenderingEnabled(); + + public native void nativeRegisterTexture(long nativePlatformViewAndroid, long textureId, SurfaceTexture surfaceTexture); + + public native void nativeMarkTextureFrameAvailable(long nativePlatformViewAndroid, long textureId); + + public native void nativeUnregisterTexture(long nativePlatformViewAndroid, long textureId); + //------- End from FlutterView ----- + + // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters + //------ Start from FlutterNativeView ---- + public native long nativeAttach(FlutterJNI flutterJNI, boolean isBackgroundView); + public native void nativeDestroy(long nativePlatformViewAndroid); + public native void nativeDetach(long nativePlatformViewAndroid); + + public native void nativeRunBundleAndSnapshotFromLibrary( + long nativePlatformViewAndroid, + @NonNull String pathToBundleWithEntrypoint, + @Nullable String pathToFallbackBundle, + @Nullable String entrypointFunctionName, + @Nullable String pathToEntrypointFunction, + @NonNull AssetManager manager + ); + + public native String nativeGetObservatoryUri(); + + // Send an empty platform message to Dart. + public native void nativeDispatchEmptyPlatformMessage( + long nativePlatformViewAndroid, + String channel, + int responseId + ); + + // Send a data-carrying platform message to Dart. + public native void nativeDispatchPlatformMessage( + long nativePlatformViewAndroid, + String channel, + ByteBuffer message, + int position, + int responseId + ); + + // Send an empty response to a platform message received from Dart. + public native void nativeInvokePlatformMessageEmptyResponseCallback( + long nativePlatformViewAndroid, + int responseId + ); + + // Send a data-carrying response to a platform message received from Dart. + public native void nativeInvokePlatformMessageResponseCallback( + long nativePlatformViewAndroid, + int responseId, + ByteBuffer message, + int position + ); + //------ End from FlutterNativeView ---- + + // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters + //------ Start from Engine --- + @SuppressWarnings("unused") + private void onPreEngineRestart() { + for (EngineLifecycleListener listener : engineLifecycleListeners) { + listener.onPreEngineRestart(); + } + } + //------ End from Engine --- +} \ No newline at end of file diff --git a/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java b/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java new file mode 100644 index 0000000000000..52ae3b669a050 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.engine.dart; + +/** + * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. + * IF YOU USE IT, WE WILL BREAK YOU. + */ +public interface PlatformMessageHandler { + void handlePlatformMessage(final String channel, byte[] message, final int replyId); + + void handlePlatformMessageResponse(int replyId, byte[] reply); +} \ No newline at end of file diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java new file mode 100644 index 0000000000000..cece7c178c9eb --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -0,0 +1,275 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.engine.renderer; + +import android.annotation.TargetApi; +import android.graphics.Bitmap; +import android.graphics.SurfaceTexture; +import android.os.Build; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.view.Surface; + +import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicLong; + +import io.flutter.embedding.engine.FlutterJNI; +import io.flutter.view.TextureRegistry; + +/** + * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. + * IF YOU USE IT, WE WILL BREAK YOU. + * + * {@code FlutterRenderer} works in tandem with a provided {@link RenderSurface} to create an + * interactive Flutter UI. + * + * {@code FlutterRenderer} manages textures for rendering, and forwards messages to native Flutter + * code via JNI. The corresponding {@link RenderSurface} is used as a delegate to carry out + * certain actions on behalf of this {@code FlutterRenderer} within an Android view hierarchy. + * + * {@link FlutterView} is an implementation of a {@link RenderSurface}. + */ +@TargetApi(Build.VERSION_CODES.JELLY_BEAN) +public class FlutterRenderer implements TextureRegistry { + + private final FlutterJNI flutterJNI; + private final long nativeObjectReference; + private final AtomicLong nextTextureId = new AtomicLong(0L); + private RenderSurface renderSurface; + + public FlutterRenderer(@NonNull FlutterJNI flutterJNI, long nativeObjectReference) { + this.flutterJNI = flutterJNI; + this.nativeObjectReference = nativeObjectReference; + } + + public void attachToRenderSurface(@NonNull RenderSurface renderSurface) { + // TODO(mattcarroll): determine desired behavior when attaching to an already attached renderer + if (this.renderSurface != null) { + detachFromRenderSurface(); + } + + this.renderSurface = renderSurface; + this.flutterJNI.setRenderSurface(renderSurface); + } + + public void detachFromRenderSurface() { + // TODO(mattcarroll): determine desired behavior if we're asked to detach without first being attached + if (this.renderSurface != null) { + surfaceDestroyed(); + this.renderSurface = null; + this.flutterJNI.setRenderSurface(null); + } + } + + public void addOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { + flutterJNI.addOnFirstFrameRenderedListener(listener); + } + + public void removeOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { + flutterJNI.removeOnFirstFrameRenderedListener(listener); + } + + //------ START TextureRegistry IMPLEMENTATION ----- + // TODO(mattcarroll): this method probably shouldn't be public. It's part of an obscure relationship + // with PlatformViewsController. Re-evaluate that relationship. + // However, I did find that this method is called from the Camera plugin. + // TODO(mattcarroll): detachFromGLContext requires API 16. Create solution for earlier APIs. + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @Override + public SurfaceTextureEntry createSurfaceTexture() { + final SurfaceTexture surfaceTexture = new SurfaceTexture(0); + surfaceTexture.detachFromGLContext(); + final SurfaceTextureRegistryEntry entry = new SurfaceTextureRegistryEntry( + nextTextureId.getAndIncrement(), + surfaceTexture + ); + registerTexture(entry.id(), surfaceTexture); + return entry; + } + + final class SurfaceTextureRegistryEntry implements TextureRegistry.SurfaceTextureEntry { + private final long id; + private final SurfaceTexture surfaceTexture; + private boolean released; + + SurfaceTextureRegistryEntry(long id, SurfaceTexture surfaceTexture) { + this.id = id; + this.surfaceTexture = surfaceTexture; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // The callback relies on being executed on the UI thread (unsynchronised read of mNativeView + // and also the engine code check for platform thread in Shell::OnPlatformViewMarkTextureFrameAvailable), + // so we explicitly pass a Handler for the current thread. + this.surfaceTexture.setOnFrameAvailableListener(onFrameListener, new Handler()); + } else { + // Android documentation states that the listener can be called on an arbitrary thread. + // But in practice, versions of Android that predate the newer API will call the listener + // on the thread where the SurfaceTexture was constructed. + this.surfaceTexture.setOnFrameAvailableListener(onFrameListener); + } + } + + private SurfaceTexture.OnFrameAvailableListener onFrameListener = new SurfaceTexture.OnFrameAvailableListener() { + @Override + public void onFrameAvailable(SurfaceTexture texture) { + if (released) { + // Even though we make sure to unregister the callback before releasing, as of Android O + // SurfaceTexture has a data race when accessing the callback, so the callback may + // still be called by a stale reference after released==true and mNativeView==null. + return; + } + markTextureFrameAvailable(id); + } + }; + + @Override + public SurfaceTexture surfaceTexture() { + return surfaceTexture; + } + + @Override + public long id() { + return id; + } + + @Override + public void release() { + if (released) { + return; + } + unregisterTexture(id); + surfaceTexture.release(); + released = true; + } + } + //------ END TextureRegistry IMPLEMENTATION ---- + + // TODO(mattcarroll): describe the native behavior that this invokes + public void surfaceCreated(Surface surface) { + flutterJNI.nativeSurfaceCreated(nativeObjectReference, surface); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public void surfaceChanged(int width, int height) { + flutterJNI.nativeSurfaceChanged(nativeObjectReference, width, height); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public void surfaceDestroyed() { + flutterJNI.nativeSurfaceDestroyed(nativeObjectReference); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + 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) { + flutterJNI.nativeSetViewportMetrics( + nativeObjectReference, + devicePixelRatio, + physicalWidth, + physicalHeight, + physicalPaddingTop, + physicalPaddingRight, + physicalPaddingBottom, + physicalPaddingLeft, + physicalViewInsetTop, + physicalViewInsetRight, + physicalViewInsetBottom, + physicalViewInsetLeft + ); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public Bitmap getBitmap() { + return flutterJNI.nativeGetBitmap(nativeObjectReference); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public void dispatchPointerDataPacket(ByteBuffer buffer, int position) { + flutterJNI.nativeDispatchPointerDataPacket(nativeObjectReference, buffer, position); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + private void registerTexture(long textureId, SurfaceTexture surfaceTexture) { + flutterJNI.nativeRegisterTexture(nativeObjectReference, textureId, surfaceTexture); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + private void markTextureFrameAvailable(long textureId) { + flutterJNI.nativeMarkTextureFrameAvailable(nativeObjectReference, textureId); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + private void unregisterTexture(long textureId) { + flutterJNI.nativeUnregisterTexture(nativeObjectReference, textureId); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public boolean isSoftwareRenderingEnabled() { + return flutterJNI.nativeGetIsSoftwareRenderingEnabled(); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public void setAccessibilityFeatures(int flags) { + flutterJNI.nativeSetAccessibilityFeatures(nativeObjectReference, flags); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public void setSemanticsEnabled(boolean enabled) { + flutterJNI.nativeSetSemanticsEnabled(nativeObjectReference, enabled); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public void dispatchSemanticsAction(int id, + int action, + ByteBuffer args, + int argsPosition) { + flutterJNI.nativeDispatchSemanticsAction( + nativeObjectReference, + id, + action, + args, + argsPosition + ); + } + + /** + * Delegate used in conjunction with a {@link FlutterRenderer} to create an interactive Flutter + * UI. + * + * A {@code RenderSurface} is responsible for carrying out behaviors that are needed by a + * corresponding {@link FlutterRenderer}, e.g., {@link #updateSemantics(ByteBuffer, String[])}. + * + * A {@code RenderSurface} also receives callbacks for important events, e.g., + * {@link #onFirstFrameRendered()}. + */ + public interface RenderSurface { + // TODO(mattcarroll): describe what this callback is intended to do + void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings); + + // TODO(mattcarroll): describe what this callback is intended to do + void updateSemantics(ByteBuffer buffer, String[] strings); + + /** + * The {@link FlutterRenderer} corresponding to this {@code RenderSurface} has painted its + * first frame since being initialized. + * + * "Initialized" refers to Flutter engine initialization, not the first frame after attaching + * to the {@link FlutterRenderer}. Therefore, the first frame may have already rendered by + * the time a {@code RenderSurface} has called {@link #attachToRenderSurface(RenderSurface)} + * on a {@link FlutterRenderer}. In such a situation, {@code #onFirstFrameRendered()} will + * never be called. + */ + void onFirstFrameRendered(); + } +} \ No newline at end of file diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java b/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java new file mode 100644 index 0000000000000..a43b3481c402c --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.engine.renderer; + +/** + * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. + * IF YOU USE IT, WE WILL BREAK YOU. + */ +public interface OnFirstFrameRenderedListener { + /** + * A {@link FlutterRenderer} has painted its first frame since being initialized. + * + * This method will not be invoked if this listener is added after the first frame is rendered. + */ + void onFirstFrameRendered(); +} \ No newline at end of file diff --git a/shell/platform/android/io/flutter/view/FlutterNativeView.java b/shell/platform/android/io/flutter/view/FlutterNativeView.java index bb5e132d46ac6..7edbc42d1920d 100644 --- a/shell/platform/android/io/flutter/view/FlutterNativeView.java +++ b/shell/platform/android/io/flutter/view/FlutterNativeView.java @@ -8,12 +8,16 @@ import android.content.Context; import android.util.Log; import io.flutter.app.FlutterPluginRegistry; +import io.flutter.embedding.engine.FlutterJNI; +import io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener; +import io.flutter.embedding.engine.renderer.FlutterRenderer.RenderSurface; import io.flutter.plugin.common.*; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; import java.util.HashMap; import java.util.Map; -import android.content.res.AssetManager; + +import io.flutter.embedding.engine.dart.PlatformMessageHandler; public class FlutterNativeView implements BinaryMessenger { private static final String TAG = "FlutterNativeView"; @@ -25,6 +29,7 @@ public class FlutterNativeView implements BinaryMessenger { private final FlutterPluginRegistry mPluginRegistry; private long mNativePlatformView; private FlutterView mFlutterView; + private FlutterJNI mFlutterJNI; private final Context mContext; private boolean applicationIsRunning; @@ -35,6 +40,10 @@ public FlutterNativeView(Context context) { public FlutterNativeView(Context context, boolean isBackgroundView) { mContext = context; mPluginRegistry = new FlutterPluginRegistry(this, context); + mFlutterJNI = new FlutterJNI(); + mFlutterJNI.setRenderSurface(new RenderSurfaceImpl()); + mFlutterJNI.setPlatformMessageHandler(new PlatformMessageHandlerImpl()); + mFlutterJNI.addEngineLifecycleListener(new EngineLifecycleListenerImpl()); attach(this, isBackgroundView); assertAttached(); mMessageHandlers = new HashMap<>(); @@ -43,13 +52,13 @@ public FlutterNativeView(Context context, boolean isBackgroundView) { public void detach() { mPluginRegistry.detach(); mFlutterView = null; - nativeDetach(mNativePlatformView); + mFlutterJNI.nativeDetach(mNativePlatformView); } public void destroy() { mPluginRegistry.destroy(); mFlutterView = null; - nativeDestroy(mNativePlatformView); + mFlutterJNI.nativeDestroy(mNativePlatformView); mNativePlatformView = 0; applicationIsRunning = false; } @@ -101,7 +110,7 @@ private void runFromBundleInternal(String bundlePath, String entrypoint, if (applicationIsRunning) throw new AssertionError( "This Flutter engine instance is already running an application"); - nativeRunBundleAndSnapshotFromLibrary(mNativePlatformView, bundlePath, + mFlutterJNI.nativeRunBundleAndSnapshotFromLibrary(mNativePlatformView, bundlePath, defaultPath, entrypoint, libraryPath, mContext.getResources().getAssets()); applicationIsRunning = true; @@ -112,7 +121,7 @@ public boolean isApplicationRunning() { } public static String getObservatoryUri() { - return nativeGetObservatoryUri(); + return new FlutterJNI().nativeGetObservatoryUri(); } @Override @@ -133,9 +142,9 @@ public void send(String channel, ByteBuffer message, BinaryReply callback) { mPendingReplies.put(replyId, callback); } if (message == null) { - nativeDispatchEmptyPlatformMessage(mNativePlatformView, channel, replyId); + mFlutterJNI.nativeDispatchEmptyPlatformMessage(mNativePlatformView, channel, replyId); } else { - nativeDispatchPlatformMessage( + mFlutterJNI.nativeDispatchPlatformMessage( mNativePlatformView, channel, message, message.position(), replyId); } } @@ -149,116 +158,99 @@ public void setMessageHandler(String channel, BinaryMessageHandler handler) { } } + /*package*/ FlutterJNI getFlutterJNI() { + return mFlutterJNI; + } + private void attach(FlutterNativeView view, boolean isBackgroundView) { - mNativePlatformView = nativeAttach(view, isBackgroundView); + mNativePlatformView = mFlutterJNI.nativeAttach(mFlutterJNI, isBackgroundView); } - // Called by native to send us a platform message. - private void handlePlatformMessage(final String channel, byte[] message, final int replyId) { - assertAttached(); - BinaryMessageHandler handler = mMessageHandlers.get(channel); - if (handler != null) { - try { - final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message)); - handler.onMessage(buffer, new BinaryReply() { - private final AtomicBoolean done = new AtomicBoolean(false); - @Override - public void reply(ByteBuffer reply) { - if (!isAttached()) { - Log.d(TAG, - "handlePlatformMessage replying to a detached view, channel=" - + channel); - return; + private final class PlatformMessageHandlerImpl implements PlatformMessageHandler { + // Called by native to send us a platform message. + public void handlePlatformMessage(final String channel, byte[] message, final int replyId) { + assertAttached(); + BinaryMessageHandler handler = mMessageHandlers.get(channel); + if (handler != null) { + try { + final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message)); + handler.onMessage(buffer, new BinaryReply() { + private final AtomicBoolean done = new AtomicBoolean(false); + @Override + public void reply(ByteBuffer reply) { + if (!isAttached()) { + Log.d(TAG, "handlePlatformMessage replying ot a detached view, channel=" + channel); + return; + } + if (done.getAndSet(true)) { + throw new IllegalStateException("Reply already submitted"); + } + if (reply == null) { + mFlutterJNI.nativeInvokePlatformMessageEmptyResponseCallback(mNativePlatformView, replyId); + } else { + mFlutterJNI.nativeInvokePlatformMessageResponseCallback(mNativePlatformView, replyId, reply, reply.position()); + } } - if (done.getAndSet(true)) { - throw new IllegalStateException("Reply already submitted"); - } - if (reply == null) { - nativeInvokePlatformMessageEmptyResponseCallback( - mNativePlatformView, replyId); - } else { - nativeInvokePlatformMessageResponseCallback( - mNativePlatformView, replyId, reply, reply.position()); - } - } - }); - } catch (Exception ex) { - Log.e(TAG, "Uncaught exception in binary message listener", ex); - nativeInvokePlatformMessageEmptyResponseCallback(mNativePlatformView, replyId); + }); + } catch (Exception exception) { + Log.e(TAG, "Uncaught exception in binary message listener", exception); + mFlutterJNI.nativeInvokePlatformMessageEmptyResponseCallback(mNativePlatformView, replyId); + } + return; } - return; + mFlutterJNI.nativeInvokePlatformMessageEmptyResponseCallback(mNativePlatformView, replyId); } - nativeInvokePlatformMessageEmptyResponseCallback(mNativePlatformView, replyId); - } - // Called by native to respond to a platform message that we sent. - private void handlePlatformMessageResponse(int replyId, byte[] reply) { - BinaryReply callback = mPendingReplies.remove(replyId); - if (callback != null) { - try { - callback.reply(reply == null ? null : ByteBuffer.wrap(reply)); - } catch (Exception ex) { - Log.e(TAG, "Uncaught exception in binary message reply handler", ex); + // Called by native to respond to a platform message that we sent. + public void handlePlatformMessageResponse(int replyId, byte[] reply) { + BinaryReply callback = mPendingReplies.remove(replyId); + if (callback != null) { + try { + callback.reply(reply == null ? null : ByteBuffer.wrap(reply)); + } catch (Exception ex) { + Log.e(TAG, "Uncaught exception in binary message reply handler", ex); + } } } } - // Called by native to update the semantics/accessibility tree. - private void updateSemantics(ByteBuffer buffer, String[] strings) { - if (mFlutterView == null) - return; - mFlutterView.updateSemantics(buffer, strings); - } + private final class RenderSurfaceImpl implements RenderSurface { + // Called by native to update the semantics/accessibility tree. + public void updateSemantics(ByteBuffer buffer, String[] strings) { + if (mFlutterView == null) { + return; + } + mFlutterView.updateSemantics(buffer, strings); + } - // Called by native to update the custom accessibility actions. - private void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { - if (mFlutterView == null) - return; - mFlutterView.updateCustomAccessibilityActions(buffer, strings); - } + // Called by native to update the custom accessibility actions. + public void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { + if (mFlutterView == null) { + return; + } + mFlutterView.updateCustomAccessibilityActions(buffer, strings); + } - // Called by native to notify first Flutter frame rendered. - private void onFirstFrame() { - if (mFlutterView == null) - return; - mFlutterView.onFirstFrame(); + // Called by native to notify first Flutter frame rendered. + public void onFirstFrameRendered() { + if (mFlutterView == null) { + return; + } + mFlutterView.onFirstFrame(); + } } - // Called by native to notify when the engine is restarted (hot restart). - @SuppressWarnings("unused") - private void onPreEngineRestart() { - if (mFlutterView != null) { - mFlutterView.resetAccessibilityTree(); + private final class EngineLifecycleListenerImpl implements EngineLifecycleListener { + // Called by native to notify when the engine is restarted (cold reload). + @SuppressWarnings("unused") + public void onPreEngineRestart() { + if (mFlutterView != null) { + mFlutterView.resetAccessibilityTree(); + } + if (mPluginRegistry == null) { + return; + } + mPluginRegistry.onPreEngineRestart(); } - if (mPluginRegistry == null) - return; - mPluginRegistry.onPreEngineRestart(); } - - private static native long nativeAttach(FlutterNativeView view, boolean isBackgroundView); - private static native void nativeDestroy(long nativePlatformViewAndroid); - private static native void nativeDetach(long nativePlatformViewAndroid); - - private static native void nativeRunBundleAndSnapshotFromLibrary( - long nativePlatformViewAndroid, String bundlePath, - String defaultPath, String entrypoint, String libraryUrl, - AssetManager manager); - - private static native String nativeGetObservatoryUri(); - - // Send an empty platform message to Dart. - private static native void nativeDispatchEmptyPlatformMessage( - long nativePlatformViewAndroid, String channel, int responseId); - - // Send a data-carrying platform message to Dart. - private static native void nativeDispatchPlatformMessage(long nativePlatformViewAndroid, - String channel, ByteBuffer message, int position, int responseId); - - // Send an empty response to a platform message received from Dart. - private static native void nativeInvokePlatformMessageEmptyResponseCallback( - long nativePlatformViewAndroid, int responseId); - - // Send a data-carrying response to a platform message received from Dart. - private static native void nativeInvokePlatformMessageResponseCallback( - long nativePlatformViewAndroid, int responseId, ByteBuffer message, int position); } diff --git a/shell/platform/android/io/flutter/view/FlutterView.java b/shell/platform/android/io/flutter/view/FlutterView.java index 759b2744cdd6a..1a8e0e54a0774 100644 --- a/shell/platform/android/io/flutter/view/FlutterView.java +++ b/shell/platform/android/io/flutter/view/FlutterView.java @@ -108,38 +108,38 @@ public FlutterView(Context context, AttributeSet attrs) { public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) { super(context, attrs); - mIsSoftwareRenderingEnabled = nativeGetIsSoftwareRenderingEnabled(); - mAnimationScaleObserver = new AnimationScaleObserver(new Handler()); - mMetrics = new ViewportMetrics(); - mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density; - setFocusable(true); - setFocusableInTouchMode(true); - Activity activity = (Activity) getContext(); if (nativeView == null) { mNativeView = new FlutterNativeView(activity.getApplicationContext()); } else { mNativeView = nativeView; } + mIsSoftwareRenderingEnabled = mNativeView.getFlutterJNI().nativeGetIsSoftwareRenderingEnabled(); + mAnimationScaleObserver = new AnimationScaleObserver(new Handler()); + mMetrics = new ViewportMetrics(); + mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density; + setFocusable(true); + setFocusableInTouchMode(true); + mNativeView.attachViewAndActivity(this, activity); mSurfaceCallback = new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { assertAttached(); - nativeSurfaceCreated(mNativeView.get(), holder.getSurface()); + mNativeView.getFlutterJNI().nativeSurfaceCreated(mNativeView.get(), holder.getSurface()); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { assertAttached(); - nativeSurfaceChanged(mNativeView.get(), width, height); + mNativeView.getFlutterJNI().nativeSurfaceChanged(mNativeView.get(), width, height); } @Override public void surfaceDestroyed(SurfaceHolder holder) { assertAttached(); - nativeSurfaceDestroyed(mNativeView.get()); + mNativeView.getFlutterJNI().nativeSurfaceDestroyed(mNativeView.get()); } }; getHolder().addCallback(mSurfaceCallback); @@ -558,7 +558,7 @@ public boolean onTouchEvent(MotionEvent event) { } assert packet.position() % (kPointerDataFieldCount * kBytePerField) == 0; - nativeDispatchPointerDataPacket(mNativeView.get(), packet, packet.position()); + mNativeView.getFlutterJNI().nativeDispatchPointerDataPacket(mNativeView.get(), packet, packet.position()); return true; } @@ -760,45 +760,13 @@ public void runFromBundle(String bundlePath, String defaultPath, String entrypoi */ public Bitmap getBitmap() { assertAttached(); - return nativeGetBitmap(mNativeView.get()); + return mNativeView.getFlutterJNI().nativeGetBitmap(mNativeView.get()); } - private static native void nativeSurfaceCreated(long nativePlatformViewAndroid, Surface surface); - - private static native void nativeSurfaceChanged(long nativePlatformViewAndroid, int width, int height); - - private static native void nativeSurfaceDestroyed(long nativePlatformViewAndroid); - - private static native void nativeSetViewportMetrics(long nativePlatformViewAndroid, float devicePixelRatio, - int physicalWidth, int physicalHeight, int physicalPaddingTop, int physicalPaddingRight, - int physicalPaddingBottom, int physicalPaddingLeft, int physicalViewInsetTop, int physicalViewInsetRight, - int physicalViewInsetBottom, int physicalViewInsetLeft); - - private static native Bitmap nativeGetBitmap(long nativePlatformViewAndroid); - - private static native void nativeDispatchPointerDataPacket(long nativePlatformViewAndroid, ByteBuffer buffer, - int position); - - private static native void nativeDispatchSemanticsAction(long nativePlatformViewAndroid, int id, int action, - ByteBuffer args, int argsPosition); - - private static native void nativeSetSemanticsEnabled(long nativePlatformViewAndroid, boolean enabled); - - private static native void nativeSetAccessibilityFeatures(long nativePlatformViewAndroid, int flags); - - private static native boolean nativeGetIsSoftwareRenderingEnabled(); - - private static native void nativeRegisterTexture(long nativePlatformViewAndroid, long textureId, - SurfaceTexture surfaceTexture); - - private static native void nativeMarkTextureFrameAvailable(long nativePlatformViewAndroid, long textureId); - - private static native void nativeUnregisterTexture(long nativePlatformViewAndroid, long textureId); - private void updateViewportMetrics() { if (!isAttached()) return; - nativeSetViewportMetrics(mNativeView.get(), mMetrics.devicePixelRatio, mMetrics.physicalWidth, + mNativeView.getFlutterJNI().nativeSetViewportMetrics(mNativeView.get(), mMetrics.devicePixelRatio, mMetrics.physicalWidth, mMetrics.physicalHeight, mMetrics.physicalPaddingTop, mMetrics.physicalPaddingRight, mMetrics.physicalPaddingBottom, mMetrics.physicalPaddingLeft, mMetrics.physicalViewInsetTop, mMetrics.physicalViewInsetRight, mMetrics.physicalViewInsetBottom, mMetrics.physicalViewInsetLeft); @@ -860,7 +828,7 @@ protected void dispatchSemanticsAction(int id, AccessibilityBridge.Action action encodedArgs = StandardMessageCodec.INSTANCE.encodeMessage(args); position = encodedArgs.position(); } - nativeDispatchSemanticsAction(mNativeView.get(), id, action.value, encodedArgs, position); + mNativeView.getFlutterJNI().nativeDispatchSemanticsAction(mNativeView.get(), id, action.value, encodedArgs, position); } @Override @@ -903,7 +871,7 @@ private void updateAccessibilityFeatures() { mAccessibilityFeatureFlags &= ~AccessibilityFeature.DISABLE_ANIMATIONS.value; } } - nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + mNativeView.getFlutterJNI().nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); } @Override @@ -933,7 +901,7 @@ public void onAccessibilityStateChanged(boolean enabled) { if (mAccessibilityNodeProvider != null) { mAccessibilityNodeProvider.setAccessibilityEnabled(false); } - nativeSetSemanticsEnabled(mNativeView.get(), false); + mNativeView.getFlutterJNI().nativeSetSemanticsEnabled(mNativeView.get(), false); } resetWillNotDraw(); } @@ -972,7 +940,7 @@ public void onChange(boolean selfChange, Uri uri) { } else { mAccessibilityFeatureFlags &= ~AccessibilityFeature.DISABLE_ANIMATIONS.value; } - nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + mNativeView.getFlutterJNI().nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); } } @@ -983,14 +951,14 @@ public void onTouchExplorationStateChanged(boolean enabled) { mTouchExplorationEnabled = true; ensureAccessibilityEnabled(); mAccessibilityFeatureFlags |= AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; - nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + mNativeView.getFlutterJNI().nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); } else { mTouchExplorationEnabled = false; if (mAccessibilityNodeProvider != null) { mAccessibilityNodeProvider.handleTouchExplorationExit(); } mAccessibilityFeatureFlags &= ~AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; - nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + mNativeView.getFlutterJNI().nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); } resetWillNotDraw(); } @@ -1015,7 +983,7 @@ void ensureAccessibilityEnabled() { if (mAccessibilityNodeProvider == null) { mAccessibilityNodeProvider = new AccessibilityBridge(this); } - nativeSetSemanticsEnabled(mNativeView.get(), true); + mNativeView.getFlutterJNI().nativeSetSemanticsEnabled(mNativeView.get(), true); mAccessibilityNodeProvider.setAccessibilityEnabled(true); } @@ -1073,7 +1041,7 @@ public TextureRegistry.SurfaceTextureEntry createSurfaceTexture() { surfaceTexture.detachFromGLContext(); final SurfaceTextureRegistryEntry entry = new SurfaceTextureRegistryEntry(nextTextureId.getAndIncrement(), surfaceTexture); - nativeRegisterTexture(mNativeView.get(), entry.id(), surfaceTexture); + mNativeView.getFlutterJNI().nativeRegisterTexture(mNativeView.get(), entry.id(), surfaceTexture); return entry; } @@ -1108,7 +1076,7 @@ public void onFrameAvailable(SurfaceTexture texture) { // still be called by a stale reference after released==true and mNativeView==null. return; } - nativeMarkTextureFrameAvailable(mNativeView.get(), SurfaceTextureRegistryEntry.this.id); + mNativeView.getFlutterJNI().nativeMarkTextureFrameAvailable(mNativeView.get(), SurfaceTextureRegistryEntry.this.id); } }; @@ -1128,7 +1096,7 @@ public void release() { return; } released = true; - nativeUnregisterTexture(mNativeView.get(), id); + mNativeView.getFlutterJNI().nativeUnregisterTexture(mNativeView.get(), id); // Otherwise onFrameAvailableListener might be called after mNativeView==null // (https://github.com/flutter/flutter/issues/20951). See also the check in onFrameAvailable. surfaceTexture.setOnFrameAvailableListener(null); diff --git a/shell/platform/android/platform_view_android_jni.cc b/shell/platform/android/platform_view_android_jni.cc index be42414b6c9be..94437087e3201 100644 --- a/shell/platform/android/platform_view_android_jni.cc +++ b/shell/platform/android/platform_view_android_jni.cc @@ -46,9 +46,11 @@ bool CheckException(JNIEnv* env) { static fml::jni::ScopedJavaGlobalRef* g_flutter_callback_info_class = nullptr; -static fml::jni::ScopedJavaGlobalRef* g_flutter_view_class = nullptr; -static fml::jni::ScopedJavaGlobalRef* g_flutter_native_view_class = - nullptr; + +// FlutterJNI.java, used in 2nd iteration of embedding to centralize all JNI +// calls. +static fml::jni::ScopedJavaGlobalRef* g_flutter_jni_class = nullptr; + static fml::jni::ScopedJavaGlobalRef* g_surface_texture_class = nullptr; // Called By Native @@ -146,11 +148,11 @@ void SurfaceTextureDetachFromGLContext(JNIEnv* env, jobject obj) { // Called By Java -static jlong Attach(JNIEnv* env, - jclass clazz, - jobject flutterView, - jboolean is_background_view) { - fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterView); +static jlong AttachJNI(JNIEnv* env, + jclass clazz, + jobject flutterJNI, + jboolean is_background_view) { + fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterJNI); auto shell_holder = std::make_unique( FlutterMain::Get().GetSettings(), java_object, is_background_view); if (shell_holder->IsValid()) { @@ -160,11 +162,12 @@ static jlong Attach(JNIEnv* env, } } -static void Detach(JNIEnv* env, jobject jcaller, jlong shell_holder) { +// TODO(mattcarroll): delete this method here and in FlutterJNI.java +static void DetachJNI(JNIEnv* env, jobject jcaller, jlong shell_holder) { // Nothing to do. } -static void Destroy(JNIEnv* env, jobject jcaller, jlong shell_holder) { +static void DestroyJNI(JNIEnv* env, jobject jcaller, jlong shell_holder) { delete ANDROID_SHELL_HOLDER; } @@ -542,52 +545,23 @@ static void InvokePlatformMessageEmptyResponseCallback(JNIEnv* env, ); } -bool PlatformViewAndroid::Register(JNIEnv* env) { - if (env == nullptr) { - return false; - } - - g_flutter_callback_info_class = new fml::jni::ScopedJavaGlobalRef( - env, env->FindClass("io/flutter/view/FlutterCallbackInformation")); - if (g_flutter_callback_info_class->is_null()) { - return false; - } - - g_flutter_callback_info_constructor = env->GetMethodID( - g_flutter_callback_info_class->obj(), "", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - if (g_flutter_callback_info_constructor == nullptr) { - return false; - } - - g_flutter_view_class = new fml::jni::ScopedJavaGlobalRef( - env, env->FindClass("io/flutter/view/FlutterView")); - if (g_flutter_view_class->is_null()) { - return false; - } - - g_flutter_native_view_class = new fml::jni::ScopedJavaGlobalRef( - env, env->FindClass("io/flutter/view/FlutterNativeView")); - if (g_flutter_native_view_class->is_null()) { - return false; - } - - g_surface_texture_class = new fml::jni::ScopedJavaGlobalRef( - env, env->FindClass("android/graphics/SurfaceTexture")); - if (g_surface_texture_class->is_null()) { - return false; - } - - static const JNINativeMethod native_view_methods[] = { +bool RegisterApi(JNIEnv* env) { + static const JNINativeMethod flutter_jni_methods[] = { + // Start of methods from FlutterNativeView { .name = "nativeAttach", - .signature = "(Lio/flutter/view/FlutterNativeView;Z)J", - .fnPtr = reinterpret_cast(&shell::Attach), + .signature = "(Lio/flutter/embedding/engine/FlutterJNI;Z)J", + .fnPtr = reinterpret_cast(&shell::AttachJNI), + }, + { + .name = "nativeDetach", + .signature = "(J)V", + .fnPtr = reinterpret_cast(&shell::DetachJNI), }, { .name = "nativeDestroy", .signature = "(J)V", - .fnPtr = reinterpret_cast(&shell::Destroy), + .fnPtr = reinterpret_cast(&shell::DestroyJNI), }, { .name = "nativeRunBundleAndSnapshotFromLibrary", @@ -597,11 +571,6 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { .fnPtr = reinterpret_cast(&shell::RunBundleAndSnapshotFromLibrary), }, - { - .name = "nativeDetach", - .signature = "(J)V", - .fnPtr = reinterpret_cast(&shell::Detach), - }, { .name = "nativeGetObservatoryUri", .signature = "()Ljava/lang/String;", @@ -630,9 +599,13 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { .fnPtr = reinterpret_cast( &shell::InvokePlatformMessageEmptyResponseCallback), }, - }; - static const JNINativeMethod view_methods[] = { + // Start of methods from FlutterView + { + .name = "nativeGetBitmap", + .signature = "(J)Landroid/graphics/Bitmap;", + .fnPtr = reinterpret_cast(&shell::GetBitmap), + }, { .name = "nativeSurfaceCreated", .signature = "(JLandroid/view/Surface;)V", @@ -653,11 +626,6 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { .signature = "(JFIIIIIIIIII)V", .fnPtr = reinterpret_cast(&shell::SetViewportMetrics), }, - { - .name = "nativeGetBitmap", - .signature = "(J)Landroid/graphics/Bitmap;", - .fnPtr = reinterpret_cast(&shell::GetBitmap), - }, { .name = "nativeDispatchPointerDataPacket", .signature = "(JLjava/nio/ByteBuffer;I)V", @@ -700,74 +668,114 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { }, }; - static const JNINativeMethod callback_info_methods[] = { - { - .name = "nativeLookupCallbackInformation", - .signature = "(J)Lio/flutter/view/FlutterCallbackInformation;", - .fnPtr = reinterpret_cast(&shell::LookupCallbackInformation), - }, - }; - - if (env->RegisterNatives(g_flutter_native_view_class->obj(), - native_view_methods, - arraysize(native_view_methods)) != 0) { - return false; - } - - if (env->RegisterNatives(g_flutter_view_class->obj(), view_methods, - arraysize(view_methods)) != 0) { - return false; - } - - if (env->RegisterNatives(g_flutter_callback_info_class->obj(), - callback_info_methods, - arraysize(callback_info_methods)) != 0) { + if (env->RegisterNatives(g_flutter_jni_class->obj(), flutter_jni_methods, + arraysize(flutter_jni_methods)) != 0) { + FML_LOG(ERROR) << "Failed to RegisterNatives with FlutterJNI"; return false; } g_handle_platform_message_method = - env->GetMethodID(g_flutter_native_view_class->obj(), - "handlePlatformMessage", "(Ljava/lang/String;[BI)V"); + env->GetMethodID(g_flutter_jni_class->obj(), "handlePlatformMessage", + "(Ljava/lang/String;[BI)V"); if (g_handle_platform_message_method == nullptr) { + FML_LOG(ERROR) << "Could not locate handlePlatformMessage method"; return false; } - g_handle_platform_message_response_method = - env->GetMethodID(g_flutter_native_view_class->obj(), - "handlePlatformMessageResponse", "(I[B)V"); + g_handle_platform_message_response_method = env->GetMethodID( + g_flutter_jni_class->obj(), "handlePlatformMessageResponse", "(I[B)V"); if (g_handle_platform_message_response_method == nullptr) { + FML_LOG(ERROR) << "Could not locate handlePlatformMessageResponse method"; return false; } g_update_semantics_method = - env->GetMethodID(g_flutter_native_view_class->obj(), "updateSemantics", + env->GetMethodID(g_flutter_jni_class->obj(), "updateSemantics", "(Ljava/nio/ByteBuffer;[Ljava/lang/String;)V"); if (g_update_semantics_method == nullptr) { + FML_LOG(ERROR) << "Could not locate updateSemantics method"; return false; } g_update_custom_accessibility_actions_method = env->GetMethodID( - g_flutter_native_view_class->obj(), "updateCustomAccessibilityActions", + g_flutter_jni_class->obj(), "updateCustomAccessibilityActions", "(Ljava/nio/ByteBuffer;[Ljava/lang/String;)V"); if (g_update_custom_accessibility_actions_method == nullptr) { + FML_LOG(ERROR) + << "Could not locate updateCustomAccessibilityActions method"; return false; } - g_on_first_frame_method = env->GetMethodID(g_flutter_native_view_class->obj(), - "onFirstFrame", "()V"); + g_on_first_frame_method = + env->GetMethodID(g_flutter_jni_class->obj(), "onFirstFrame", "()V"); if (g_on_first_frame_method == nullptr) { + FML_LOG(ERROR) << "Could not locate onFirstFrame method"; return false; } - g_on_engine_restart_method = env->GetMethodID( - g_flutter_native_view_class->obj(), "onPreEngineRestart", "()V"); + g_on_engine_restart_method = + env->GetMethodID(g_flutter_jni_class->obj(), "onPreEngineRestart", "()V"); if (g_on_engine_restart_method == nullptr) { + FML_LOG(ERROR) << "Could not locate onEngineRestart method"; + return false; + } + + return true; +} + +bool PlatformViewAndroid::Register(JNIEnv* env) { + if (env == nullptr) { + FML_LOG(ERROR) << "No JNIEnv provided"; + return false; + } + + g_flutter_callback_info_class = new fml::jni::ScopedJavaGlobalRef( + env, env->FindClass("io/flutter/view/FlutterCallbackInformation")); + if (g_flutter_callback_info_class->is_null()) { + FML_LOG(ERROR) << "Could not locate FlutterCallbackInformation class"; + return false; + } + + g_flutter_callback_info_constructor = env->GetMethodID( + g_flutter_callback_info_class->obj(), "", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + if (g_flutter_callback_info_constructor == nullptr) { + FML_LOG(ERROR) << "Could not locate FlutterCallbackInformation constructor"; + return false; + } + + g_flutter_jni_class = new fml::jni::ScopedJavaGlobalRef( + env, env->FindClass("io/flutter/embedding/engine/FlutterJNI")); + if (g_flutter_jni_class->is_null()) { + FML_LOG(ERROR) << "Failed to find FlutterJNI Class."; + return false; + } + + g_surface_texture_class = new fml::jni::ScopedJavaGlobalRef( + env, env->FindClass("android/graphics/SurfaceTexture")); + if (g_surface_texture_class->is_null()) { + FML_LOG(ERROR) << "Could not locate SurfaceTexture class"; + return false; + } + + static const JNINativeMethod callback_info_methods[] = { + { + .name = "nativeLookupCallbackInformation", + .signature = "(J)Lio/flutter/view/FlutterCallbackInformation;", + .fnPtr = reinterpret_cast(&shell::LookupCallbackInformation), + }, + }; + + if (env->RegisterNatives(g_flutter_callback_info_class->obj(), + callback_info_methods, + arraysize(callback_info_methods)) != 0) { + FML_LOG(ERROR) << "Failed to RegisterNatives with FlutterCallbackInfo"; return false; } @@ -775,6 +783,7 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { g_surface_texture_class->obj(), "attachToGLContext", "(I)V"); if (g_attach_to_gl_context_method == nullptr) { + FML_LOG(ERROR) << "Could not locate attachToGlContext method"; return false; } @@ -782,6 +791,7 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { env->GetMethodID(g_surface_texture_class->obj(), "updateTexImage", "()V"); if (g_update_tex_image_method == nullptr) { + FML_LOG(ERROR) << "Could not locate updateTexImage method"; return false; } @@ -789,6 +799,7 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { g_surface_texture_class->obj(), "getTransformMatrix", "([F)V"); if (g_get_transform_matrix_method == nullptr) { + FML_LOG(ERROR) << "Could not locate getTransformMatrix method"; return false; } @@ -796,10 +807,11 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { g_surface_texture_class->obj(), "detachFromGLContext", "()V"); if (g_detach_from_gl_context_method == nullptr) { + FML_LOG(ERROR) << "Could not locate detachFromGlContext method"; return false; } - return true; + return RegisterApi(env); } } // namespace shell From 2e2c657e59a985233f88b0b128d59fe0a7d8fde4 Mon Sep 17 00:00:00 2001 From: matthew-carroll Date: Mon, 10 Dec 2018 16:44:01 -0800 Subject: [PATCH 2/9] Added JavaDoc to FlutterEngine and FlutterJNI, refactored FlutterJNI to track its own native ID, removed comment from platform_view_android_jni.cc --- .../embedding/engine/FlutterEngine.java | 18 +- .../flutter/embedding/engine/FlutterJNI.java | 369 ++++++++++++++---- .../engine/renderer/FlutterRenderer.java | 32 +- .../io/flutter/view/FlutterNativeView.java | 43 +- .../android/io/flutter/view/FlutterView.java | 32 +- .../android/platform_view_android_jni.cc | 2 - 6 files changed, 363 insertions(+), 133 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index a97ef757a29b6..5810ea1db57bb 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -5,8 +5,24 @@ package io.flutter.embedding.engine; /** + * A single Flutter execution environment. + * * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. * IF YOU USE IT, WE WILL BREAK YOU. + * + * A {@code FlutterEngine} can execute in the background, or it can be rendered to the screen by + * using the accompanying {@link FlutterRenderer}. Rendering can be started and stopped, thus + * allowing a {@code FlutterEngine} to move from UI interaction to data-only processing and then + * back to UI interaction. + * + * To start running Flutter within this {@code FlutterEngine}, get a reference to this engine's + * {@link DartExecutor} and then use {@link DartExecutor#runFromBundle(FlutterRunArguments)}. + * The {@link DartExecutor#runFromBundle(FlutterRunArguments)} method must not be invoked twice on the same + * {@code FlutterEngine}. + * + * To start rendering Flutter content to the screen, use {@link #getRenderer()} to obtain a + * {@link FlutterRenderer} and then attach a {@link FlutterRenderer.RenderSurface}. Consider using + * a {@link io.flutter.embedding.android.FlutterView} as a {@link FlutterRenderer.RenderSurface}. */ public class FlutterEngine { // TODO(mattcarroll): bring in FlutterEngine implementation in future PR @@ -20,4 +36,4 @@ public interface EngineLifecycleListener { */ void onPreEngineRestart(); } -} +} \ No newline at end of file diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 3509f150873bb..23718c67a1040 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -9,7 +9,6 @@ import android.graphics.SurfaceTexture; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.util.Log; import android.view.Surface; import java.nio.ByteBuffer; @@ -22,12 +21,76 @@ import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener; /** + * Interface between Flutter embedding's Java code and Flutter engine's C/C++ code. + * * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. * IF YOU USE IT, WE WILL BREAK YOU. + * + * 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 {@code 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 {@code FlutterJNI}. + * + * Despite the fact that individual JNI calls are inherently static, there is state that exists + * within {@code FlutterJNI}. Most calls within {@code FlutterJNI} correspond to a specific + * "platform view", of which there may be many. Therefore, each {@code FlutterJNI} instance holds + * onto a "native platform view ID" after {@link #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 {@code 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 {@code FlutterJNI} and + * then attach it to the native side: + * + * {@code + * // 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 + * {@link FlutterRenderer.RenderSurface} with {@link #setRenderSurface(FlutterRenderer.RenderSurface)} + * + * To receive callbacks for certain events that occur on the native side, register listeners: + * + *
    + *
  1. {@link #addEngineLifecycleListener(EngineLifecycleListener)}
  2. + *
  3. {@link #addOnFirstFrameRenderedListener(OnFirstFrameRenderedListener)}
  4. + *
+ * + * To facilitate platform messages between Java and Dart running in Flutter, register a handler: + * + * {@link #setPlatformMessageHandler(PlatformMessageHandler)} + * + * To invoke a native method that is not associated with a platform view, invoke it statically: + * + * {@code + * String uri = FlutterJNI.nativeGetObservatoryUri(); + * } */ public class FlutterJNI { private static final String TAG = "FlutterJNI"; + public static native boolean nativeGetIsSoftwareRenderingEnabled(); + + public static native String nativeGetObservatoryUri(); + + private Long nativePlatformViewId; private FlutterRenderer.RenderSurface renderSurface; private PlatformMessageHandler platformMessageHandler; private final Set engineLifecycleListeners = new CopyOnWriteArraySet<>(); @@ -37,32 +100,10 @@ public void setRenderSurface(@Nullable FlutterRenderer.RenderSurface renderSurfa this.renderSurface = renderSurface; } - public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler) { - this.platformMessageHandler = platformMessageHandler; - } - - public void addEngineLifecycleListener(@NonNull EngineLifecycleListener engineLifecycleListener) { - engineLifecycleListeners.add(engineLifecycleListener); - } - - public void removeEngineLifecycleListener(@NonNull EngineLifecycleListener engineLifecycleListener) { - engineLifecycleListeners.remove(engineLifecycleListener); - } - - public void addOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { - firstFrameListeners.add(listener); - } - - public void removeOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { - firstFrameListeners.remove(listener); - } - - //------ START RENDER SURFACE CALLBACKS ----- // TODO(mattcarroll): define "update" // Called by native to update the semantics/accessibility tree. @SuppressWarnings("unused") - public void updateSemantics(ByteBuffer buffer, String[] strings) { - Log.d(TAG, "updateSemantics()"); + private void updateSemantics(ByteBuffer buffer, String[] strings) { if (renderSurface != null) { renderSurface.updateSemantics(buffer, strings); } @@ -71,8 +112,7 @@ public void updateSemantics(ByteBuffer buffer, String[] strings) { // TODO(mattcarroll): define "update" // Called by native to update the custom accessibility actions. @SuppressWarnings("unused") - public void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { - Log.d(TAG, "updateCustomAccessibilityActions()"); + private void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { if (renderSurface != null) { renderSurface.updateCustomAccessibilityActions(buffer, strings); } @@ -81,7 +121,6 @@ public void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings // Called by native to notify first Flutter frame rendered. @SuppressWarnings("unused") private void onFirstFrame() { - Log.d(TAG, "onFirstFrame()"); if (renderSurface != null) { renderSurface.onFirstFrameRendered(); } @@ -90,9 +129,12 @@ private void onFirstFrame() { listener.onFirstFrameRendered(); } } - //------ END RENDER SURFACE CALLBACKS ------ - //------ START PLATFORM MESSAGE CALLBACKS ---- + public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler) { + this.platformMessageHandler = platformMessageHandler; + } + + // Called by native. @SuppressWarnings("unused") private void handlePlatformMessage(final String channel, byte[] message, final int replyId) { if (platformMessageHandler != null) { @@ -107,64 +149,197 @@ private void handlePlatformMessageResponse(int replyId, byte[] reply) { platformMessageHandler.handlePlatformMessageResponse(replyId, reply); } } - //------ END PLATFORM MESSAGE CALLBACKS ---- + + public void addEngineLifecycleListener(@NonNull EngineLifecycleListener engineLifecycleListener) { + engineLifecycleListeners.add(engineLifecycleListener); + } + + public void removeEngineLifecycleListener(@NonNull EngineLifecycleListener engineLifecycleListener) { + engineLifecycleListeners.remove(engineLifecycleListener); + } + + public void addOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { + firstFrameListeners.add(listener); + } + + public void removeOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { + firstFrameListeners.remove(listener); + } // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters //----- Start from FlutterView ----- - public native void nativeSurfaceCreated(long nativePlatformViewAndroid, Surface surface); + public void onSurfaceCreated(@NonNull Surface surface) { + ensureAttachedToNative(); + nativeSurfaceCreated(nativePlatformViewId, surface); + } + + private native void nativeSurfaceCreated(long nativePlatformViewId, Surface surface); - public native void nativeSurfaceChanged(long nativePlatformViewAndroid, - int width, - int height); + public void onSurfaceChanged(int width, int height) { + ensureAttachedToNative(); + nativeSurfaceChanged(nativePlatformViewId, width, height); + } + + private native void nativeSurfaceChanged(long nativePlatformViewId, int width, int height); + + public void onSurfaceDestroyed() { + ensureAttachedToNative(); + nativeSurfaceDestroyed(nativePlatformViewId); + } + + private native void nativeSurfaceDestroyed(long nativePlatformViewId); + + 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) { + ensureAttachedToNative(); + nativeSetViewportMetrics( + nativePlatformViewId, + devicePixelRatio, + physicalWidth, + physicalHeight, + physicalPaddingTop, + physicalPaddingRight, + physicalPaddingBottom, + physicalPaddingLeft, + physicalViewInsetTop, + physicalViewInsetRight, + physicalViewInsetBottom, + physicalViewInsetLeft + ); + } + + private native void nativeSetViewportMetrics(long nativePlatformViewId, + float devicePixelRatio, + int physicalWidth, + int physicalHeight, + int physicalPaddingTop, + int physicalPaddingRight, + int physicalPaddingBottom, + int physicalPaddingLeft, + int physicalViewInsetTop, + int physicalViewInsetRight, + int physicalViewInsetBottom, + int physicalViewInsetLeft); + + public Bitmap getBitmap() { + ensureAttachedToNative(); + return nativeGetBitmap(nativePlatformViewId); + } + + private native Bitmap nativeGetBitmap(long nativePlatformViewId); + + public void dispatchPointerDataPacket(ByteBuffer buffer, int position) { + ensureAttachedToNative(); + nativeDispatchPointerDataPacket(nativePlatformViewId, buffer, position); + } - public native void nativeSurfaceDestroyed(long nativePlatformViewAndroid); + private native void nativeDispatchPointerDataPacket(long nativePlatformViewId, + ByteBuffer buffer, + int position); - public native void nativeSetViewportMetrics(long nativePlatformViewAndroid, - float devicePixelRatio, - int physicalWidth, - int physicalHeight, - int physicalPaddingTop, - int physicalPaddingRight, - int physicalPaddingBottom, - int physicalPaddingLeft, - int physicalViewInsetTop, - int physicalViewInsetRight, - int physicalViewInsetBottom, - int physicalViewInsetLeft); + public void dispatchSemanticsAction(int id, int action, ByteBuffer args, int argsPosition) { + ensureAttachedToNative(); + nativeDispatchSemanticsAction(nativePlatformViewId, id, action, args, argsPosition); + } + + private native void nativeDispatchSemanticsAction(long nativePlatformViewId, + int id, + int action, + ByteBuffer args, + int argsPosition); - public native Bitmap nativeGetBitmap(long nativePlatformViewAndroid); + public void setSemanticsEnabled(boolean enabled) { + ensureAttachedToNative(); + nativeSetSemanticsEnabled(nativePlatformViewId, enabled); + } - public native void nativeDispatchPointerDataPacket(long nativePlatformViewAndroid, - ByteBuffer buffer, - int position); + private native void nativeSetSemanticsEnabled(long nativePlatformViewId, boolean enabled); - public native void nativeDispatchSemanticsAction(long nativePlatformViewAndroid, - int id, - int action, - ByteBuffer args, - int argsPosition); + public void setAccessibilityFeatures(int flags) { + ensureAttachedToNative(); + nativeSetAccessibilityFeatures(nativePlatformViewId, flags); + } - public native void nativeSetSemanticsEnabled(long nativePlatformViewAndroid, boolean enabled); + private native void nativeSetAccessibilityFeatures(long nativePlatformViewId, int flags); - public native void nativeSetAccessibilityFeatures(long nativePlatformViewAndroid, int flags); + public void registerTexture(long textureId, SurfaceTexture surfaceTexture) { + ensureAttachedToNative(); + nativeRegisterTexture(nativePlatformViewId, textureId, surfaceTexture); + } - public native boolean nativeGetIsSoftwareRenderingEnabled(); + private native void nativeRegisterTexture(long nativePlatformViewId, long textureId, SurfaceTexture surfaceTexture); - public native void nativeRegisterTexture(long nativePlatformViewAndroid, long textureId, SurfaceTexture surfaceTexture); + public void markTextureFrameAvailable(long textureId) { + ensureAttachedToNative(); + nativeMarkTextureFrameAvailable(nativePlatformViewId, textureId); + } - public native void nativeMarkTextureFrameAvailable(long nativePlatformViewAndroid, long textureId); + private native void nativeMarkTextureFrameAvailable(long nativePlatformViewId, long textureId); - public native void nativeUnregisterTexture(long nativePlatformViewAndroid, long textureId); + public void unregisterTexture(long textureId) { + ensureAttachedToNative(); + nativeUnregisterTexture(nativePlatformViewId, textureId); + } + + private native void nativeUnregisterTexture(long nativePlatformViewId, long textureId); //------- End from FlutterView ----- // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters //------ Start from FlutterNativeView ---- - public native long nativeAttach(FlutterJNI flutterJNI, boolean isBackgroundView); - public native void nativeDestroy(long nativePlatformViewAndroid); - public native void nativeDetach(long nativePlatformViewAndroid); + public boolean isAttached() { + return nativePlatformViewId != null; + } + + public void attachToNative(boolean isBackgroundView) { + ensureNotAttachedToNative(); + nativePlatformViewId = nativeAttach(this, isBackgroundView); + } - public native void nativeRunBundleAndSnapshotFromLibrary( - long nativePlatformViewAndroid, + private native long nativeAttach(FlutterJNI flutterJNI, boolean isBackgroundView); + + public void detachFromNativeButKeepNativeResources() { + ensureAttachedToNative(); + nativeDetach(nativePlatformViewId); + nativePlatformViewId = null; + } + + private native void nativeDetach(long nativePlatformViewId); + + public void detachFromNativeAndReleaseResources() { + ensureAttachedToNative(); + nativeDestroy(nativePlatformViewId); + nativePlatformViewId = null; + } + + private native void nativeDestroy(long nativePlatformViewId); + + public void runBundleAndSnapshotFromLibrary(@NonNull String pathToBundleWithEntrypoint, + @Nullable String pathToFallbackBundle, + @Nullable String entrypointFunctionName, + @Nullable String pathToEntrypointFunction, + @NonNull AssetManager assetManager) { + ensureAttachedToNative(); + nativeRunBundleAndSnapshotFromLibrary( + nativePlatformViewId, + pathToBundleWithEntrypoint, + pathToFallbackBundle, + entrypointFunctionName, + pathToEntrypointFunction, + assetManager + ); + } + + private native void nativeRunBundleAndSnapshotFromLibrary( + long nativePlatformViewId, @NonNull String pathToBundleWithEntrypoint, @Nullable String pathToFallbackBundle, @Nullable String entrypointFunctionName, @@ -172,33 +347,62 @@ public native void nativeRunBundleAndSnapshotFromLibrary( @NonNull AssetManager manager ); - public native String nativeGetObservatoryUri(); + public void dispatchEmptyPlatformMessage(String channel, int responseId) { + ensureAttachedToNative(); + nativeDispatchEmptyPlatformMessage(nativePlatformViewId, channel, responseId); + } // Send an empty platform message to Dart. - public native void nativeDispatchEmptyPlatformMessage( - long nativePlatformViewAndroid, + private native void nativeDispatchEmptyPlatformMessage( + long nativePlatformViewId, String channel, int responseId ); + public void dispatchPlatformMessage(String channel, ByteBuffer message, int position, int responseId) { + ensureAttachedToNative(); + nativeDispatchPlatformMessage( + nativePlatformViewId, + channel, + message, + position, + responseId + ); + } + // Send a data-carrying platform message to Dart. - public native void nativeDispatchPlatformMessage( - long nativePlatformViewAndroid, + private native void nativeDispatchPlatformMessage( + long nativePlatformViewId, String channel, ByteBuffer message, int position, int responseId ); + public void invokePlatformMessageEmptyResponseCallback(int responseId) { + ensureAttachedToNative(); + nativeInvokePlatformMessageEmptyResponseCallback(nativePlatformViewId, responseId); + } + // Send an empty response to a platform message received from Dart. - public native void nativeInvokePlatformMessageEmptyResponseCallback( - long nativePlatformViewAndroid, + private native void nativeInvokePlatformMessageEmptyResponseCallback( + long nativePlatformViewId, int responseId ); + public void invokePlatformMessageResponseCallback(int responseId, ByteBuffer message, int position) { + ensureAttachedToNative(); + nativeInvokePlatformMessageResponseCallback( + nativePlatformViewId, + responseId, + message, + position + ); + } + // Send a data-carrying response to a platform message received from Dart. - public native void nativeInvokePlatformMessageResponseCallback( - long nativePlatformViewAndroid, + private native void nativeInvokePlatformMessageResponseCallback( + long nativePlatformViewId, int responseId, ByteBuffer message, int position @@ -207,6 +411,7 @@ public native void nativeInvokePlatformMessageResponseCallback( // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters //------ Start from Engine --- + // Called by native. @SuppressWarnings("unused") private void onPreEngineRestart() { for (EngineLifecycleListener listener : engineLifecycleListeners) { @@ -214,4 +419,16 @@ private void onPreEngineRestart() { } } //------ End from Engine --- + + private void ensureNotAttachedToNative() { + if (nativePlatformViewId != null) { + throw new RuntimeException("Cannot execute operation because FlutterJNI is attached to native."); + } + } + + private void ensureAttachedToNative() { + if (nativePlatformViewId == null) { + throw new RuntimeException("Cannot execute operation because FlutterJNI is not attached to native."); + } + } } \ No newline at end of file diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java index cece7c178c9eb..e49b3a00bd3a6 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -35,13 +35,11 @@ public class FlutterRenderer implements TextureRegistry { private final FlutterJNI flutterJNI; - private final long nativeObjectReference; private final AtomicLong nextTextureId = new AtomicLong(0L); private RenderSurface renderSurface; - public FlutterRenderer(@NonNull FlutterJNI flutterJNI, long nativeObjectReference) { + public FlutterRenderer(@NonNull FlutterJNI flutterJNI) { this.flutterJNI = flutterJNI; - this.nativeObjectReference = nativeObjectReference; } public void attachToRenderSurface(@NonNull RenderSurface renderSurface) { @@ -148,17 +146,17 @@ public void release() { // TODO(mattcarroll): describe the native behavior that this invokes public void surfaceCreated(Surface surface) { - flutterJNI.nativeSurfaceCreated(nativeObjectReference, surface); + flutterJNI.onSurfaceCreated(surface); } // TODO(mattcarroll): describe the native behavior that this invokes public void surfaceChanged(int width, int height) { - flutterJNI.nativeSurfaceChanged(nativeObjectReference, width, height); + flutterJNI.onSurfaceChanged(width, height); } // TODO(mattcarroll): describe the native behavior that this invokes public void surfaceDestroyed() { - flutterJNI.nativeSurfaceDestroyed(nativeObjectReference); + flutterJNI.onSurfaceDestroyed(); } // TODO(mattcarroll): describe the native behavior that this invokes @@ -173,8 +171,7 @@ public void setViewportMetrics(float devicePixelRatio, int physicalViewInsetRight, int physicalViewInsetBottom, int physicalViewInsetLeft) { - flutterJNI.nativeSetViewportMetrics( - nativeObjectReference, + flutterJNI.setViewportMetrics( devicePixelRatio, physicalWidth, physicalHeight, @@ -191,42 +188,42 @@ public void setViewportMetrics(float devicePixelRatio, // TODO(mattcarroll): describe the native behavior that this invokes public Bitmap getBitmap() { - return flutterJNI.nativeGetBitmap(nativeObjectReference); + return flutterJNI.getBitmap(); } // TODO(mattcarroll): describe the native behavior that this invokes public void dispatchPointerDataPacket(ByteBuffer buffer, int position) { - flutterJNI.nativeDispatchPointerDataPacket(nativeObjectReference, buffer, position); + flutterJNI.dispatchPointerDataPacket(buffer, position); } // TODO(mattcarroll): describe the native behavior that this invokes private void registerTexture(long textureId, SurfaceTexture surfaceTexture) { - flutterJNI.nativeRegisterTexture(nativeObjectReference, textureId, surfaceTexture); + flutterJNI.registerTexture(textureId, surfaceTexture); } // TODO(mattcarroll): describe the native behavior that this invokes private void markTextureFrameAvailable(long textureId) { - flutterJNI.nativeMarkTextureFrameAvailable(nativeObjectReference, textureId); + flutterJNI.markTextureFrameAvailable(textureId); } // TODO(mattcarroll): describe the native behavior that this invokes private void unregisterTexture(long textureId) { - flutterJNI.nativeUnregisterTexture(nativeObjectReference, textureId); + flutterJNI.unregisterTexture(textureId); } // TODO(mattcarroll): describe the native behavior that this invokes public boolean isSoftwareRenderingEnabled() { - return flutterJNI.nativeGetIsSoftwareRenderingEnabled(); + return FlutterJNI.nativeGetIsSoftwareRenderingEnabled(); } // TODO(mattcarroll): describe the native behavior that this invokes public void setAccessibilityFeatures(int flags) { - flutterJNI.nativeSetAccessibilityFeatures(nativeObjectReference, flags); + flutterJNI.setAccessibilityFeatures(flags); } // TODO(mattcarroll): describe the native behavior that this invokes public void setSemanticsEnabled(boolean enabled) { - flutterJNI.nativeSetSemanticsEnabled(nativeObjectReference, enabled); + flutterJNI.setSemanticsEnabled(enabled); } // TODO(mattcarroll): describe the native behavior that this invokes @@ -234,8 +231,7 @@ public void dispatchSemanticsAction(int id, int action, ByteBuffer args, int argsPosition) { - flutterJNI.nativeDispatchSemanticsAction( - nativeObjectReference, + flutterJNI.dispatchSemanticsAction( id, action, args, diff --git a/shell/platform/android/io/flutter/view/FlutterNativeView.java b/shell/platform/android/io/flutter/view/FlutterNativeView.java index 7edbc42d1920d..7eab4b0a58dd0 100644 --- a/shell/platform/android/io/flutter/view/FlutterNativeView.java +++ b/shell/platform/android/io/flutter/view/FlutterNativeView.java @@ -27,7 +27,6 @@ public class FlutterNativeView implements BinaryMessenger { private final Map mPendingReplies = new HashMap<>(); private final FlutterPluginRegistry mPluginRegistry; - private long mNativePlatformView; private FlutterView mFlutterView; private FlutterJNI mFlutterJNI; private final Context mContext; @@ -52,14 +51,13 @@ public FlutterNativeView(Context context, boolean isBackgroundView) { public void detach() { mPluginRegistry.detach(); mFlutterView = null; - mFlutterJNI.nativeDetach(mNativePlatformView); + mFlutterJNI.detachFromNativeButKeepNativeResources(); } public void destroy() { mPluginRegistry.destroy(); mFlutterView = null; - mFlutterJNI.nativeDestroy(mNativePlatformView); - mNativePlatformView = 0; + mFlutterJNI.detachFromNativeAndReleaseResources(); applicationIsRunning = false; } @@ -73,11 +71,7 @@ public void attachViewAndActivity(FlutterView flutterView, Activity activity) { } public boolean isAttached() { - return mNativePlatformView != 0; - } - - public long get() { - return mNativePlatformView; + return mFlutterJNI.isAttached(); } public void assertAttached() { @@ -110,8 +104,13 @@ private void runFromBundleInternal(String bundlePath, String entrypoint, if (applicationIsRunning) throw new AssertionError( "This Flutter engine instance is already running an application"); - mFlutterJNI.nativeRunBundleAndSnapshotFromLibrary(mNativePlatformView, bundlePath, - defaultPath, entrypoint, libraryPath, mContext.getResources().getAssets()); + mFlutterJNI.runBundleAndSnapshotFromLibrary( + bundlePath, + defaultPath, + entrypoint, + libraryPath, + mContext.getResources().getAssets() + ); applicationIsRunning = true; } @@ -121,7 +120,7 @@ public boolean isApplicationRunning() { } public static String getObservatoryUri() { - return new FlutterJNI().nativeGetObservatoryUri(); + return FlutterJNI.nativeGetObservatoryUri(); } @Override @@ -142,10 +141,14 @@ public void send(String channel, ByteBuffer message, BinaryReply callback) { mPendingReplies.put(replyId, callback); } if (message == null) { - mFlutterJNI.nativeDispatchEmptyPlatformMessage(mNativePlatformView, channel, replyId); + mFlutterJNI.dispatchEmptyPlatformMessage(channel, replyId); } else { - mFlutterJNI.nativeDispatchPlatformMessage( - mNativePlatformView, channel, message, message.position(), replyId); + mFlutterJNI.dispatchPlatformMessage( + channel, + message, + message.position(), + replyId + ); } } @@ -163,7 +166,7 @@ public void setMessageHandler(String channel, BinaryMessageHandler handler) { } private void attach(FlutterNativeView view, boolean isBackgroundView) { - mNativePlatformView = mFlutterJNI.nativeAttach(mFlutterJNI, isBackgroundView); + mFlutterJNI.attachToNative(isBackgroundView); } private final class PlatformMessageHandlerImpl implements PlatformMessageHandler { @@ -186,19 +189,19 @@ public void reply(ByteBuffer reply) { throw new IllegalStateException("Reply already submitted"); } if (reply == null) { - mFlutterJNI.nativeInvokePlatformMessageEmptyResponseCallback(mNativePlatformView, replyId); + mFlutterJNI.invokePlatformMessageEmptyResponseCallback(replyId); } else { - mFlutterJNI.nativeInvokePlatformMessageResponseCallback(mNativePlatformView, replyId, reply, reply.position()); + mFlutterJNI.invokePlatformMessageResponseCallback(replyId, reply, reply.position()); } } }); } catch (Exception exception) { Log.e(TAG, "Uncaught exception in binary message listener", exception); - mFlutterJNI.nativeInvokePlatformMessageEmptyResponseCallback(mNativePlatformView, replyId); + mFlutterJNI.invokePlatformMessageEmptyResponseCallback(replyId); } return; } - mFlutterJNI.nativeInvokePlatformMessageEmptyResponseCallback(mNativePlatformView, replyId); + mFlutterJNI.invokePlatformMessageEmptyResponseCallback(replyId); } // Called by native to respond to a platform message that we sent. diff --git a/shell/platform/android/io/flutter/view/FlutterView.java b/shell/platform/android/io/flutter/view/FlutterView.java index 1a8e0e54a0774..fb25c0aa6fb00 100644 --- a/shell/platform/android/io/flutter/view/FlutterView.java +++ b/shell/platform/android/io/flutter/view/FlutterView.java @@ -127,19 +127,19 @@ public FlutterView(Context context, AttributeSet attrs, FlutterNativeView native @Override public void surfaceCreated(SurfaceHolder holder) { assertAttached(); - mNativeView.getFlutterJNI().nativeSurfaceCreated(mNativeView.get(), holder.getSurface()); + mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface()); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { assertAttached(); - mNativeView.getFlutterJNI().nativeSurfaceChanged(mNativeView.get(), width, height); + mNativeView.getFlutterJNI().onSurfaceChanged(width, height); } @Override public void surfaceDestroyed(SurfaceHolder holder) { assertAttached(); - mNativeView.getFlutterJNI().nativeSurfaceDestroyed(mNativeView.get()); + mNativeView.getFlutterJNI().onSurfaceDestroyed(); } }; getHolder().addCallback(mSurfaceCallback); @@ -558,7 +558,7 @@ public boolean onTouchEvent(MotionEvent event) { } assert packet.position() % (kPointerDataFieldCount * kBytePerField) == 0; - mNativeView.getFlutterJNI().nativeDispatchPointerDataPacket(mNativeView.get(), packet, packet.position()); + mNativeView.getFlutterJNI().dispatchPointerDataPacket(packet, packet.position()); return true; } @@ -760,13 +760,13 @@ public void runFromBundle(String bundlePath, String defaultPath, String entrypoi */ public Bitmap getBitmap() { assertAttached(); - return mNativeView.getFlutterJNI().nativeGetBitmap(mNativeView.get()); + return mNativeView.getFlutterJNI().getBitmap(); } private void updateViewportMetrics() { if (!isAttached()) return; - mNativeView.getFlutterJNI().nativeSetViewportMetrics(mNativeView.get(), mMetrics.devicePixelRatio, mMetrics.physicalWidth, + mNativeView.getFlutterJNI().setViewportMetrics(mMetrics.devicePixelRatio, mMetrics.physicalWidth, mMetrics.physicalHeight, mMetrics.physicalPaddingTop, mMetrics.physicalPaddingRight, mMetrics.physicalPaddingBottom, mMetrics.physicalPaddingLeft, mMetrics.physicalViewInsetTop, mMetrics.physicalViewInsetRight, mMetrics.physicalViewInsetBottom, mMetrics.physicalViewInsetLeft); @@ -828,7 +828,7 @@ protected void dispatchSemanticsAction(int id, AccessibilityBridge.Action action encodedArgs = StandardMessageCodec.INSTANCE.encodeMessage(args); position = encodedArgs.position(); } - mNativeView.getFlutterJNI().nativeDispatchSemanticsAction(mNativeView.get(), id, action.value, encodedArgs, position); + mNativeView.getFlutterJNI().dispatchSemanticsAction(id, action.value, encodedArgs, position); } @Override @@ -871,7 +871,7 @@ private void updateAccessibilityFeatures() { mAccessibilityFeatureFlags &= ~AccessibilityFeature.DISABLE_ANIMATIONS.value; } } - mNativeView.getFlutterJNI().nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + mNativeView.getFlutterJNI().setAccessibilityFeatures(mAccessibilityFeatureFlags); } @Override @@ -901,7 +901,7 @@ public void onAccessibilityStateChanged(boolean enabled) { if (mAccessibilityNodeProvider != null) { mAccessibilityNodeProvider.setAccessibilityEnabled(false); } - mNativeView.getFlutterJNI().nativeSetSemanticsEnabled(mNativeView.get(), false); + mNativeView.getFlutterJNI().setSemanticsEnabled(false); } resetWillNotDraw(); } @@ -940,7 +940,7 @@ public void onChange(boolean selfChange, Uri uri) { } else { mAccessibilityFeatureFlags &= ~AccessibilityFeature.DISABLE_ANIMATIONS.value; } - mNativeView.getFlutterJNI().nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + mNativeView.getFlutterJNI().setAccessibilityFeatures(mAccessibilityFeatureFlags); } } @@ -951,14 +951,14 @@ public void onTouchExplorationStateChanged(boolean enabled) { mTouchExplorationEnabled = true; ensureAccessibilityEnabled(); mAccessibilityFeatureFlags |= AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; - mNativeView.getFlutterJNI().nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + mNativeView.getFlutterJNI().setAccessibilityFeatures(mAccessibilityFeatureFlags); } else { mTouchExplorationEnabled = false; if (mAccessibilityNodeProvider != null) { mAccessibilityNodeProvider.handleTouchExplorationExit(); } mAccessibilityFeatureFlags &= ~AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; - mNativeView.getFlutterJNI().nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + mNativeView.getFlutterJNI().setAccessibilityFeatures(mAccessibilityFeatureFlags); } resetWillNotDraw(); } @@ -983,7 +983,7 @@ void ensureAccessibilityEnabled() { if (mAccessibilityNodeProvider == null) { mAccessibilityNodeProvider = new AccessibilityBridge(this); } - mNativeView.getFlutterJNI().nativeSetSemanticsEnabled(mNativeView.get(), true); + mNativeView.getFlutterJNI().setSemanticsEnabled(true); mAccessibilityNodeProvider.setAccessibilityEnabled(true); } @@ -1041,7 +1041,7 @@ public TextureRegistry.SurfaceTextureEntry createSurfaceTexture() { surfaceTexture.detachFromGLContext(); final SurfaceTextureRegistryEntry entry = new SurfaceTextureRegistryEntry(nextTextureId.getAndIncrement(), surfaceTexture); - mNativeView.getFlutterJNI().nativeRegisterTexture(mNativeView.get(), entry.id(), surfaceTexture); + mNativeView.getFlutterJNI().registerTexture(entry.id(), surfaceTexture); return entry; } @@ -1076,7 +1076,7 @@ public void onFrameAvailable(SurfaceTexture texture) { // still be called by a stale reference after released==true and mNativeView==null. return; } - mNativeView.getFlutterJNI().nativeMarkTextureFrameAvailable(mNativeView.get(), SurfaceTextureRegistryEntry.this.id); + mNativeView.getFlutterJNI().markTextureFrameAvailable(SurfaceTextureRegistryEntry.this.id); } }; @@ -1096,7 +1096,7 @@ public void release() { return; } released = true; - mNativeView.getFlutterJNI().nativeUnregisterTexture(mNativeView.get(), id); + mNativeView.getFlutterJNI().unregisterTexture(id); // Otherwise onFrameAvailableListener might be called after mNativeView==null // (https://github.com/flutter/flutter/issues/20951). See also the check in onFrameAvailable. surfaceTexture.setOnFrameAvailableListener(null); diff --git a/shell/platform/android/platform_view_android_jni.cc b/shell/platform/android/platform_view_android_jni.cc index 94437087e3201..28a37f93156fa 100644 --- a/shell/platform/android/platform_view_android_jni.cc +++ b/shell/platform/android/platform_view_android_jni.cc @@ -47,8 +47,6 @@ bool CheckException(JNIEnv* env) { static fml::jni::ScopedJavaGlobalRef* g_flutter_callback_info_class = nullptr; -// FlutterJNI.java, used in 2nd iteration of embedding to centralize all JNI -// calls. static fml::jni::ScopedJavaGlobalRef* g_flutter_jni_class = nullptr; static fml::jni::ScopedJavaGlobalRef* g_surface_texture_class = nullptr; From 22adcc7b44a528d64ef8bba4f21a5a1b55ae16d3 Mon Sep 17 00:00:00 2001 From: matthew-carroll Date: Mon, 10 Dec 2018 17:09:23 -0800 Subject: [PATCH 3/9] Removed comment saying we should make FlutterRenderer#createSurfaceTexture() private. --- .../io/flutter/embedding/engine/renderer/FlutterRenderer.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java index e49b3a00bd3a6..2648c711be1e0 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -70,9 +70,6 @@ public void removeOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedList } //------ START TextureRegistry IMPLEMENTATION ----- - // TODO(mattcarroll): this method probably shouldn't be public. It's part of an obscure relationship - // with PlatformViewsController. Re-evaluate that relationship. - // However, I did find that this method is called from the Camera plugin. // TODO(mattcarroll): detachFromGLContext requires API 16. Create solution for earlier APIs. @TargetApi(Build.VERSION_CODES.JELLY_BEAN) @Override From 9ca9b80a09e08723c4cd202020fb9255b5349157 Mon Sep 17 00:00:00 2001 From: matthew-carroll Date: Mon, 10 Dec 2018 17:36:43 -0800 Subject: [PATCH 4/9] Added JavaDoc for FlutterJNI#setPlatformMessageHandler(...) --- .../flutter/embedding/engine/FlutterJNI.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 23718c67a1040..350f05bb77454 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -130,6 +130,30 @@ private void onFirstFrame() { } } + /** + * Sets the handler for all platform messages that come from the attached platform view to Java. + * + * 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 + * {@code FlutterJNI} methods: + *
    + *
  • {@link #dispatchPlatformMessage(String, ByteBuffer, int, int)}
  • + *
  • {@link #dispatchEmptyPlatformMessage(String, int)}
  • + *
+ * + * {@code FlutterJNI} is also the recipient of all platform messages sent from its attached + * Flutter context (AKA platform view). {@code 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: + * + * {@code setPlatformMessageHandler(PlatformMessageHandler)} + * + * If a message is received but no {@link PlatformMessageHandler} is registered, that message will + * be dropped (ignored). Therefore, when using {@code FlutterJNI} to integrate a Flutter context + * in an app, a {@link 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 {@link FlutterNativeView} for an example implementation. + */ public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler) { this.platformMessageHandler = platformMessageHandler; } From 1e28b723b56b0b850efe8a1adde0dfdd1ef7beae Mon Sep 17 00:00:00 2001 From: matthew-carroll Date: Mon, 10 Dec 2018 17:44:39 -0800 Subject: [PATCH 5/9] Added JavaDoc for FlutterJNI#setRenderSurface(...) --- .../flutter/embedding/engine/FlutterJNI.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 350f05bb77454..47cff999752c2 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -96,6 +96,22 @@ public class FlutterJNI { private final Set engineLifecycleListeners = new CopyOnWriteArraySet<>(); private final Set firstFrameListeners = new CopyOnWriteArraySet<>(); + /** + * Sets the {@link FlutterRenderer.RenderSurface} delegate for the attached Flutter context. + * + * Flutter expects a user interface to exist on the platform side (Android), and that interface + * is expected to offer some capabilities that Flutter depends upon. The {@link FlutterRenderer.RenderSurface} + * interface represents those expectations. For example, Flutter expects to be able to request + * that its user interface "update custom accessibility actions" and therefore the delegate interface + * declares a corresponding method, {@link FlutterRenderer.RenderSurface#updateCustomAccessibilityActions(ByteBuffer, String[])}. + * + * If an app includes a user interface that renders a Flutter UI then a {@link FlutterRenderer.RenderSurface} + * should be set (this is the typical Flutter scenario). If no UI is being rendered, such as a + * Flutter app that is running Dart code in the background, then no registration may be necessary. + * + * If no {@link FlutterRenderer.RenderSurface} is registered then related messages coming from + * Flutter will be dropped (ignored). + */ public void setRenderSurface(@Nullable FlutterRenderer.RenderSurface renderSurface) { this.renderSurface = renderSurface; } @@ -107,6 +123,7 @@ private void updateSemantics(ByteBuffer buffer, String[] strings) { if (renderSurface != null) { renderSurface.updateSemantics(buffer, strings); } + // TODO(mattcarroll): log dropped messages when in debug mode } // TODO(mattcarroll): define "update" @@ -116,6 +133,7 @@ private void updateCustomAccessibilityActions(ByteBuffer buffer, String[] string if (renderSurface != null) { renderSurface.updateCustomAccessibilityActions(buffer, strings); } + // TODO(mattcarroll): log dropped messages when in debug mode } // Called by native to notify first Flutter frame rendered. @@ -124,6 +142,7 @@ private void onFirstFrame() { if (renderSurface != null) { renderSurface.onFirstFrameRendered(); } + // TODO(mattcarroll): log dropped messages when in debug mode for (OnFirstFrameRenderedListener listener : firstFrameListeners) { listener.onFirstFrameRendered(); @@ -164,6 +183,7 @@ private void handlePlatformMessage(final String channel, byte[] message, final i if (platformMessageHandler != null) { platformMessageHandler.handlePlatformMessage(channel, message, replyId); } + // TODO(mattcarroll): log dropped messages when in debug mode } // Called by native to respond to a platform message that we sent. @@ -172,6 +192,7 @@ private void handlePlatformMessageResponse(int replyId, byte[] reply) { if (platformMessageHandler != null) { platformMessageHandler.handlePlatformMessageResponse(replyId, reply); } + // TODO(mattcarroll): log dropped messages when in debug mode } public void addEngineLifecycleListener(@NonNull EngineLifecycleListener engineLifecycleListener) { From 785be70c73ceea79b144ae8bff25e7290425932c Mon Sep 17 00:00:00 2001 From: matthew-carroll Date: Thu, 20 Dec 2018 20:20:15 -0500 Subject: [PATCH 6/9] Specified @UiThread for public JNI methods, added GitHub issues for TODOs. --- .../flutter/embedding/engine/FlutterJNI.java | 149 ++++++++++++------ 1 file changed, 97 insertions(+), 52 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 47cff999752c2..e82260150dd71 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -9,11 +9,12 @@ import android.graphics.SurfaceTexture; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.UiThread; import android.view.Surface; import java.nio.ByteBuffer; +import java.util.HashSet; import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; import io.flutter.embedding.engine.dart.PlatformMessageHandler; import io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener; @@ -86,15 +87,17 @@ public class FlutterJNI { private static final String TAG = "FlutterJNI"; + @UiThread public static native boolean nativeGetIsSoftwareRenderingEnabled(); + @UiThread public static native String nativeGetObservatoryUri(); private Long nativePlatformViewId; private FlutterRenderer.RenderSurface renderSurface; private PlatformMessageHandler platformMessageHandler; - private final Set engineLifecycleListeners = new CopyOnWriteArraySet<>(); - private final Set firstFrameListeners = new CopyOnWriteArraySet<>(); + private final Set engineLifecycleListeners = new HashSet<>(); + private final Set firstFrameListeners = new HashSet<>(); /** * Sets the {@link FlutterRenderer.RenderSurface} delegate for the attached Flutter context. @@ -112,37 +115,49 @@ public class FlutterJNI { * If no {@link FlutterRenderer.RenderSurface} is registered then related messages coming from * Flutter will be dropped (ignored). */ + @UiThread public void setRenderSurface(@Nullable FlutterRenderer.RenderSurface renderSurface) { this.renderSurface = renderSurface; } - // TODO(mattcarroll): define "update" - // Called by native to update the semantics/accessibility tree. + /** + * Call invoked by native to be forwarded to an {@link io.flutter.view.AccessibilityBridge}. + * + * The {@code buffer} and {@code strings} form a communication protocol that is implemented here: + * https://github.com/flutter/engine/blob/master/shell/platform/android/platform_view_android.cc#L207 + */ @SuppressWarnings("unused") + @UiThread private void updateSemantics(ByteBuffer buffer, String[] strings) { if (renderSurface != null) { renderSurface.updateSemantics(buffer, strings); } - // TODO(mattcarroll): log dropped messages when in debug mode + // TODO(mattcarroll): log dropped messages when in debug mode (https://github.com/flutter/flutter/issues/25391) } - // TODO(mattcarroll): define "update" - // Called by native to update the custom accessibility actions. + /** + * Call invoked by native to be forwarded to an {@link io.flutter.view.AccessibilityBridge}. + * + * The {@code buffer} and {@code strings} form a communication protocol that is implemented here: + * https://github.com/flutter/engine/blob/master/shell/platform/android/platform_view_android.cc#L207 + */ @SuppressWarnings("unused") + @UiThread private void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { if (renderSurface != null) { renderSurface.updateCustomAccessibilityActions(buffer, strings); } - // TODO(mattcarroll): log dropped messages when in debug mode + // TODO(mattcarroll): log dropped messages when in debug mode (https://github.com/flutter/flutter/issues/25391) } // Called by native to notify first Flutter frame rendered. @SuppressWarnings("unused") + @UiThread private void onFirstFrame() { if (renderSurface != null) { renderSurface.onFirstFrameRendered(); } - // TODO(mattcarroll): log dropped messages when in debug mode + // TODO(mattcarroll): log dropped messages when in debug mode (https://github.com/flutter/flutter/issues/25391) for (OnFirstFrameRenderedListener listener : firstFrameListeners) { listener.onFirstFrameRendered(); @@ -173,6 +188,7 @@ private void onFirstFrame() { * to operate correctly. Moreover, the handler must be implemented such that fundamental platform * messages are handled as expected. See {@link FlutterNativeView} for an example implementation. */ + @UiThread public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler) { this.platformMessageHandler = platformMessageHandler; } @@ -183,7 +199,7 @@ private void handlePlatformMessage(final String channel, byte[] message, final i if (platformMessageHandler != null) { platformMessageHandler.handlePlatformMessage(channel, message, replyId); } - // TODO(mattcarroll): log dropped messages when in debug mode + // TODO(mattcarroll): log dropped messages when in debug mode (https://github.com/flutter/flutter/issues/25391) } // Called by native to respond to a platform message that we sent. @@ -192,27 +208,32 @@ private void handlePlatformMessageResponse(int replyId, byte[] reply) { if (platformMessageHandler != null) { platformMessageHandler.handlePlatformMessageResponse(replyId, reply); } - // TODO(mattcarroll): log dropped messages when in debug mode + // TODO(mattcarroll): log dropped messages when in debug mode (https://github.com/flutter/flutter/issues/25391) } + @UiThread public void addEngineLifecycleListener(@NonNull EngineLifecycleListener engineLifecycleListener) { engineLifecycleListeners.add(engineLifecycleListener); } + @UiThread public void removeEngineLifecycleListener(@NonNull EngineLifecycleListener engineLifecycleListener) { engineLifecycleListeners.remove(engineLifecycleListener); } + @UiThread public void addOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { firstFrameListeners.add(listener); } + @UiThread public void removeOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { firstFrameListeners.remove(listener); } - // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters + // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters (https://github.com/flutter/flutter/issues/25533) //----- Start from FlutterView ----- + @UiThread public void onSurfaceCreated(@NonNull Surface surface) { ensureAttachedToNative(); nativeSurfaceCreated(nativePlatformViewId, surface); @@ -220,6 +241,7 @@ public void onSurfaceCreated(@NonNull Surface surface) { private native void nativeSurfaceCreated(long nativePlatformViewId, Surface surface); + @UiThread public void onSurfaceChanged(int width, int height) { ensureAttachedToNative(); nativeSurfaceChanged(nativePlatformViewId, width, height); @@ -227,6 +249,7 @@ public void onSurfaceChanged(int width, int height) { private native void nativeSurfaceChanged(long nativePlatformViewId, int width, int height); + @UiThread public void onSurfaceDestroyed() { ensureAttachedToNative(); nativeSurfaceDestroyed(nativePlatformViewId); @@ -234,17 +257,20 @@ public void onSurfaceDestroyed() { private native void nativeSurfaceDestroyed(long nativePlatformViewId); - 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) { + @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 + ) { ensureAttachedToNative(); nativeSetViewportMetrics( nativePlatformViewId, @@ -262,19 +288,22 @@ public void setViewportMetrics(float devicePixelRatio, ); } - private native void nativeSetViewportMetrics(long nativePlatformViewId, - float devicePixelRatio, - int physicalWidth, - int physicalHeight, - int physicalPaddingTop, - int physicalPaddingRight, - int physicalPaddingBottom, - int physicalPaddingLeft, - int physicalViewInsetTop, - int physicalViewInsetRight, - int physicalViewInsetBottom, - int physicalViewInsetLeft); + private native void nativeSetViewportMetrics( + long nativePlatformViewId, + float devicePixelRatio, + int physicalWidth, + int physicalHeight, + int physicalPaddingTop, + int physicalPaddingRight, + int physicalPaddingBottom, + int physicalPaddingLeft, + int physicalViewInsetTop, + int physicalViewInsetRight, + int physicalViewInsetBottom, + int physicalViewInsetLeft + ); + @UiThread public Bitmap getBitmap() { ensureAttachedToNative(); return nativeGetBitmap(nativePlatformViewId); @@ -282,6 +311,7 @@ public Bitmap getBitmap() { private native Bitmap nativeGetBitmap(long nativePlatformViewId); + @UiThread public void dispatchPointerDataPacket(ByteBuffer buffer, int position) { ensureAttachedToNative(); nativeDispatchPointerDataPacket(nativePlatformViewId, buffer, position); @@ -291,17 +321,21 @@ private native void nativeDispatchPointerDataPacket(long nativePlatformViewId, ByteBuffer buffer, int position); + @UiThread public void dispatchSemanticsAction(int id, int action, ByteBuffer args, int argsPosition) { ensureAttachedToNative(); nativeDispatchSemanticsAction(nativePlatformViewId, id, action, args, argsPosition); } - private native void nativeDispatchSemanticsAction(long nativePlatformViewId, - int id, - int action, - ByteBuffer args, - int argsPosition); + private native void nativeDispatchSemanticsAction( + long nativePlatformViewId, + int id, + int action, + ByteBuffer args, + int argsPosition + ); + @UiThread public void setSemanticsEnabled(boolean enabled) { ensureAttachedToNative(); nativeSetSemanticsEnabled(nativePlatformViewId, enabled); @@ -309,6 +343,7 @@ public void setSemanticsEnabled(boolean enabled) { private native void nativeSetSemanticsEnabled(long nativePlatformViewId, boolean enabled); + @UiThread public void setAccessibilityFeatures(int flags) { ensureAttachedToNative(); nativeSetAccessibilityFeatures(nativePlatformViewId, flags); @@ -316,6 +351,7 @@ public void setAccessibilityFeatures(int flags) { private native void nativeSetAccessibilityFeatures(long nativePlatformViewId, int flags); + @UiThread public void registerTexture(long textureId, SurfaceTexture surfaceTexture) { ensureAttachedToNative(); nativeRegisterTexture(nativePlatformViewId, textureId, surfaceTexture); @@ -323,6 +359,7 @@ public void registerTexture(long textureId, SurfaceTexture surfaceTexture) { private native void nativeRegisterTexture(long nativePlatformViewId, long textureId, SurfaceTexture surfaceTexture); + @UiThread public void markTextureFrameAvailable(long textureId) { ensureAttachedToNative(); nativeMarkTextureFrameAvailable(nativePlatformViewId, textureId); @@ -330,6 +367,7 @@ public void markTextureFrameAvailable(long textureId) { private native void nativeMarkTextureFrameAvailable(long nativePlatformViewId, long textureId); + @UiThread public void unregisterTexture(long textureId) { ensureAttachedToNative(); nativeUnregisterTexture(nativePlatformViewId, textureId); @@ -338,12 +376,13 @@ public void unregisterTexture(long textureId) { private native void nativeUnregisterTexture(long nativePlatformViewId, long textureId); //------- End from FlutterView ----- - // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters + // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters (https://github.com/flutter/flutter/issues/25533) //------ Start from FlutterNativeView ---- public boolean isAttached() { return nativePlatformViewId != null; } + @UiThread public void attachToNative(boolean isBackgroundView) { ensureNotAttachedToNative(); nativePlatformViewId = nativeAttach(this, isBackgroundView); @@ -351,6 +390,7 @@ public void attachToNative(boolean isBackgroundView) { private native long nativeAttach(FlutterJNI flutterJNI, boolean isBackgroundView); + @UiThread public void detachFromNativeButKeepNativeResources() { ensureAttachedToNative(); nativeDetach(nativePlatformViewId); @@ -359,6 +399,7 @@ public void detachFromNativeButKeepNativeResources() { private native void nativeDetach(long nativePlatformViewId); + @UiThread public void detachFromNativeAndReleaseResources() { ensureAttachedToNative(); nativeDestroy(nativePlatformViewId); @@ -367,16 +408,17 @@ public void detachFromNativeAndReleaseResources() { private native void nativeDestroy(long nativePlatformViewId); - public void runBundleAndSnapshotFromLibrary(@NonNull String pathToBundleWithEntrypoint, - @Nullable String pathToFallbackBundle, - @Nullable String entrypointFunctionName, - @Nullable String pathToEntrypointFunction, - @NonNull AssetManager assetManager) { + @UiThread + public void runBundleAndSnapshotFromLibrary( + @NonNull String[] prioritizedBundlePaths, + @Nullable String entrypointFunctionName, + @Nullable String pathToEntrypointFunction, + @NonNull AssetManager assetManager + ) { ensureAttachedToNative(); nativeRunBundleAndSnapshotFromLibrary( nativePlatformViewId, - pathToBundleWithEntrypoint, - pathToFallbackBundle, + prioritizedBundlePaths, entrypointFunctionName, pathToEntrypointFunction, assetManager @@ -385,13 +427,13 @@ public void runBundleAndSnapshotFromLibrary(@NonNull String pathToBundleWithEntr private native void nativeRunBundleAndSnapshotFromLibrary( long nativePlatformViewId, - @NonNull String pathToBundleWithEntrypoint, - @Nullable String pathToFallbackBundle, + @NonNull String[] prioritizedBundlePaths, @Nullable String entrypointFunctionName, @Nullable String pathToEntrypointFunction, @NonNull AssetManager manager ); + @UiThread public void dispatchEmptyPlatformMessage(String channel, int responseId) { ensureAttachedToNative(); nativeDispatchEmptyPlatformMessage(nativePlatformViewId, channel, responseId); @@ -404,6 +446,7 @@ private native void nativeDispatchEmptyPlatformMessage( int responseId ); + @UiThread public void dispatchPlatformMessage(String channel, ByteBuffer message, int position, int responseId) { ensureAttachedToNative(); nativeDispatchPlatformMessage( @@ -424,6 +467,7 @@ private native void nativeDispatchPlatformMessage( int responseId ); + @UiThread public void invokePlatformMessageEmptyResponseCallback(int responseId) { ensureAttachedToNative(); nativeInvokePlatformMessageEmptyResponseCallback(nativePlatformViewId, responseId); @@ -435,6 +479,7 @@ private native void nativeInvokePlatformMessageEmptyResponseCallback( int responseId ); + @UiThread public void invokePlatformMessageResponseCallback(int responseId, ByteBuffer message, int position) { ensureAttachedToNative(); nativeInvokePlatformMessageResponseCallback( @@ -454,7 +499,7 @@ private native void nativeInvokePlatformMessageResponseCallback( ); //------ End from FlutterNativeView ---- - // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters + // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters (https://github.com/flutter/flutter/issues/25533) //------ Start from Engine --- // Called by native. @SuppressWarnings("unused") From 691ad358098bd084ce26a552f879a6a1c294406c Mon Sep 17 00:00:00 2001 From: matthew-carroll Date: Wed, 19 Dec 2018 18:59:57 -0500 Subject: [PATCH 7/9] PR Updates: added and clarified JavaDocs. --- .../android/io/flutter/embedding/engine/FlutterEngine.java | 5 ++++- .../flutter/embedding/engine/renderer/FlutterRenderer.java | 2 +- .../engine/renderer/OnFirstFrameRenderedListener.java | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index 5810ea1db57bb..62373d891adce 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -15,6 +15,9 @@ * allowing a {@code FlutterEngine} to move from UI interaction to data-only processing and then * back to UI interaction. * + * Multiple {@code FlutterEngine}s may exist, execute Dart code, and render UIs within a single + * Android app. + * * To start running Flutter within this {@code FlutterEngine}, get a reference to this engine's * {@link DartExecutor} and then use {@link DartExecutor#runFromBundle(FlutterRunArguments)}. * The {@link DartExecutor#runFromBundle(FlutterRunArguments)} method must not be invoked twice on the same @@ -32,7 +35,7 @@ public class FlutterEngine { */ public interface EngineLifecycleListener { /** - * Lifecycle callback invoked after a hot restart of the Flutter engine. + * Lifecycle callback invoked before a hot restart of the Flutter engine. */ void onPreEngineRestart(); } diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java index 2648c711be1e0..656990a1e2717 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -25,7 +25,7 @@ * {@code FlutterRenderer} works in tandem with a provided {@link RenderSurface} to create an * interactive Flutter UI. * - * {@code FlutterRenderer} manages textures for rendering, and forwards messages to native Flutter + * {@code FlutterRenderer} manages textures for rendering, and forwards some Java calls to native Flutter * code via JNI. The corresponding {@link RenderSurface} is used as a delegate to carry out * certain actions on behalf of this {@code FlutterRenderer} within an Android view hierarchy. * diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java b/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java index a43b3481c402c..bc6e21e5b207d 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java @@ -5,6 +5,8 @@ package io.flutter.embedding.engine.renderer; /** + * Listener invoked after Flutter paints its first frame since being initialized. + * * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. * IF YOU USE IT, WE WILL BREAK YOU. */ From cb00a5e81e43e803c51c918d46c72cbe6f178454 Mon Sep 17 00:00:00 2001 From: matthew-carroll Date: Thu, 20 Dec 2018 19:44:42 -0500 Subject: [PATCH 8/9] PR Updates: Added blank lines at end of files. --- .../android/io/flutter/embedding/engine/FlutterEngine.java | 2 +- .../android/io/flutter/embedding/engine/FlutterJNI.java | 2 +- .../flutter/embedding/engine/dart/PlatformMessageHandler.java | 2 +- .../io/flutter/embedding/engine/renderer/FlutterRenderer.java | 2 +- .../embedding/engine/renderer/OnFirstFrameRenderedListener.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index 62373d891adce..4f98a8c73eb89 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -39,4 +39,4 @@ public interface EngineLifecycleListener { */ void onPreEngineRestart(); } -} \ No newline at end of file +} diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index e82260150dd71..6cc9e36f92799 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -521,4 +521,4 @@ private void ensureAttachedToNative() { throw new RuntimeException("Cannot execute operation because FlutterJNI is not attached to native."); } } -} \ No newline at end of file +} diff --git a/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java b/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java index 52ae3b669a050..0403348b871b8 100644 --- a/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java +++ b/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java @@ -12,4 +12,4 @@ public interface PlatformMessageHandler { void handlePlatformMessage(final String channel, byte[] message, final int replyId); void handlePlatformMessageResponse(int replyId, byte[] reply); -} \ No newline at end of file +} diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java index 656990a1e2717..6048cd4ab59aa 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -265,4 +265,4 @@ public interface RenderSurface { */ void onFirstFrameRendered(); } -} \ No newline at end of file +} diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java b/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java index bc6e21e5b207d..46d271b7f171b 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java @@ -17,4 +17,4 @@ public interface OnFirstFrameRenderedListener { * This method will not be invoked if this listener is added after the first frame is rendered. */ void onFirstFrameRendered(); -} \ No newline at end of file +} From 42bc54645453a9c4006a0f1cf0b4b290e883b6b8 Mon Sep 17 00:00:00 2001 From: matthew-carroll Date: Thu, 20 Dec 2018 20:37:14 -0500 Subject: [PATCH 9/9] PR Updates: Added TODO for cbracken to expand javadoc. --- .../android/io/flutter/embedding/engine/FlutterJNI.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 6cc9e36f92799..4923f1c77c341 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -140,6 +140,8 @@ private void updateSemantics(ByteBuffer buffer, String[] strings) { * * The {@code buffer} and {@code strings} form a communication protocol that is implemented here: * https://github.com/flutter/engine/blob/master/shell/platform/android/platform_view_android.cc#L207 + * + * // TODO(cbracken): expand these docs to include more actionable information. */ @SuppressWarnings("unused") @UiThread