未验证 提交 816d3fc5 编写于 作者: M Matt Carroll 提交者: GitHub

New Plugin API PR1: Introduces PluginRegistry and FlutterPlugin, adds support...

New Plugin API PR1: Introduces PluginRegistry and FlutterPlugin, adds support for plugin registration to FlutterEngine. (#8826)

上级 21ad7f05
......@@ -547,6 +547,8 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/Flutte
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/FlutterPlugin.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/PluginRegistry.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/embedding/engine/systemchannels/AccessibilityChannel.java
......
......@@ -133,6 +133,8 @@ action("flutter_shell_java") {
"io/flutter/embedding/engine/dart/DartExecutor.java",
"io/flutter/embedding/engine/dart/DartMessenger.java",
"io/flutter/embedding/engine/dart/PlatformMessageHandler.java",
"io/flutter/embedding/engine/plugins/FlutterPlugin.java",
"io/flutter/embedding/engine/plugins/PluginRegistry.java",
"io/flutter/embedding/engine/renderer/FlutterRenderer.java",
"io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java",
"io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java",
......
......@@ -484,7 +484,7 @@ public class FlutterFragment extends Fragment {
// TODO(mattcarroll): the following call should exist here, but the plugin system needs to be revamped.
// The existing attach() method does not know how to handle this kind of FlutterView.
//flutterEngine.getPluginRegistry().attach(this, getActivity());
//flutterEngine.getPlugins().attach(this, getActivity());
doInitialFlutterViewRun();
}
......@@ -587,11 +587,6 @@ public class FlutterFragment extends Fragment {
* @param grantResults permission grants or denials
*/
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (flutterEngine != null) {
flutterEngine.getPluginRegistry().onRequestPermissionsResult(requestCode, permissions, grantResults);
} else {
Log.w(TAG, "onRequestPermissionResult() invoked before FlutterFragment was attached to an Activity.");
}
}
/**
......@@ -603,11 +598,6 @@ public class FlutterFragment extends Fragment {
* @param intent new Intent
*/
public void onNewIntent(@NonNull Intent intent) {
if (flutterEngine != null) {
flutterEngine.getPluginRegistry().onNewIntent(intent);
} else {
Log.w(TAG, "onNewIntent() invoked before FlutterFragment was attached to an Activity.");
}
}
/**
......@@ -619,11 +609,6 @@ public class FlutterFragment extends Fragment {
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (flutterEngine != null) {
flutterEngine.getPluginRegistry().onActivityResult(requestCode, resultCode, data);
} else {
Log.w(TAG, "onActivityResult() invoked before FlutterFragment was attached to an Activity.");
}
}
/**
......@@ -633,11 +618,6 @@ public class FlutterFragment extends Fragment {
* See {@link android.app.Activity#onUserLeaveHint()}
*/
public void onUserLeaveHint() {
if (flutterEngine != null) {
flutterEngine.getPluginRegistry().onUserLeaveHint();
} else {
Log.w(TAG, "onUserLeaveHint() invoked before FlutterFragment was attached to an Activity.");
}
}
/**
......
......@@ -4,11 +4,17 @@
package io.flutter.embedding.engine;
import android.arch.lifecycle.Lifecycle;
import android.content.Context;
import android.support.annotation.NonNull;
import io.flutter.app.FlutterPluginRegistry;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.PluginRegistry;
import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
import io.flutter.embedding.engine.systemchannels.KeyEventChannel;
......@@ -54,7 +60,7 @@ public class FlutterEngine {
@NonNull
private final DartExecutor dartExecutor;
@NonNull
private final FlutterPluginRegistry pluginRegistry;
private final PluginRegistry pluginRegistry;
// System channels.
@NonNull
......@@ -79,7 +85,8 @@ public class FlutterEngine {
private final EngineLifecycleListener engineLifecycleListener = new EngineLifecycleListener() {
@SuppressWarnings("unused")
public void onPreEngineRestart() {
pluginRegistry.onPreEngineRestart();
// TODO(mattcarroll): work into plugin API. should probably loop through each plugin.
// pluginRegistry.onPreEngineRestart();
}
};
......@@ -95,7 +102,7 @@ public class FlutterEngine {
* {@link #getRenderer()} and {@link FlutterRenderer#attachToRenderSurface(FlutterRenderer.RenderSurface)}.
*
* A new {@code FlutterEngine} does not come with any Flutter plugins attached. To attach plugins,
* see {@link #getPluginRegistry()}.
* see {@link #getPlugins()}.
*
* A new {@code FlutterEngine} does come with all default system channels attached.
*/
......@@ -120,7 +127,12 @@ public class FlutterEngine {
systemChannel = new SystemChannel(dartExecutor);
textInputChannel = new TextInputChannel(dartExecutor);
this.pluginRegistry = new FlutterPluginRegistry(this, context);
// TODO(mattcarroll): bring in Lifecycle.
this.pluginRegistry = new FlutterEnginePluginRegistry(
context.getApplicationContext(),
this,
null
);
}
private void attachToJni() {
......@@ -137,18 +149,6 @@ public class FlutterEngine {
return flutterJNI.isAttached();
}
/**
* Detaches this {@code FlutterEngine} from Flutter's native implementation, but allows
* reattachment later.
*
* // TODO(mattcarroll): document use-cases for this behavior.
*/
public void detachFromJni() {
pluginRegistry.detach();
dartExecutor.onDetachedFromJNI();
flutterJNI.removeEngineLifecycleListener(engineLifecycleListener);
}
/**
* Cleans up all components within this {@code FlutterEngine} and then detaches from Flutter's
* native implementation.
......@@ -156,7 +156,7 @@ public class FlutterEngine {
* This {@code FlutterEngine} instance should be discarded after invoking this method.
*/
public void destroy() {
pluginRegistry.destroy();
pluginRegistry.removeAll();
dartExecutor.onDetachedFromJNI();
flutterJNI.removeEngineLifecycleListener(engineLifecycleListener);
flutterJNI.detachFromNativeAndReleaseResources();
......@@ -262,12 +262,71 @@ public class FlutterEngine {
return textInputChannel;
}
// TODO(mattcarroll): propose a robust story for plugin backward compability and future facing API.
/**
* Plugin registry, which registers plugins that want to be applied to this {@code FlutterEngine}.
*/
@NonNull
public FlutterPluginRegistry getPluginRegistry() {
public PluginRegistry getPlugins() {
return pluginRegistry;
}
private static class FlutterEnginePluginRegistry implements PluginRegistry {
private final Map<Class<? extends FlutterPlugin>, FlutterPlugin> plugins = new HashMap<>();
private final FlutterPlugin.FlutterPluginBinding pluginBinding;
FlutterEnginePluginRegistry(
@NonNull Context appContext,
@NonNull FlutterEngine flutterEngine,
@NonNull Lifecycle lifecycle
) {
pluginBinding = new FlutterPlugin.FlutterPluginBinding(
appContext,
flutterEngine,
lifecycle
);
}
public void add(@NonNull FlutterPlugin plugin) {
plugins.put(plugin.getClass(), plugin);
plugin.onAttachedToEngine(pluginBinding);
}
public void add(@NonNull Set<FlutterPlugin> plugins) {
for (FlutterPlugin plugin : plugins) {
add(plugin);
}
}
public boolean has(@NonNull Class<? extends FlutterPlugin> pluginClass) {
return plugins.containsKey(pluginClass);
}
public FlutterPlugin get(@NonNull Class<? extends FlutterPlugin> pluginClass) {
return plugins.get(pluginClass);
}
public void remove(@NonNull Class<? extends FlutterPlugin> pluginClass) {
FlutterPlugin plugin = plugins.get(pluginClass);
if (plugin != null) {
plugin.onDetachedFromEngine(pluginBinding);
plugins.remove(pluginClass);
}
}
public void remove(@NonNull Set<Class<? extends FlutterPlugin>> pluginClasses) {
for (Class<? extends FlutterPlugin> pluginClass : pluginClasses) {
remove(pluginClass);
}
}
public void removeAll() {
for (FlutterPlugin plugin : plugins.values()) {
plugin.onDetachedFromEngine(pluginBinding);
}
plugins.clear();
}
}
/**
* Lifecycle callbacks for Flutter engine lifecycle events.
*/
......
// 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.plugins;
import android.arch.lifecycle.Lifecycle;
import android.content.Context;
import android.support.annotation.NonNull;
import io.flutter.embedding.engine.FlutterEngine;
/**
* Interface to be implemented by all Flutter plugins.
* <p>
* A Flutter plugin allows Flutter developers to interact with a host platform, e.g., Android and
* iOS, via Dart code. It includes platform code, as well as Dart code. A plugin author is
* responsible for setting up an appropriate {@link io.flutter.plugin.common.MethodChannel}
* to communicate between platform code and Dart code.
* <p>
* A Flutter plugin has a lifecycle. First, a developer must add a {@code FlutterPlugin} to an
* instance of {@link FlutterEngine}. To do this, obtain a {@link PluginRegistry} with
* {@link FlutterEngine#getPlugins()}, then call {@link PluginRegistry#add(FlutterPlugin)},
* passing the instance of the Flutter plugin. During the call to
* {@link PluginRegistry#add(FlutterPlugin)}, the {@link FlutterEngine} will invoke
* {@link #onAttachedToEngine(FlutterPluginBinding)} on the given {@code FlutterPlugin}. If the
* {@code FlutterPlugin} is removed from the {@link FlutterEngine} via
* {@link PluginRegistry#remove(Class)}, or if the {@link FlutterEngine} is destroyed, the
* {@link FlutterEngine} will invoke {@link FlutterPlugin#onDetachedFromEngine(FlutterPluginBinding)}
* on the given {@code FlutterPlugin}.
* <p>
* Once a {@code FlutterPlugin} is attached to a {@link FlutterEngine}, the plugin's code is
* permitted to access and invoke methods on resources within the {@link FlutterPluginBinding} that
* the {@link FlutterEngine} gave to the {@code FlutterPlugin} in
* {@link #onAttachedToEngine(FlutterPluginBinding)}. This includes, for example, the application
* {@link Context} for the running app.
* <p>
* The {@link FlutterPluginBinding} provided in {@link #onAttachedToEngine(FlutterPluginBinding)}
* is no longer valid after the execution of {@link #onDetachedFromEngine(FlutterPluginBinding)}.
* Do not access any properties of the {@link FlutterPluginBinding} after the completion of
* {@link #onDetachedFromEngine(FlutterPluginBinding)}.
* <p>
* The Android side of a Flutter plugin can be thought of as applying itself to a {@link FlutterEngine}.
* Use {@link FlutterPluginBinding#getFlutterEngine()} to retrieve the {@link FlutterEngine} that
* the {@code FlutterPlugin} is attached to. To register a
* {@link io.flutter.plugin.common.MethodChannel}, obtain the {@link FlutterEngine}'s
* {@link io.flutter.embedding.engine.dart.DartExecutor} with {@link FlutterEngine#getDartExecutor()},
* then pass the {@link io.flutter.embedding.engine.dart.DartExecutor} to the
* {@link io.flutter.plugin.common.MethodChannel} as a {@link io.flutter.plugin.common.BinaryMessenger}.
* <p>
* An Android Flutter plugin may require access to app resources or other artifacts that can only
* be retrieved through a {@link Context}. Developers can access the application context via
* {@link FlutterPluginBinding#getApplicationContext()}.
* <p>
* TODO(mattcarroll): explain ActivityAware when it's added to the new plugin API surface.
*/
public interface FlutterPlugin {
/**
* This {@code FlutterPlugin} has been associated with a {@link FlutterEngine} instance.
* <p>
* Relevant resources that this {@code FlutterPlugin} may need are provided via the {@code binding}.
* The {@code binding} may be cached and referenced until
* {@link #onDetachedFromEngine(FlutterPluginBinding)} is invoked and returns.
*/
void onAttachedToEngine(@NonNull FlutterPluginBinding binding);
/**
* This {@code FlutterPlugin} has been removed from a {@link FlutterEngine} instance.
* <p>
* The {@code binding} passed to this method is the same instance that was passed in
* {@link #onAttachedToEngine(FlutterPluginBinding)}. It is provided again in this method as a
* convenience. The {@code binding} may be referenced during the execution of this method, but
* it must not be cached or referenced after this method returns.
* <p>
* {@code FlutterPlugin}s should release all resources in this method.
*/
void onDetachedFromEngine(@NonNull FlutterPluginBinding binding);
/**
* Resources made available to all plugins registered with a given {@link FlutterEngine}.
* <p>
* The {@code FlutterPluginBinding}'s {@code flutterEngine} refers to the {@link FlutterEngine}
* that the associated {@code FlutterPlugin} is intended to apply to. For example, if a
* {@code FlutterPlugin} needs to setup a communication channel with its associated Flutter app,
* that can be done by wrapping a {@code MethodChannel} around
* {@link FlutterEngine#getDartExecutor()}.
* <p>
* A {@link FlutterEngine} may move from foreground to background, from an {@code Activity} to
* a {@code Service}, and {@code FlutterPluginBinding}'s {@code lifecycle} generalizes those
* lifecycles so that a {@code FlutterPlugin} can react to lifecycle events without being
* concerned about which Android Component is currently holding the {@link FlutterEngine}.
* TODO(mattcarroll): add info about ActivityAware and ServiceAware for plugins that care.
*/
class FlutterPluginBinding {
private final Context applicationContext;
private final FlutterEngine flutterEngine;
private final Lifecycle lifecycle;
public FlutterPluginBinding(
@NonNull Context applicationContext,
@NonNull FlutterEngine flutterEngine,
@NonNull Lifecycle lifecycle
) {
this.applicationContext = applicationContext;
this.flutterEngine = flutterEngine;
this.lifecycle = lifecycle;
}
@NonNull
public Context getApplicationContext() {
return applicationContext;
}
@NonNull
public FlutterEngine getFlutterEngine() {
return flutterEngine;
}
@NonNull
public Lifecycle getLifecycle() {
return lifecycle;
}
}
}
// 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.plugins;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.Set;
public interface PluginRegistry {
/**
* Attaches the given {@code plugin} to the {@link io.flutter.embedding.engine.FlutterEngine}
* associated with this {@code PluginRegistry}.
*/
void add(@NonNull FlutterPlugin plugin);
/**
* Attaches the given {@code plugins} to the {@link io.flutter.embedding.engine.FlutterEngine}
* associated with this {@code PluginRegistry}.
*/
void add(@NonNull Set<FlutterPlugin> plugins);
/**
* Returns true if a plugin of the given type is currently attached to the
* {@link io.flutter.embedding.engine.FlutterEngine} associated with this {@code PluginRegistry}.
*/
boolean has(@NonNull Class<? extends FlutterPlugin> pluginClass);
/**
* Returns the instance of a plugin that is currently attached to the
* {@link io.flutter.embedding.engine.FlutterEngine} associated with this {@code PluginRegistry},
* which matches the given {@code pluginClass}.
* <p>
* If no matching plugin is found, {@code null} is returned.
*/
@Nullable
FlutterPlugin get(@NonNull Class<? extends FlutterPlugin> pluginClass);
/**
* Detaches the plugin of the given type from the {@link io.flutter.embedding.engine.FlutterEngine}
* associated with this {@code PluginRegistry}.
* <p>
* If no such plugin exists, this method does nothing.
*/
void remove(@NonNull Class<? extends FlutterPlugin> pluginClass);
/**
* Detaches the plugins of the given types from the {@link io.flutter.embedding.engine.FlutterEngine}
* associated with this {@code PluginRegistry}.
* <p>
* If no such plugins exist, this method does nothing.
*/
void remove(@NonNull Set<Class<? extends FlutterPlugin>> plugins);
/**
* Detaches all plugins that are currently attached to the
* {@link io.flutter.embedding.engine.FlutterEngine} associated with this {@code PluginRegistry}.
* <p>
* If no plugins are currently attached, this method does nothing.
*/
void removeAll();
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册