支持新的 Android 的 API

备忘 You might be directed to this page if the framework detects that your app uses a plugin based on the old Android APIs.

If you don’t write or maintain a Flutter plugin,you can skip this page.

As of the 1.10.17 release, new plugin APIs are available on themaster and dev channels. The old APIs won’t be immediatelydeprecated, but we encourage you to migrate to the new APIs.Over time, plugins using the old APIs might produce strangebehaviors when embedding Flutter into an Android app.Most of the Flutter plugins provided by flutter.dev havebeen migrated already. (Learn how to become a[verified publisher][] on pub.dev!) For an example ofa plugin that uses the new APIs, see thebattery package.

Upgrade steps

The following instructions outline the steps for supporting the new API:

  • Update the main plugin class (*Plugin.java) to implementFlutterPlugin. For more complex plugins, you can separate theFlutterPlugin and MethodCallHandler into two classes. See the nextsection, Basic plugin, for more details on accessing app resources withthe latest version (v2) of embedding.Also, note that the plugin should still contain the static registerWith()method to remain compatible with apps that don’t use the v2 embedding. Theeasiest thing to do (if possible) is move the logic from registerWith()into a private method that both registerWith() and onAttachedToEngine()can call. Either registerWith() or onAttachToEngine() will be called, notboth.If you are creating channels in your onAttachToEngine(), there is no needto cleanup those creations in onDetachFromEngine(), and creating them againthe second time onAttachToEngine() is called is fine.In addition, you should document all non-overridden public members within theplugin. In an add-to-app scenario, these classes will be accessible to adeveloper and require documentation.

  • (Optional) If your plugin needs an Activity reference,also implement ActivityAware.

  • (Optional) If your plugin is expected to be held in abackground Service at any point in time,implement ServiceAware.

  • Update the example app’s MainActivity.java to use thev2 embedding FlutterActivity. You may have to make apublic constructor for you plugin class if one didn’texist already. For example:

  1. package io.flutter.plugins.firebasecoreexample;
  2. import io.flutter.embedding.android.FlutterActivity;
  3. import io.flutter.embedding.engine.FlutterEngine;
  4. import io.flutter.plugins.firebase.core.FirebaseCorePlugin;
  5. public class MainActivity extends FlutterActivity {
  6. // TODO(<github-username>): Remove this once v2 of
  7. // GeneratedPluginRegistrant rolls to stable.
  8. // https://github.com/flutter/flutter/issues/42694
  9. @Override
  10. public void configureFlutterEngine(FlutterEngine flutterEngine) {
  11. flutterEngine.getPlugins().add(new FirebaseCorePlugin());
  12. }
  13. }
  • (Optional) Use ShimPluginRegistry to add plugins that don’t yetsupport the v2 embedding. For example:
  1. ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine);
  2. PathProviderPlugin.registerWith(
  3. shimPluginRegistry.registrarFor("io.flutter.plugins.pathprovider.PathProviderPlugin"));
  4. VideoPlayerPlugin.registerWith(
  5. shimPluginRegistry.registrarFor("io.flutter.plugins.videoplayer.VideoPlayerPlugin"));
  • Create an EmbeddingV1Activity.java file that uses the v1embedding in the same folder as MainActivity. For example:
  1. package io.flutter.plugins.firebasecoreexample;
  2. import android.os.Bundle;
  3. import io.flutter.app.FlutterActivity;
  4. import io.flutter.plugins.GeneratedPluginRegistrant;
  5. public class EmbeddingV1Activity extends FlutterActivity {
  6. @Override
  7. protected void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. GeneratedPluginRegistrant.registerWith(this);
  10. }
  11. }
  • Add the EmbeddingV1Activity to the /example/android/app/src/main/AndroidManifest.xml.For example:
  1. <activity
  2. android:name=".EmbeddingV1Activity"
  3. android:theme="@style/LaunchTheme"
  4. android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale"
  5. android:hardwareAccelerated="true"
  6. android:windowSoftInputMode="adjustResize">
  7. </activity>
  • To have the plugin support Flutter on branches master and stable, include this gradle script in /android/build.gradle.
  1. // TODO(<github-username>): Remove this hack once androidx
  2. // lifecycle is included on stable.
  3. // https://github.com/flutter/flutter/issues/42348
  4. afterEvaluate {
  5. def containsEmbeddingDependencies = false
  6. for (def configuration : configurations.all) {
  7. for (def dependency : configuration.dependencies) {
  8. if (dependency.group == 'io.flutter' &&
  9. dependency.name.startsWith('flutter_embedding') &&
  10. dependency.isTransitive())
  11. {
  12. containsEmbeddingDependencies = true
  13. break
  14. }
  15. }
  16. }
  17. if (!containsEmbeddingDependencies) {
  18. android {
  19. dependencies {
  20. def lifecycle_version = "1.1.1"
  21. compileOnly "android.arch.lifecycle:runtime:$lifecycle_version"
  22. compileOnly "android.arch.lifecycle:common:$lifecycle_version"
  23. compileOnly "android.arch.lifecycle:common-java8:$lifecycle_version"
  24. }
  25. }
  26. }
  27. }

Testing your plugin

The remaining steps address testing your plugin, which we encourage,but aren’t required.

  • Update <plugin_name>/example/android/app/build.gradleto replace references to android.support.test with androidx.test:
  1. defaultConfig {
  2. ...
  3. testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
  4. ...
  5. }
  1. dependencies {
  2. ...
  3. androidTestImplementation 'androidx.test:runner:1.2.0'
  4. androidTestImplementation 'androidx.test:rules:1.2.0'
  5. androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
  6. ...
  7. }
  • Add tests files for MainActivity and EmbeddingV1Activityin <plugin_name>/example/android/app/src/androidTest/java/<plugin_path>/.You will need to create these directories. For example:
  1. package io.flutter.plugins.firebase.core;
  2. import androidx.test.rule.ActivityTestRule;
  3. import dev.flutter.plugins.e2e.FlutterRunner;
  4. import io.flutter.plugins.firebasecoreexample.MainActivity;
  5. import org.junit.Rule;
  6. import org.junit.runner.RunWith;
  7. @RunWith(FlutterRunner.class)
  8. public class MainActivityTest {
  9. @Rule public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class);
  10. }
  1. package io.flutter.plugins.firebase.core;
  2. import androidx.test.rule.ActivityTestRule;
  3. import dev.flutter.plugins.e2e.FlutterRunner;
  4. import io.flutter.plugins.firebasecoreexample.EmbeddingV1Activity;
  5. import org.junit.Rule;
  6. import org.junit.runner.RunWith;
  7. @RunWith(FlutterRunner.class)
  8. public class EmbeddingV1ActivityTest {
  9. @Rule
  10. public ActivityTestRule<EmbeddingV1Activity> rule =
  11. new ActivityTestRule<>(EmbeddingV1Activity.class);
  12. }
  • Add e2e and flutter_driver dev_dependencies to<plugin_name>/pubspec.yaml and<plugin_name>/example/pubspec.yaml.
  1. e2e: ^0.2.1
  2. flutter_driver:
  3. sdk: flutter
  • Manually register the E2E plugin in MainActivity.javaalongside any other plugins used by the example app.
  1. package io.flutter.plugins.packageinfoexample;
  2. import dev.flutter.plugins.e2e.E2EPlugin;
  3. import io.flutter.embedding.android.FlutterActivity;
  4. import io.flutter.embedding.engine.FlutterEngine;
  5. import io.flutter.plugins.packageinfo.PackageInfoPlugin;
  6. public class MainActivity extends FlutterActivity {
  7. // TODO(jackson): Remove this once v2 of GeneratedPluginRegistrant
  8. // rolls to stable.
  9. // https://github.com/flutter/flutter/issues/42694
  10. @Override
  11. public void configureFlutterEngine(FlutterEngine flutterEngine) {
  12. flutterEngine.getPlugins().add(new PackageInfoPlugin());
  13. flutterEngine.getPlugins().add(new E2EPlugin());
  14. }
  15. }
  • Update minimum Flutter version of environment in<plugin_name>/pubspec.yaml. All plugins movingforward will set the minimum version to 1.9.1+hotfix.4which is the minimum version for which we can guarantee support.For example:
  1. environment:
  2. sdk: ">=2.0.0-dev.28.0 <3.0.0"
  3. flutter: ">=1.9.1+hotfix.4 <2.0.0"
  • Create a simple test in <plugin_name>/test/<plugin_name>_e2e.dart.For the purpose of testing the PR that adds the v2 embedding support,we’re trying to test some very basic functionality of the plugin.This is a smoke test to ensure that the plugin properly registerswith the new embedder. For example:
  1. import 'package:flutter_test/flutter_test.dart';
  2. import 'package:battery/battery.dart';
  3. import 'package:e2e/e2e.dart';
  4. void main() {
  5. E2EWidgetsFlutterBinding.ensureInitialized();
  6. testWidgets('Can get battery level', (WidgetTester tester) async {
  7. final Battery battery = Battery();
  8. final int batteryLevel = await battery.batteryLevel;
  9. expect(batteryLevel, isNotNull);
  10. });
  11. }
  • Test run the e2e tests locally. In a terminal,do the following:
  1. cd <plugin_name>/example
  2. flutter build apk
  3. cd android
  4. ./gradlew app:connectedAndroidTest -Ptarget=`pwd`/../../test/<plugin_name>_e2e.dart

Basic plugin

To get started with a Flutter Android plugin in code,start by implementing FlutterPlugin.

  1. public class MyPlugin implements FlutterPlugin {
  2. @Override
  3. public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
  4. // TODO: your plugin is now attached to a Flutter experience.
  5. }
  6. @Override
  7. public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
  8. // TODO: your plugin is no longer attached to a Flutter experience.
  9. }
  10. }

As shown above, your plugin may or may not be associated witha given Flutter experience at any given moment in time.You should take care to initialize your plugin’s behaviorin onAttachedToEngine(), and then cleanup your plugin’s referencesin onDetachedFromEngine().

The FlutterPluginBinding provides your plugin with a fewimportant references:

  • binding.getFlutterEngine()
  • Returns the FlutterEngine that your plugin is attached to,providing access to components like the DartExecutor,FlutterRenderer, and more.
  • binding.getApplicationContext()
  • Returns the Android application’s Context for the running app.
  • binding.getLifecycle()
  • Returns a reference that can be used to obtain a Lifecycle object.If you need to use this lifecycle reference then you need add aproject dependency on Flutter’s Android lifecycle package.

UI/Activity plugin

If your plugin needs to interact with the UI,such as requesting permissions, or altering Android UI chrome,then you need to take additional steps to define your plugin.You must implement the ActivityAware interface.

  1. public class MyPlugin implements FlutterPlugin, ActivityAware {
  2. //...normal plugin behavior is hidden...
  3. @Override
  4. public void onAttachedToActivity(ActivityPluginBinding activityPluginBinding) {
  5. // TODO: your plugin is now attached to an Activity
  6. }
  7. @Override
  8. public void onDetachedFromActivityForConfigChanges() {
  9. // TODO: the Activity your plugin was attached to was
  10. // destroyed to change configuration.
  11. // This call will be followed by onReattachedToActivityForConfigChanges().
  12. }
  13. @Override
  14. public void onReattachedToActivityForConfigChanges(ActivityPluginBinding activityPluginBinding) {
  15. // TODO: your plugin is now attached to a new Activity
  16. // after a configuration change.
  17. }
  18. @Override
  19. public void onDetachedFromActivity() {
  20. // TODO: your plugin is no longer associated with an Activity.
  21. // Clean up references.
  22. }
  23. }

To interact with an Activity, your ActivityAware plugin mustimplement appropriate behavior at 4 stages. First, your pluginis attached to an Activity. You can access that Activity anda number of its callbacks through the provided ActivityPluginBinding.

Since Activitys can be destroyed during configuration changes,you must cleanup any references to the given Activity inonDetachedFromActivityForConfigChanges(),and then re-establish those references inonReattachedToActivityForConfigChanges().

Finally, in onDetachedFromActivity() your plugin should cleanup all references related to Activity behavior and return toa non-UI configuration.

Service plugin

TODO

ContentProvider plugin

TODO

[verified publisher]: {“api”=>”https://api.dart.dev”, “sdk”=>{“channel”=>”stable”}}/tools/pub/verified-publishers