Skip to content

Commit

Permalink
Get rid of FlutterNativeView, introduce FlutterEngine and FlutterRend…
Browse files Browse the repository at this point in the history
…erer (flutter#6195)

* Copied over FlutterView. Copied a number of dependencies due to package change. Grouped related methods within FlutterView. Deleted a few unused methods.

* Moved BinaryMessenger out of FlutterView and into FlutterEngine, moved native calls from FlutterView to FlutterEngine.

* Introduced concept of a FlutterRenderer. It doesn't have any meaningful behavior yet. Need strategy for resolving FlutterNativeView vs FlutterEngine first.

* Removed discovery behavior from FlutterView to match recent PR: flutter#6157

* Replaced FlutterNativeView with FlutterEngine and FlutterRenderer. Eliminated FlutterEngine references from within FlutterView. Centralized all JNI calls within a FlutterJNI and updated the C side accordingly.

* Added comments to FlutterEngine, FlutterRender, FlutterFragment, and FlutterView.

* Removed some extraneous code. Adjusted first frame notification slightly.

* Fixed formatting in platform_view_android_jni.cc

* Update licenses file.

* Exposed FlutterEngine from plugin system. Updated some access modifiers as needed.

* PR Updates.
  • Loading branch information
matthew-carroll authored and Amir Hardon committed Sep 21, 2018
1 parent 6d203b0 commit 6435a7d
Show file tree
Hide file tree
Showing 20 changed files with 1,943 additions and 1,454 deletions.
4 changes: 3 additions & 1 deletion ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivityDele
FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivityEvents.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/legacy/FlutterNativeView.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/legacy/FlutterPluginRegistry.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/legacy/PluginRegistry.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BasicMessageChannel.java
Expand Down Expand Up @@ -202,6 +201,9 @@ FILE: ../../../flutter/lib/ui/natives.dart
FILE: ../../../flutter/lib/ui/window/window.h
FILE: ../../../flutter/shell/common/skia_event_tracer_impl.cc
FILE: ../../../flutter/shell/platform/android/apk_asset_provider.cc
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/FlutterEngine.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/FlutterJNI.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/FlutterRenderer.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/JSONUtil.java
FILE: ../../../flutter/shell/platform/darwin/desktop/Info.plist
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Flutter.podspec
Expand Down
18 changes: 18 additions & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,24 @@ java_library("flutter_shell_java") {
supports_android = true

java_files = [
"io/flutter/embedding/FlutterActivity.java",
"io/flutter/embedding/FlutterEngine.java",
"io/flutter/embedding/FlutterFragment.java",
"io/flutter/embedding/FlutterJNI.java",
"io/flutter/embedding/FlutterRenderer.java",
"io/flutter/embedding/FlutterShellArgs.java",
"io/flutter/embedding/FlutterView.java",
"io/flutter/embedding/legacy/AccessibilityBridge.java",
"io/flutter/embedding/legacy/FlutterPluginRegistry.java",
"io/flutter/embedding/legacy/InputConnectionAdaptor.java",
"io/flutter/embedding/legacy/PlatformViewFactory.java",
"io/flutter/embedding/legacy/PlatformViewRegistry.java",
"io/flutter/embedding/legacy/PlatformViewRegistryImpl.java",
"io/flutter/embedding/legacy/PlatformViewsController.java",
"io/flutter/embedding/legacy/PluginRegistry.java",
"io/flutter/embedding/legacy/SingleViewPresentation.java",
"io/flutter/embedding/legacy/TextInputPlugin.java",
"io/flutter/embedding/legacy/VirtualDisplayController.java",
"io/flutter/app/FlutterActivity.java",
"io/flutter/app/FlutterActivityDelegate.java",
"io/flutter/app/FlutterActivityEvents.java",
Expand Down
12 changes: 12 additions & 0 deletions shell/platform/android/io/flutter/embedding/FlutterActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;

import io.flutter.embedding.legacy.PluginRegistry;
import io.flutter.plugin.platform.PlatformPlugin;
import io.flutter.view.FlutterMain;

Expand Down Expand Up @@ -65,6 +67,7 @@ public class FlutterActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate()");

FlutterShellArgs args = FlutterShellArgs.fromIntent(getIntent());
// TODO(mattcarroll): Change FlutterMain to accept FlutterShellArgs and move additional constants in
Expand All @@ -79,6 +82,7 @@ public void onCreate(Bundle savedInstanceState) {
@Override
public void onPostResume() {
super.onPostResume();
Log.d(TAG, "onPostResume()");
flutterFragment.onPostResume();
}

Expand All @@ -90,6 +94,7 @@ protected void onNewIntent(Intent intent) {

@Override
public void onBackPressed() {
Log.d(TAG, "onBackPressed()");
flutterFragment.onBackPressed();
}

Expand All @@ -102,11 +107,17 @@ public void onUserLeaveHint() {
flutterFragment.onUserLeaveHint();
}

@Nullable
protected FlutterEngine getFlutterEngine() {
return flutterFragment.getFlutterEngine();
}

/**
* Sets up a {@code FrameLayout} that takes up all available space in the {@code Activity}. This
* {@code FrameLayout} will hold a {@code FlutterFragment}, which displays a {@code FlutterView}.
*/
private void createFlutterFragmentContainer() {
Log.d(TAG, "createFlutterFragmentContainer()");
FrameLayout container = new FrameLayout(this);
container.setId(CONTAINER_ID);
container.setLayoutParams(new ViewGroup.LayoutParams(
Expand All @@ -122,6 +133,7 @@ private void createFlutterFragmentContainer() {
* a reference to that {@code FlutterFragment} is retained in {@code flutterFragment}.
*/
private void createFlutterFragment() {
Log.d(TAG, "createFlutterFragment()");
FragmentManager fragmentManager = getFragmentManager();

flutterFragment = (FlutterFragment) fragmentManager.findFragmentByTag(TAG_FLUTTER_FRAGMENT);
Expand Down
262 changes: 262 additions & 0 deletions shell/platform/android/io/flutter/embedding/FlutterEngine.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
package io.flutter.embedding;

import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;

import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import io.flutter.embedding.legacy.FlutterPluginRegistry;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.view.FlutterRunArguments;

/**
* A single Flutter execution environment.
*
* 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}, use {@link #runFromBundle(FlutterRunArguments)}.
* The {@link #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 FlutterView} as a {@link FlutterRenderer.RenderSurface}.
* TODO(mattcarroll): for the above instructions for RenderSurface to be true, we need to refactor
* the native relationships and also the PlatformViewsController relationship.
*/
public class FlutterEngine implements BinaryMessenger {
private static final String TAG = "FlutterEngine";

private final Resources resources;
private final FlutterJNI flutterJNI;
private final FlutterRenderer renderer;
private final FlutterPluginRegistry pluginRegistry;
private final Map<String, BinaryMessageHandler> mMessageHandlers;
private final Map<Integer, BinaryReply> mPendingReplies = new HashMap<>();
private long nativeObjectReference;
private boolean isBackgroundView; // TODO(mattcarroll): rename to something without "view"
private boolean applicationIsRunning;
private int mNextReplyId = 1;

public FlutterEngine(
Context context,
Resources resources,
boolean isBackgroundView
) {
this.flutterJNI = new FlutterJNI();
this.resources = resources;
this.isBackgroundView = isBackgroundView;
pluginRegistry = new FlutterPluginRegistry(this, context);
mMessageHandlers = new HashMap<>();

attach();
// TODO(mattcarroll): FlutterRenderer is temporally coupled to attach(). Remove that coupling if possible.
this.renderer = new FlutterRenderer(flutterJNI, nativeObjectReference);
}

private void attach() {
// TODO(mattcarroll): what impact does "isBackgroundView' have?
nativeObjectReference = flutterJNI.nativeAttach(this, isBackgroundView);

if (!isAttached()) {
throw new RuntimeException("FlutterEngine failed to attach to its native Object reference.");
}
}

private void assertAttached() {
if (!isAttached()) throw new AssertionError("Platform view is not attached");
}

@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean isAttached() {
return nativeObjectReference != 0;
}

public void detach() {
pluginRegistry.detach();
// TODO(mattcarroll): why do we have a nativeDetach() method? can we get rid of this?
flutterJNI.nativeDetach(nativeObjectReference);
}

public void destroy() {
pluginRegistry.destroy();
flutterJNI.nativeDestroy(nativeObjectReference);
nativeObjectReference = 0;
applicationIsRunning = false;
}

public void runFromBundle(FlutterRunArguments args) {
if (args.bundlePath == null) {
throw new AssertionError("A bundlePath must be specified");
} else if (args.entrypoint == null) {
throw new AssertionError("An entrypoint must be specified");
}
runFromBundleInternal(args.bundlePath, args.entrypoint, args.libraryPath, args.defaultPath);
}

private void runFromBundleInternal(
String bundlePath,
String entrypoint,
String libraryPath,
String defaultPath
) {
assertAttached();

if (applicationIsRunning) {
throw new AssertionError("This Flutter engine instance is already running an application");
}

flutterJNI.nativeRunBundleAndSnapshotFromLibrary(nativeObjectReference, bundlePath,
defaultPath, entrypoint, libraryPath, resources.getAssets());

applicationIsRunning = true;
}

public boolean isApplicationRunning() {
return applicationIsRunning;
}

// TODO(mattcarroll): what does this callback actually represent?
// Called by native to notify when the engine is restarted (cold reload).
@SuppressWarnings("unused")
private void onPreEngineRestart() {
if (pluginRegistry == null)
return;
pluginRegistry.onPreEngineRestart();
}

public FlutterRenderer getRenderer() {
return renderer;
}

public FlutterPluginRegistry getPluginRegistry() {
return pluginRegistry;
}

// TODO(mattcarroll): Is this method really all about plugins? or does it have other implications?
public void attachViewAndActivity(Activity activity) {
pluginRegistry.attach(this, activity);
}

// TODO(mattcarroll): Implement observatory lookup for "flutter attach"
public String getObservatoryUrl() {
return flutterJNI.nativeGetObservatoryUri();
}

//------- START ISOLATE METHOD CHANNEL COMMS ------
@Override
public void setMessageHandler(String channel, BinaryMessageHandler handler) {
if (handler == null) {
mMessageHandlers.remove(channel);
} else {
mMessageHandlers.put(channel, handler);
}
}

@Override
public void send(String channel, ByteBuffer message) {
send(channel, message, null);
}

@Override
public void send(String channel, ByteBuffer message, BinaryReply callback) {
if (!isAttached()) {
Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel);
return;
}

int replyId = 0;
if (callback != null) {
replyId = mNextReplyId++;
mPendingReplies.put(replyId, callback);
}
if (message == null) {
flutterJNI.nativeDispatchEmptyPlatformMessage(nativeObjectReference, channel, replyId);
} else {
flutterJNI.nativeDispatchPlatformMessage(
nativeObjectReference, channel, message, message.position(), replyId);
}
}

// Called by native to send us a platform message.
@SuppressWarnings("unused")
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;
}
if (done.getAndSet(true)) {
throw new IllegalStateException("Reply already submitted");
}
if (reply == null) {
flutterJNI.nativeInvokePlatformMessageEmptyResponseCallback(nativeObjectReference, replyId);
} else {
flutterJNI.nativeInvokePlatformMessageResponseCallback(nativeObjectReference, replyId, reply, reply.position());
}
}
});
} catch (Exception ex) {
Log.e(TAG, "Uncaught exception in binary message listener", ex);
flutterJNI.nativeInvokePlatformMessageEmptyResponseCallback(nativeObjectReference, replyId);
}
return;
}
flutterJNI.nativeInvokePlatformMessageEmptyResponseCallback(nativeObjectReference, replyId);
}

// Called by native to respond to a platform message that we sent.
@SuppressWarnings("unused")
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);
}
}
}
//------- END ISOLATE METHOD CHANNEL COMMS -----

//------ START NATIVE CALLBACKS THAT SHOULD BE MOVED TO FlutterRenderer ------
// Called by native to update the semantics/accessibility tree.
@SuppressWarnings("unused")
private void updateSemantics(ByteBuffer buffer, String[] strings) {
Log.d(TAG, "updateSemantics()");
renderer.updateSemantics(buffer, strings);
}

// Called by native to update the custom accessibility actions.
@SuppressWarnings("unused")
private void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) {
Log.d(TAG, "updateCustomAccessibilityActions()");
renderer.updateCustomAccessibilityActions(buffer, strings);
}

// Called by native to notify first Flutter frame rendered.
@SuppressWarnings("unused")
private void onFirstFrame() {
Log.d(TAG, "onFirstFrame()");
renderer.onFirstFrameRendered();
}
//------ END NATIVE CALLBACKS THAT SHOULD BE MOVED TO FlutterRenderer ------
}
Loading

0 comments on commit 6435a7d

Please sign in to comment.