Flutter 將您的外掛遷移到新的Android API

kassadin發表於2019-12-11

Migrating your plugin to the new Android APIs

翻譯自 flutter.dev/docs/develo…

如果你不寫或維護一個 Flutter 外掛,您可以跳過這一頁。

從 1.10.17 版本開始,新的外掛 API 在 master 和 dev 通道上可用。 不會立即淘汰舊的 API,但我們建議您遷移到新的 API。 隨著時間的流逝,將 Flutter 嵌入到 Android 應用中時,使用舊 API 的外掛可能會產生奇怪的行為。 flutter.dev 提供的大多數 Flutter 外掛已經被遷移。 (瞭解如何成為 pub.dev 的 verified publisher!)有關使用新 API 的外掛的示例,請參閱 battery package

遷移步驟

以下說明概述了支援新 API 的步驟:

  1. 更新主外掛類(* Plugin.java)以實現 FlutterPlugin []。 對於更復雜的外掛,您可以將 將 FlutterPluginMethodCallHandler 分為兩個類。 見下 Basic plugin [] 部分,以獲取有關通過以下方式訪問應用程式資源的更多詳細資訊: embedding 的最新版本(v2)。

    另外,請注意外掛仍應包含靜態的 registerWith() 方法, 與不使用 v2 embedding 的應用程式保持相容。 最簡單的操作(如果可能的話)是將邏輯從 registerWith() 中移出 進入一個私有方法,使 registerWith()onAttachedToEngine 都可以呼叫該方法。
    registerWith()AttachToEngine() 只有一個會被呼叫。

    如果你在 onAttachToEngine() 中建立 channels, 那麼沒必要在 onDetachFromEngine() 中清理這些建立,當 onAttachToEngine() 第二次被呼叫時再次建立他們是可以的。

    此外,您還應該在文件中記錄所有 non-overridden 的公共成員。 在 add-to-app 場景中, these classes will be accessible to a developer and require documentation.

  2. (可選) 如果外掛需要 Activity 的引用,則實現 ActivityAware.

  3. (可選) 如果您的外掛預計將儲存在後臺服務,實現 ServiceAware.

  4. 更新 example app 的 MainActivity.java 來使用 v2 embedding 的 FlutterActivity。您可能需要一個 plugin 的公共建構函式(如果沒有),例如:

package io.flutter.plugins.firebasecoreexample;

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.firebase.core.FirebaseCorePlugin;

public class MainActivity extends FlutterActivity {
  // TODO(<github-username>): Remove this once v2 of
  // GeneratedPluginRegistrant rolls to stable.
  // https://github.com/flutter/flutter/issues/42694
  @Override
  public void configureFlutterEngine(FlutterEngine flutterEngine) {
    flutterEngine.getPlugins().add(new FirebaseCorePlugin());
  }
}
複製程式碼
  1. (可選) 使用 ShimPluginRegistry 還不支援 v2 embedding 的外掛。例如:
ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine);
PathProviderPlugin.registerWith(
        shimPluginRegistry.registrarFor("io.flutter.plugins.pathprovider.PathProviderPlugin"));
VideoPlayerPlugin.registerWith(
        shimPluginRegistry.registrarFor("io.flutter.plugins.videoplayer.VideoPlayerPlugin"));
複製程式碼
  1. MainActivity 同資料夾下建立 EmbeddingV1Activity.java 檔案使用 v1 embedding。例如:
package io.flutter.plugins.firebasecoreexample;

import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class EmbeddingV1Activity extends FlutterActivity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   GeneratedPluginRegistrant.registerWith(this);
 }
}
複製程式碼
  1. 新增 EmbeddingV1Activity 到 <plugin_name>/example/android/app/src/main/AndroidManifest.xml. 例如
<activity
    android:name=".EmbeddingV1Activity"
    android:theme="@style/LaunchTheme"
         android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale"
    android:hardwareAccelerated="true"
    android:windowSoftInputMode="adjustResize">
</activity>
複製程式碼
  1. 為了使外掛在分支 master 和穩定版上支援 Flutter, 將這個 gradle 指令碼新增到 <plugin_name>/android/build.gradle。
// TODO(<github-username>): Remove this hack once androidx
// lifecycle is included on stable.
// https://github.com/flutter/flutter/issues/42348

afterEvaluate {
    def containsEmbeddingDependencies = false
    for (def configuration : configurations.all) {
        for (def dependency : configuration.dependencies) {
            if (dependency.group == 'io.flutter' &&
                    dependency.name.startsWith('flutter_embedding') &&
                    dependency.isTransitive())
            {
                containsEmbeddingDependencies = true
                break
            }
        }
    }
    if (!containsEmbeddingDependencies) {
        android {
            dependencies {
                def lifecycle_version = "1.1.1"
                compileOnly "android.arch.lifecycle:runtime:$lifecycle_version"
                compileOnly "android.arch.lifecycle:common:$lifecycle_version"
                compileOnly "android.arch.lifecycle:common-java8:$lifecycle_version"
            }
        }
    }
}
複製程式碼

測試外掛

剩下的步驟涉及測試您的外掛,我們鼓勵,但不是必需的。

  1. 更新 <plugin_name>/example/android/app/build.gradleandroid.support.test 替換為 androidx.test:
defaultConfig {
   ...
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
  ...
}
複製程式碼
dependencies {
...
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
...
}
複製程式碼
  1. <plugin_name>/example/android/app/src/androidTest/java/<plugin_path>/ 中為 MainActivityEmbeddingV1Activity 新增測試檔案。例如:
package io.flutter.plugins.firebase.core;

import androidx.test.rule.ActivityTestRule;
import dev.flutter.plugins.e2e.FlutterRunner;
import io.flutter.plugins.firebasecoreexample.MainActivity;
import org.junit.Rule;
import org.junit.runner.RunWith;

@RunWith(FlutterRunner.class)
public class MainActivityTest {
  @Rule public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class);
}
複製程式碼
package io.flutter.plugins.firebase.core;

import androidx.test.rule.ActivityTestRule;
import dev.flutter.plugins.e2e.FlutterRunner;
import io.flutter.plugins.firebasecoreexample.EmbeddingV1Activity;
import org.junit.Rule;
import org.junit.runner.RunWith;

@RunWith(FlutterRunner.class)
public class EmbeddingV1ActivityTest {
  @Rule
  public ActivityTestRule<EmbeddingV1Activity> rule =
      new ActivityTestRule<>(EmbeddingV1Activity.class);
}
複製程式碼
  1. 新增 e2eflutter_driver dev_dependencies 到 <plugin_name>/pubspec.yaml<plugin_name>/example/pubspec.yaml.
e2e: ^0.2.1
flutter_driver:
  sdk: flutter
複製程式碼
  1. MainActivity.java 中手動註冊 E2E 外掛 以及示例應用程式使用的任何其他外掛。
package io.flutter.plugins.packageinfoexample;

import dev.flutter.plugins.e2e.E2EPlugin;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.packageinfo.PackageInfoPlugin;

public class MainActivity extends FlutterActivity {
  // TODO(jackson): Remove this once v2 of GeneratedPluginRegistrant
  // rolls to stable.
  // https://github.com/flutter/flutter/issues/42694
  @Override
  public void configureFlutterEngine(FlutterEngine flutterEngine) {
    flutterEngine.getPlugins().add(new PackageInfoPlugin());
    flutterEngine.getPlugins().add(new E2EPlugin());
  }
}
複製程式碼
  1. <plugin_name> /pubspec.yaml 更新環境中的最低 Flutter 版本 所有外掛都在目前 forward 會將最低版本設定為 1.9.1 + hotfix.4,這是我們可以保證支援的最低版本。例如:
environment:
  sdk: ">=2.0.0-dev.28.0 <3.0.0"
  flutter: ">=1.9.1+hotfix.4 <2.0.0"

複製程式碼
  1. <plugin_name>/test/<plugin_name>_e2e.dart 建立一個簡單測試。 為了測試新增了 v2 嵌入支援的 PR,我們正在嘗試測試該外掛的一些非常基本的功能。這是一個冒煙測試,以確保外掛正確註冊新的嵌入器。 例如:
import 'package:flutter_test/flutter_test.dart';
import 'package:battery/battery.dart';
import 'package:e2e/e2e.dart';

void main() {
  E2EWidgetsFlutterBinding.ensureInitialized();

  testWidgets('Can get battery level', (WidgetTester tester) async {
    final Battery battery = Battery();
    final int batteryLevel = await battery.batteryLevel;
    expect(batteryLevel, isNotNull);
  });
}
複製程式碼
  1. 在本地測試執行 e2e 測試,在 terminal 執行下列操作:
cd <plugin_name>/example
flutter build apk
cd android
./gradlew app:connectedAndroidTest -Ptarget=`pwd`/../../test/<plugin_name>_e2e.dart

複製程式碼

基本外掛

要開始實現 Flutter Android 外掛,首先實現FlutterPlugin.

public class MyPlugin implements FlutterPlugin {
  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
    // TODO: your plugin is now attached to a Flutter experience.
  }

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
    // TODO: your plugin is no longer attached to a Flutter experience.
  }
}
複製程式碼

如上所示,您的外掛可能會或可能不會與 在任何給定時間的特定 Flutter 體驗。 您應該注意初始化外掛的行為 在 onAttachedToEngine() 中,然後清除外掛的引用 在 onDetachedFromEngine() 中。

FlutterPluginBinding 給你的外掛提供了幾個重要的引用:

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 a project dependency on Flutter's Android lifecycle package.

UI/Activity 外掛

如果您的外掛需要與使用者介面進行互動, 例如請求許可權或更改 Android UI 鑲邊, 那麼您需要採取其他步驟來定義您的外掛。 您必須實現 ActivityAware 介面。

public class MyPlugin implements FlutterPlugin, ActivityAware {
  //...normal plugin behavior is hidden...

  @Override
  public void onAttachedToActivity(ActivityPluginBinding activityPluginBinding) {
    // TODO: your plugin is now attached to an Activity
  }

  @Override
  public void onDetachedFromActivityForConfigChanges() {
    // TODO: the Activity your plugin was attached to was
    // destroyed to change configuration.
    // This call will be followed by onReattachedToActivityForConfigChanges().
  }

  @Override
  public void onReattachedToActivityForConfigChanges(ActivityPluginBinding activityPluginBinding) {
    // TODO: your plugin is now attached to a new Activity
    // after a configuration change.
  }

  @Override
  public void onDetachedFromActivity() {
    // TODO: your plugin is no longer associated with an Activity.
    // Clean up references.
  }
}
複製程式碼

為了和 Activity 互動,你的 ActivityAware 外掛必須在 4 個階段實施適當的行為。 首先你的外掛被載入到 Activity. 可通過提供的 ActivityPluginBinding 接觸到 Activity 和一堆 callbacks.

由於可以在配置更改期間銷燬 Activity, 在 onDetachedFromActivityForConfigChanges() 您必須清除對指定給定 Activity 的所有引用, 然後在 onReattachedToActivityForConfigChanges() 中重建這些引用。

最後,在onDetachedFromActivity() 中,您的外掛應清理 與 Activity 行為相關的所有參考,然後返回 非 UI 配置。

Service plugin

TODO

ContentProvider plugin

TODO

相關文章