Files
engine/shell/platform/android/io/flutter/view/FlutterNativeView.java
T
Jason Simmons fcff2d619c Use the DartServiceIsolate status callback to publish the observatory URI to the Android embedder (#9337)
The Android embedder had been using a JNI call to get the observatory URI from
DartServiceIsolate.  This call was not thread safe and was redundant with the
server status callback mechanism used on iOS.
2019-06-17 16:17:29 -07:00

229 lines
7.6 KiB
Java

// 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.view;
import android.app.Activity;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
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.dart.DartExecutor;
import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.embedding.engine.renderer.FlutterRenderer.RenderSurface;
import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener;
import io.flutter.plugin.common.*;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.HashMap;
import java.util.Map;
import io.flutter.embedding.engine.dart.PlatformMessageHandler;
public class FlutterNativeView implements BinaryMessenger {
private static final String TAG = "FlutterNativeView";
private final FlutterPluginRegistry mPluginRegistry;
private final DartExecutor dartExecutor;
private FlutterView mFlutterView;
private final FlutterJNI mFlutterJNI;
private final Context mContext;
private boolean applicationIsRunning;
public FlutterNativeView(@NonNull Context context) {
this(context, false);
}
public FlutterNativeView(@NonNull Context context, boolean isBackgroundView) {
mContext = context;
mPluginRegistry = new FlutterPluginRegistry(this, context);
mFlutterJNI = new FlutterJNI();
mFlutterJNI.setRenderSurface(new RenderSurfaceImpl());
this.dartExecutor = new DartExecutor(mFlutterJNI);
mFlutterJNI.addEngineLifecycleListener(new EngineLifecycleListenerImpl());
attach(this, isBackgroundView);
assertAttached();
}
public void detachFromFlutterView() {
mPluginRegistry.detach();
mFlutterView = null;
}
public void destroy() {
mPluginRegistry.destroy();
dartExecutor.onDetachedFromJNI();
mFlutterView = null;
mFlutterJNI.detachFromNativeAndReleaseResources();
applicationIsRunning = false;
}
@NonNull
public DartExecutor getDartExecutor() {
return dartExecutor;
}
@NonNull
public FlutterPluginRegistry getPluginRegistry() {
return mPluginRegistry;
}
public void attachViewAndActivity(FlutterView flutterView, Activity activity) {
mFlutterView = flutterView;
mPluginRegistry.attach(flutterView, activity);
}
public boolean isAttached() {
return mFlutterJNI.isAttached();
}
public void assertAttached() {
if (!isAttached()) throw new AssertionError("Platform view is not attached");
}
public void runFromBundle(FlutterRunArguments args) {
boolean hasBundlePaths = args.bundlePaths != null && args.bundlePaths.length != 0;
if (args.bundlePath == null && !hasBundlePaths) {
throw new AssertionError("Either bundlePath or bundlePaths must be specified");
} else if ((args.bundlePath != null || args.defaultPath != null) &&
hasBundlePaths) {
throw new AssertionError("Can't specify both bundlePath and bundlePaths");
} else if (args.entrypoint == null) {
throw new AssertionError("An entrypoint must be specified");
}
if (hasBundlePaths) {
runFromBundleInternal(args.bundlePaths, args.entrypoint, args.libraryPath);
} else {
runFromBundleInternal(new String[] {args.bundlePath, args.defaultPath},
args.entrypoint, args.libraryPath);
}
}
/**
* @deprecated
* Please use runFromBundle with `FlutterRunArguments`.
* Parameter `reuseRuntimeController` has no effect.
*/
@Deprecated
public void runFromBundle(String bundlePath, String defaultPath, String entrypoint,
boolean reuseRuntimeController) {
runFromBundleInternal(new String[] {bundlePath, defaultPath}, entrypoint, null);
}
private void runFromBundleInternal(String[] bundlePaths, String entrypoint,
String libraryPath) {
assertAttached();
if (applicationIsRunning)
throw new AssertionError(
"This Flutter engine instance is already running an application");
mFlutterJNI.runBundleAndSnapshotFromLibrary(
bundlePaths,
entrypoint,
libraryPath,
mContext.getResources().getAssets()
);
applicationIsRunning = true;
}
public boolean isApplicationRunning() {
return applicationIsRunning;
}
public static String getObservatoryUri() {
return FlutterJNI.getObservatoryUri();
}
@Override
@UiThread
public void send(String channel, ByteBuffer message) {
dartExecutor.send(channel, message);
}
@Override
@UiThread
public void send(String channel, ByteBuffer message, BinaryReply callback) {
if (!isAttached()) {
Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel);
return;
}
dartExecutor.send(channel, message, callback);
}
@Override
@UiThread
public void setMessageHandler(String channel, BinaryMessageHandler handler) {
dartExecutor.setMessageHandler(channel, handler);
}
/*package*/ FlutterJNI getFlutterJNI() {
return mFlutterJNI;
}
private void attach(FlutterNativeView view, boolean isBackgroundView) {
mFlutterJNI.attachToNative(isBackgroundView);
dartExecutor.onAttachedToJNI();
}
private final class RenderSurfaceImpl implements RenderSurface {
@Override
public void attachToRenderer(@NonNull FlutterRenderer renderer) {
// Not relevant for v1 embedding.
}
@Override
public void detachFromRenderer() {
// Not relevant for v1 embedding.
}
// 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.
public void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) {
if (mFlutterView == null) {
return;
}
mFlutterView.updateCustomAccessibilityActions(buffer, strings);
}
// Called by native to notify first Flutter frame rendered.
public void onFirstFrameRendered() {
if (mFlutterView == null) {
return;
}
mFlutterView.onFirstFrame();
}
@Override
public void addOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) {}
@Override
public void removeOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) {}
}
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();
}
}
}