Migrating your plugin to the new Android APIs
如果你不寫或維護一個 Flutter 外掛,您可以跳過這一頁。
從 1.10.17 版本開始,新的外掛 API 在 master 和 dev 通道上可用。 不會立即淘汰舊的 API,但我們建議您遷移到新的 API。 隨著時間的流逝,將 Flutter 嵌入到 Android 應用中時,使用舊 API 的外掛可能會產生奇怪的行為。 flutter.dev 提供的大多數 Flutter 外掛已經被遷移。 (瞭解如何成為 pub.dev 的 verified publisher!)有關使用新 API 的外掛的示例,請參閱 battery package。
遷移步驟
以下說明概述了支援新 API 的步驟:
-
更新主外掛類(* Plugin.java)以實現
FlutterPlugin
[]。 對於更復雜的外掛,您可以將 將FlutterPlugin
和MethodCallHandler
分為兩個類。 見下 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. -
(可選) 如果外掛需要
Activity
的引用,則實現ActivityAware
. -
(可選) 如果您的外掛預計將儲存在後臺服務,實現
ServiceAware
. -
更新 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());
}
}
複製程式碼
- (可選) 使用 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"));
複製程式碼
- 在
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);
}
}
複製程式碼
- 新增
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>
複製程式碼
- 為了使外掛在分支 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"
}
}
}
}
複製程式碼
測試外掛
剩下的步驟涉及測試您的外掛,我們鼓勵,但不是必需的。
- 更新
<plugin_name>/example/android/app/build.gradle
將android.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'
...
}
複製程式碼
- 在
<plugin_name>/example/android/app/src/androidTest/java/<plugin_path>/
中為MainActivity
和EmbeddingV1Activity
新增測試檔案。例如:
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);
}
複製程式碼
- 新增
e2e
和flutter_driver
dev_dependencies 到<plugin_name>/pubspec.yaml
和<plugin_name>/example/pubspec.yaml
.
e2e: ^0.2.1
flutter_driver:
sdk: flutter
複製程式碼
- 在
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());
}
}
複製程式碼
- 在
<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"
複製程式碼
- 在
<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);
});
}
複製程式碼
- 在本地測試執行 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