Android原生嵌入Flutter模块
Posted 白玉梁
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android原生嵌入Flutter模块相关的知识,希望对你有一定的参考价值。
读这篇文章的前提是,你对Flutter已经有一定了解,或者已经达到会开发Flutter的程度,并且Flutter相关环境已经配置OK!
本文所展示项目的运行环境:
FlutterSDK版本:flutter_windows_2.10.1-stable
androidStudio版本:Bumblebee | 2021.1
AndroidSDK版本:31
Gradle版本:7.2
第一步:在你的安卓原生项目父级目录下),执行命令:
flutter create -t module --org com.example my_flutter
此时会创建出一个包名为com.example,项目名为my_flutter的flutter项目:
注意,flutter项目跟原有项目是同级目录
. 开头的文件或文件夹里的内容,都尽量不要修改,!
第二步:打开原生项目的setting.gradle,在include ':app’下方添加如下代码:
setBinding(new Binding([gradle: this])) // new
evaluate(new File( // new
settingsDir.parentFile, // new
'my_flutter/.android/include_flutter.groovy' // new
))
第三步:打开原生项目app下的build.gradle,添加如下代码:
dependencies
//...
implementation project(':flutter')
步骤很简单,但当编译时会出现各种问题,如果你遇到一下几种问题,可以找到对应解决办法:
1.如果遇到Android SDK Command-line Tools相关问题,可以在SDK管理器中安装:
2.如果遇到android-licenses相关问题,可以执行命令:
flutter doctor --android-licenses
提示Y/N时,一路选择Y即可!
3.如果遇到:Failed to apply plugin class ‘FlutterPlugin’:
打开setting.gradle,将repositoriesMode改为:RepositoriesMode.PREFER_PROJECT
4.如果遇到引用的第三方库解析失败:
打开setting.gradle,添加maven:
dependencyResolutionManagement
repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
repositories
maven url 'https://jitpack.io'
maven url 'https://maven.aliyun.com/nexus/content/groups/public/'
maven url 'https://maven.aliyun.com/repository/central'
maven url 'https://maven.aliyun.com/nexus/content/repositories/google'
google()
mavenCentral()
打开原生跟目录下的build.gradle,添加maven:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript
repositories
maven url 'https://jitpack.io'
maven url 'https://maven.aliyun.com/nexus/content/groups/public/'
maven url 'https://maven.aliyun.com/repository/central'
maven url 'https://maven.aliyun.com/nexus/content/repositories/google'
google()
mavenCentral()
dependencies
classpath 'com.android.tools.build:gradle:7.1.1'
allprojects
repositories
maven url 'https://jitpack.io'
maven url 'https://maven.aliyun.com/nexus/content/groups/public/'
maven url 'https://maven.aliyun.com/repository/central'
maven url 'https://maven.aliyun.com/nexus/content/repositories/google'
google()
mavenCentral()
task clean(type: Delete)
delete rootProject.buildDir
编译通过后,项目会变成这样:
爆红处可以不用理会,只要运行成功即可!
小提示:
flutter clean命令,可清除生成的.android、.ios、.packages、.dart_tool文件夹;
flutter packages get命令,可重新生成以上文件;
在项目因某些原因运行出错但又找不到其他原因时,可尝试使用上述步骤解决!
打开my_flutter的lib,就可以开发flutter项目页面了!
从原生跳往Flutter页面有多种途径,首先,需要在原生项目的清单文件中声明FlutterActivity:
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize" />
在原生页面中,通过startActivity执行跳转:
startActivity(
FlutterActivity.createDefaultIntent(context)
);
这是最简单最直接的跳转方式,执行后会直接通过Flutter项目的main入口进入Flutter页面,但其有一个大问题是,进入Flutter页面,就需要先加载Flutter引擎,这就导致跳转过程非常慢,会明显感觉到卡顿,这里依照官方提供的解决方案,我们可以在Application 中预先加载一个引擎缓存起来:
public class MyApplication extends Application
@Override
public void onCreate()
super.onCreate();
FlutterEngine flutterEngine = new FlutterEngine(this);
flutterEngine.getDartExecutor().executeDartEntrypoint(
DartEntrypoint.createDefault()
);
FlutterEngineCache
.getInstance()
.put("my_engine_id", flutterEngine);
然后跳转时使用已缓存的引擎:
startActivity(
FlutterActivity
.withCachedEngine("my_engine_id")
.build(currentActivity)
);
此时,跳转将会变的非常流畅,跟原生跳转无差别!
以上步骤其实已经实现了原生+flutter混合开发的初步逻辑了,但我们知道,实际情况下,我们可能会使用Flutter做多个不同的模块,那不同模块的首页必然也不相同,以上方法则默认通过flutter的main函数进入一个默认的flutter主页,无法满足需求!那该怎么办呢,别急,官方提供了通过路由跳转的方法:
startActivity(
FlutterActivity
.withNewEngine()
.initialRoute("test")
.build(currentActivity)
);
此时我们可以在flutter的main.dart中修改main方法如下:
//通过路由启动页面
void main() => runApp(_widgetForRoute(window.defaultRouteName));
Widget _widgetForRoute(String route)
switch (route)
case 'test1':
return const Test1();
case 'test2':
return const Test2();
case 'test3':
return const Test3();
default:
return const Test1();
Test1…是我创建的不同Flutter页面!
此时,你就可以通过initialRoute方法传入不同的路由,跳转到不同的页面!
//使用新Flutter引擎
binding.btnRoute.setOnClickListener(view ->
String[] routes = new String[]"test1", "test2", "test3";
new AlertDialog.Builder(this)
.setItems(routes, (dialogInterface, i) ->
startActivity(FlutterActivity
.withNewEngine()
.initialRoute(routes[i])
.build(MainActivity.this));
)
.create()
.show();
);
效果如下:
我们发现,跳转过程明显卡顿,这是应为跳转时使用了新的引擎withNewEngine(),那我们是否可以使用已缓存的引擎,去通过路由跳转呢?可以,但不能像使用withNewEngine一样使用withCachedEngine跳转路由,因为withCachedEngine没有initialRoute方法!(关于为什么已缓存的引擎无法在设置初始路由的原因,可以参考官方文档)
Flutter给了我们解决方案,就是在创建引擎时设置初始路由:
flutterEngine = new FlutterEngine(this);
flutterEngine.getNavigationChannel().setInitialRoute("your/route/here");
flutterEngine.getDartExecutor().executeDartEntrypoint(
DartEntrypoint.createDefault()
);
FlutterEngineCache
.getInstance()
.put("my_engine_id", flutterEngine);
然后我们就可以像上面通过window.defaultRouteName接收路由的方式一样,跳转对应的flutter页面!
当然,我们还有另外一种思路,即我可以根据Flutter的功能模块,创建多个对应的FlutterEngine,每个FlutterEngine设置不同的入口函数进入不同的Flutter界面:
FlutterEngine flutterEngine1 = new FlutterEngine(this);
String pathToBundle = FlutterInjector.instance().flutterLoader().findAppBundlePath();
flutterEngine1.getDartExecutor().executeDartEntrypoint(new DartExecutor.DartEntrypoint(pathToBundle, "test1"));
FlutterEngineCache.getInstance().put("test1_engine", flutterEngine1);
FlutterEngine flutterEngine2 = new FlutterEngine(this);
flutterEngine2.getDartExecutor().executeDartEntrypoint(new DartExecutor.DartEntrypoint(pathToBundle, "test2"));
FlutterEngineCache.getInstance().put("test2_engine", flutterEngine2);
FlutterEngine flutterEngine3 = new FlutterEngine(this);
flutterEngine3.getDartExecutor().executeDartEntrypoint(new DartExecutor.DartEntrypoint(pathToBundle, "test3"));
FlutterEngineCache.getInstance().put("test3_engine", flutterEngine3);
Flutter的main.dart中:
//通过设置不同启动入口启动页面
void test1() => runApp(const Test1());
void test2() => runApp(const Test2());
void test3() => runApp(const Test3());
通过withCachedEngine执行跳转:
//使用已缓存的Flutter引擎
binding.btn1.setOnClickListener(view ->
startActivity(FlutterActivity
.withCachedEngine("test1_engine")
.build(MainActivity.this));
);
binding.btn2.setOnClickListener(view ->
startActivity(FlutterActivity
.withCachedEngine("test2_engine")
.build(MainActivity.this));
);
binding.btn3.setOnClickListener(view ->
startActivity(FlutterActivity
.withCachedEngine("test3_engine")
.build(MainActivity.this));
);
效果如下:
如图,还是非常流畅的!
当然,还有其他方式如FlutterFragment,FlutterView等,具体可以参考官方文档:
https://flutter.cn/docs/development/add-to-app/android/project-setup
打包release注意事项:
在实际打包release过程中发现了若干问题:
- Flutter 当前仅支持 为 x86_64,armeabi-v7a 和 arm64-v8a 构建预编(AOT)的库;
- 如果集成的有umeng多渠道,会导致打包后安装直接闪退;
- 使用上面介绍的最后一种多引擎不同入口方式,会导致启动flutter界面黑屏;
第一个问题就不说了;
第二个问题目前只能删除AndroidManifest中的umeng的meta-data,同时删除build.gradle中多渠道配置的代码,然后在初始化UMConfigure时手动修改渠道名(稍显麻烦但没办法);
第三个问题,debug模式下没问题,但release就不行,目前不清楚原因,所以可以使用在创建引擎缓存时设置初始路由;
以上是关于Android原生嵌入Flutter模块的主要内容,如果未能解决你的问题,请参考以下文章
Flutter 专题58 图解 Flutter 嵌入原生 AndroidView 小尝试 #yyds干货盘点#
Flutter(六)Android与Flutter混合开发(Hybird)