支援新的 Android 的 API

如果您並非親自開發或維護一個 Flutter 的 Android 外掛,您可以跳過本頁面。

自 1.12 版本釋出後, Android 平台已可以使用新的 Android 外掛 API 。基於 PluginRegistry.Registrar 的 API 不會立刻廢棄,但我們鼓勵您向基於 FlutterPlugin 的 API 進行遷移。

相較舊的 API 而言,新版 API 的優點是為生命週期的相關元件提供了更簡潔清晰的存取方式。例如,在使用舊的 PluginRegistry.Registrar.activity() 時,如果 Flutter 尚未附加到任何 activites,可能會返回 null 。

換句話說,在使用舊的 API 進行 Flutter 嵌入 Android 應用時,可能會產生意外的行為。 Flutter 開發團隊提供的大部分 Flutter 外掛 已經完成了遷移。(瞭解如何成為 認證的釋出者)作為參考, battery plus package 已經遷移到新版 API 。

升級步驟

以下的步驟簡要說明了如何支援新版 API :

  1. 在外掛的主類檔案中 (*Plugin.java) 實現 FlutterPlugin 介面。對於稍微複雜的外掛,您可以將 FlutterPluginMethodCallHandler 拆分到不同的類中。如需更多關於如何使用新版 API 獲取資源的內容,請參考下一節 基礎外掛

    同時需要注意的是,外掛仍需保留靜態的 registerWith() 方法,從而適配不相容 v2 版本嵌入的應用。 (檢視 Upgrading pre 1.12 Android projects 獲取更多資訊)

    此外,所有不可覆蓋的公開成員都應該使用文件標註。在嵌入開發的場景下,這些可見內容通常需要包含文件。

  2. (可選)如果您的外掛需要 Activity 的參考,請同時實現 ActivityAware 介面。

  3. (可選)如果您的外掛需要隨時保持一個後臺 Service ,請實現 ServiceAware 介面。

  4. 使用 FlutterActivity 將範例應用中的 MainActivity.java 遷移到 v2 版本嵌入。更多資訊請檢視 Upgrading pre 1.12 Android projects 。如果您的外掛類尚不存在,則必須新增一個公有建構函式。例如:

     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 {
       // You can keep this empty class or remove it. Plugins on the new embedding
       // now automatically registers plugins.
     }
    
  5. (可選)如果您移除了 MainActivity.java,請更新 <plugin_name>/example/android/app/src/main/AndroidManifest.xml 以使用 io.flutter.embedding.android.FlutterActivity。例如:

      <activity android:name="io.flutter.embedding.android.FlutterActivity"
             android:theme="@style/LaunchTheme"
    android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale"
             android:hardwareAccelerated="true"
             android:exported="true"
             android:windowSoftInputMode="adjustResize">
             <meta-data
                 android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
                 android:value="true" />
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
    
  6. (可選)在 MainActivity.java 同級目錄下建立一個 EmbeddingV1Activity.java 檔案,使用 v1 版本嵌入以持續測試您的專案對 v1 版本嵌入的相容性。例如:

     package io.flutter.plugins.batteryexample;
    
     import android.os.Bundle;
     import io.flutter.app.FlutterActivity;
     import io.flutter.plugins.battery.BatteryPlugin;
    
     public class EmbeddingV1Activity extends FlutterActivity {
       @Override
       protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         BatteryPlugin.registerWith(registrarFor("io.flutter.plugins.battery.BatteryPlugin"));
       }
     }
    
  7. <meta-data android:name="flutterEmbedding" android:value="2"/> 新增至 <plugin_name>/example/android/app/src/main/AndroidManifest.xml 。這會讓範例應用使用 v2 版本的嵌入。

  8. (可選)如果上步您建立了 EmbeddingV1Activity ,將 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:exported="true"
         android:windowSoftInputMode="adjustResize">
     </activity>
    

測試您的外掛

剩下的步驟讓您可以測試您的外掛,我們鼓勵您這樣做,但這並不是必需的。

  1. 替換 <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'
     ...
     }
    
  2. <plugin_name>/example/android/app/src/androidTest/java/<plugin_path>/ 路徑下新增針對 MainActivityEmbeddingV1Activity 的測試檔案,並且您需要建立該目錄。例如:

     package io.flutter.plugins.firebase.core;
    
     import androidx.test.rule.ActivityTestRule;
     import io.flutter.plugins.firebasecoreexample.MainActivity;
     import org.junit.Rule;
     import org.junit.runner.RunWith;
    
     @RunWith(FlutterRunner.class)
     public class MainActivityTest {
       // Replace `MainActivity` with `io.flutter.embedding.android.FlutterActivity` if you removed `MainActivity`.
       @Rule public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class);
     }
    

     package io.flutter.plugins.firebase.core;
    
     import androidx.test.rule.ActivityTestRule;
     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);
     }
    
  3. <plugin_name>/pubspec.yaml<plugin_name>/example/pubspec.yaml 中的 dev_dependencies 下新增 e2eflutter_driver

     integration_test:
       sdk: flutter
     flutter_driver:
       sdk: flutter
    
  4. 更新 <plugin_name>/pubspec.yaml 中 Flutter 版本的最低限制。所有已遷移的外掛都將會設定最低版本為我們保證支援的最低版本 1.12.13+hotfix.6。例如:

     environment:
       sdk: ">=2.16.1 <3.0.0"
       flutter: ">=1.17.0"
    
  5. <plugin_name>/test/<plugin_name>_e2e.dart 中建立一個簡單的測試。為了測試添加了 v2 版本嵌入支援的 PR,我們將嘗試測試一些外掛的基礎功能。這是一個確保外掛正確註冊到新的嵌入器的煙霧測試。例如:

    import 'package:flutter_test/flutter_test.dart';
     import 'package:integration_test/integration_test.dart';
    
     void main() {
       IntegrationTestWidgetsFlutterBinding.ensureInitialized();
    
       testWidgets('Can get battery level', (tester) async {
         final Battery battery = Battery();
         final int batteryLevel = await battery.batteryLevel;
         expect(batteryLevel, isNotNull);
       });
     }
  6. 本地執行 e2e 測試。在終端中執行以下內容:

     flutter test integration_test/app_test.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()
返回外掛附加到的 FlutterEngine ,提供了諸如 DartExecutorFlutterRenderer 等內容的獲取。

binding.getApplicationContext()
返回當前執行的安卓應用的 Application Context

UI/Activity 外掛

如果您的外掛需要與 UI 進行互動,例如請求許可權或更改 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 及一些回呼(Callback)。

由於 Activity 有可能在配置變化時被銷燬,您必須在 onDetachedFromActivityForConfigChanges() 方法中清理所有與 Activity 有關的參考,接著在 onReattachedToActivityForConfigChanges() 中重新建立它們。

最後,在 onDetachedFromActivity() 中清理所有與 Activity 有關的參考並返回與 UI 無關的配置。