Android 实战: 使用腾讯shadow实现flutter插件化

1,102 阅读3分钟

效果图 220720 161231.gif

下载 shadow 项目

为了方便 , 直接使用shadow官方的例子做测试 , 把 shadow 项目 clone 下来并编译 编译成功的shadow 项目如下 image.png 可以看到有很多个项目 , 但我们只关注 sample-app , sample-host就行了

编译flutter 项目

这里为了简单 , 使用之前写的futter数独作为测试 我之前写的 flutter 数独源码在这里 flutter_sudoku 项目编译成功后 , 在build/intermediates/flutter文件夹中可以看到如下编译产物 image.png

  • flutter_assets : 项目资源文件
  • libapp.so : 项目文件在这个so里
  • libflutter.so : flutter 引擎相关的都在这个so

生成包含flutter的插件apk

把这些flutter 编译产物复制到 sample-app 中 image.png sample-app 依赖flutter 给 Android提供的中间层flutter_embedding sdk

  implementation 'io.flutter:flutter_embedding_release:1.0.0-caaafc5604ee9172293eb84a381be6aadd660317'

然后在shadow sample-app 中 添加flutter 测试案例

为了方便起见可以把shadow官方例子中所有的测试案例都注释掉 , 只保留flutter 测试案例 新建一个Activity 继承自FlutterActivity

public class TestFlutterActivity extends FlutterActivity {

    public static class Case extends UseCase {
        @Override
        public String getName() {
            return "flutter 插件";
        }

        @Override
        public String getSummary() {
            return "测试 flutter 插件";
        }

        @Override
        public Class getPageClass() {
            return TestFlutterActivity.class;
        }
    }

}

点击构建sample-app 项目 会看到sample-app编译成功后会生成两个插件包 , 这两个包含flutter的插件包会提供给sample-host用做动态加载 image.png 把这两个插件包复制到宿主的assets 目录下

def createDuplicateApkTask(buildType) {
    def apkDir = file("${getBuildDir()}/outputs/apk/plugin/$buildType")

    return tasks.create("duplicatePlugin${buildType.capitalize()}ApkTask", Copy) {
        group = 'build'
        description = "复制一个sample-app-plugin-${buildType}.apk用于测试目的"
        from(apkDir) {
            include("sample-app-plugin-${buildType}.apk")
            rename { "sample-app-plugin-${buildType}2.apk" }
        }
        into(apkDir)

    }.dependsOn(":sample-app:assemblePlugin${buildType.capitalize()}")
}

image.png 运行sample-host 项目 , 可以看到一切正常 220720 161231.gif

遇到的问题

使用futter数独作为测试没发现问题 , 但是使用 flutter_wanandroid 做测试的时候, 出现了以下报错 image.png

Tried to automatically register plugins with FlutterEngine (io.flutter.embedding.engine.FlutterEngine@449c98d) but could not find or invoke the GeneratedPluginRegistrant.

image.png

Caused by: java.lang.ClassNotFoundException: Didn't find class "io.flutter.plugins.GeneratedPluginRegistrant"

GeneratedPluginRegistrant这个类干什么的 ? ,怎么会报找不到这个类GeneratedPluginRegistrant 打开flutter_wanandroid 项目 , 在java/io/flutter/plugins 目录下可以看到GeneratedPluginRegistrant 这个类 , 这个类是flutter为我们自动生成的 , 主要作用是为flutterEngine添加一些给android层做适配的代码

public final class GeneratedPluginRegistrant {
  private static final String TAG = "GeneratedPluginRegistrant";
  public static void registerWith(@NonNull FlutterEngine flutterEngine) {
    try {
      flutterEngine.getPlugins().add(new dev.fluttercommunity.plus.connectivity.ConnectivityPlugin());
    } catch(Exception e) {
      Log.e(TAG, "Error registering plugin connectivity_plus, dev.fluttercommunity.plus.connectivity.ConnectivityPlugin", e);
    }
    try {
      flutterEngine.getPlugins().add(new com.ajinasokan.flutterdisplaymode.DisplayModePlugin());
    } catch(Exception e) {
      Log.e(TAG, "Error registering plugin flutter_displaymode, com.ajinasokan.flutterdisplaymode.DisplayModePlugin", e);
    }
    try {
      flutterEngine.getPlugins().add(new com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin());
    } catch(Exception e) {
      Log.e(TAG, "Error registering plugin flutter_inappwebview, com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin", e);
    }
    try {
      flutterEngine.getPlugins().add(new io.github.ponnamkarthik.toast.fluttertoast.FlutterToastPlugin());
    } catch(Exception e) {
      Log.e(TAG, "Error registering plugin fluttertoast, io.github.ponnamkarthik.toast.fluttertoast.FlutterToastPlugin", e);
    }
    try {
      flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin());
    } catch(Exception e) {
      Log.e(TAG, "Error registering plugin shared_preferences_android, io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin", e);
    }
    try {
      flutterEngine.getPlugins().add(new com.tekartik.sqflite.SqflitePlugin());
    } catch(Exception e) {
      Log.e(TAG, "Error registering plugin sqflite, com.tekartik.sqflite.SqflitePlugin", e);
    }
  }
}

如果flutter想要以插件形式提供给宿主 , GeneratedPluginRegistrant这个类我们肯定没有 , 肯定要自己生成然后添加到flutter插件包中 有没有解决办法 , 当然是有的 , 只不过非常麻烦 ,因为项目中用到的插件 , flutter并没有给我们提供远程依赖的aar , 只能自己把如下图的 android 层 以aar 集成到我们的插件包中 , 然后自己手动生成GeneratedPluginRegistrant , 接着把DisplayModePlugin / ConnectivityPlugin /InAppWebViewFlutterPlugin等插件都添加到flutterEngine中

image.png 这个只是种思路 , 待测试