最近 Flutter 很火,相信长得帅的人都已经对它都有了初步的了解。
不过因为目前默认使用 Flutter 做为框架接管整个 App 进行开发,不够灵活:一方面使用纯 Flutter 开发须要在项目开始以前仔细评估是否有难以实现的功能;另外一方面现有的 App 想使用 Flutter 的话很难所有转换过去。
很容易想到在现有的 App 的基础上加入 Flutter 做为部分画面/功能的实现是一个理想的方案,也更有利于作技术尝试和风险控制。 实际上目前 Flutter 官方提供了两种方案用于给现有 App 加入 Flutter Module,另外还有一些第三方的方案,最近我作了一些尝试,分享一些成果。
须要注意的是, 给现有 App 引入 Flutter Module 的功能还在实验性的阶段, APIs 和工具链处于未稳定阶段,且须要切换到 master
分支(不稳定)使用。java
假设在 some/path/MyApp
下是 Android 项目目录android
cd some/path
flutter create -t module --org com.example flutter_to_app
复制代码
会在 some/path/flutter_to_app
生成一个 Flutter Moduleshell
须要在app/build.gradle
里设置缓存
android {
//...
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}
复制代码
有两种方案,直接依赖源代码和 aar 产物。bash
cd ~/Documents/Android/flutter_to_app
flutter build aar
复制代码
// MyApp/app/build.gradle
android {
// ...
}
repositories {
maven {
//可使用相对路径或者绝对路径
url 'some/path/flutter_to_app/build/host/outputs/repo'
}
}
dependencies {
// ...
releaseCompile ('com.example. flutter_to_app:flutter_release:1.0@aar') {
transitive = true
}
}
复制代码
能够用 flutter build aar --debug
生成 debug 依赖app
// MyApp/app/build.gradle
dependencies {
// ...
debugCompile ('com.example.my_flutter:flutter_debug:1.0@aar') {
transitive = true
}
}
复制代码
依赖 aar 的方式有点麻烦,还须要到 Module 中编译,因此也能够直接依赖源码编译框架
在宿主 App settings.gradle
加入maven
// MyApp/settings.gradle
include ':app'
...
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'flutter_to_app/.android/include_flutter.groovy'
))
复制代码
上面的File()
路径是 flutter module 相对 host app 的路径。binding 和 include_flutter.groovy
脚本引入 flutter module 自己和相关的 plugin。ide
最后,依赖模块:工具
// MyApp/app/build.gradle
dependencies {
implementation project(':flutter')
}
复制代码
目前有两种方式实现,分别在
io.flutter.facade.*
io.flutter.embedding.android.*
两个包下, 第一种已经被 deprecated ,第二种还处于 technical preview 阶段,因此两种版本的 API 都还不稳定,但能够大概看一下两种方式。
经过使用 Flutter.createView
:
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
View flutterView = Flutter.createView(
MainActivity.this,
getLifecycle(),
"route1"
);
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(600, 800);
layout.leftMargin = 100;
layout.topMargin = 200;
addContentView(flutterView, layout);
}
});
复制代码
经过使用 Flutter.createFragment
:
// MyApp/app/src/main/java/some/package/SomeActivity.java
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.someContainer, Flutter.createFragment("route1"));
tx.commit();
}
});
复制代码
建立View
和Fragment
都很是简单,可是实际测试下来,启动 View (FlutterFragment实际上也是经过 createView 来生成视图的)会有启动时间,体验没那么无缝。
实例化 FlutterView 嵌入 Native
FlutterView flutterView = new FlutterView(this);
FrameLayout frameLayout = findViewById(R.id.framelayout);
frameLayout.addView(flutterView);
//建立一个 FlutterView 就能够了,这个时候还不会渲染。
//调用下面代码后才会渲染
flutterView.attachToFlutterEngine(flutterEngine);
复制代码
<fragment
android:id="@+id/flutterfragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="io.flutter.embedding.android.FlutterFragment"
/>
复制代码
flutterFragment = new FlutterFragment.createDefault();
复制代码
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
android:exported="true"
/>
复制代码
//默认路由为 '/'
Intent defaultFlutter = new FlutterActivity.createDefaultIntent(currentActivity);
startActivity(defaultFlutter);
复制代码
Intent customFlutter = new FlutterActivity.IntentBuilder()
.initialRoute("someOtherRoute")
.build(currentActivity);
startActivity(customFlutter);
复制代码
实际上,经过 API 和源码能够看出,新版的 Flutter 相关类io.flutter.embedding.android.*
彻底从新设计了 Native 调用的方式,从包名(embedding)就能够看出是但愿嵌入 Native, 其中一个重要的变化是加入了 FlutterEngine
的缓存机制。 经过老的方式启动 Flutter 的响应时间长包括了须要启动FlutterEngine
的时间,能够理解为冷启动,并且从原生的不一样Activity / ViewController
启动 Flutter 都须要启动一个新的 FlutterEngine
,因此不只第一次启动 Flutter 时间长 ,每次启动都会须要一样的时间。好比下面的状况
Native A -> Flutter B -> Native C -> Flutter D
这样从Native A
和 Native B
启动时会实例化两个FlutterEngine
。
这样不只慢,对资源的开销也更多。 为了解决这个问题,新的解决方案引入了FlutterEngine
缓存机制。
// 实例化 FlutterEngine.
FlutterEngine flutterEngine = new FlutterEngine(context);
// 预热
flutterEngine
.getDartExecutor()
.executeDartEntrypoint(
DartEntrypoint.createDefault()
);
//放入 FlutterEngineCache
FlutterEngineCache
.getInstance()
.put("my_engine_id", flutterEngine);
//启动 Activity 的时候使用
Intent intent = FlutterActivity
.withCachedEngine("my_engine_id")
.build();
startActivity(intent);
//实例化 Fragment
FlutterFragment flutterFragment = FlutterFragment
.withCachedEngine("my_engine_id")
.build();
复制代码
自行处理存储 FlutterEngine 的地方
public class MyFlutterFragment extends FlutterFragment {
@Override
@Nullable
protected FlutterEngine provideFlutterEngine(@NonNull Context context) {
//自行存储 FlutterEngine 实例
return MyFlutterEngine.getFlutterEngine();
//好比 Application 中
return ((MyApp) context.getApplication).getFlutterEngine();
}
}
复制代码
public class MyFlutterActivity extends FlutterActivity {
@Nullable
@Override
public FlutterEngine provideFlutterEngine(@NonNull Context context) {
FlutterEngine flutterEngine;
//自行存储 FlutterEngine 实例
flutterEngine = MyFlutterEngineCache.getFlutterEngine();
//好比 Application 中
flutterEngine = ((MyApp) getApplication()).getFlutterEngine();
return flutterEngine;
}
}
复制代码
public class MyActivity extends Activity implements FlutterEngineProvider {
@Override
@Nullable
FlutterEngine provideFlutterEngine(@NonNull Context context) {
//自行存储 FlutterEngine 实例
return MyFlutterEngine.getFlutterEngine();
//好比 Application 中
return ((MyApp) context.getApplication).getFlutterEngine();
}
}
复制代码
新一代 Flutter-Native 混合解决方案。 FlutterBoost是一个Flutter插件,它能够轻松地为现有原生应用程序提供Flutter混合集成方案。FlutterBoost的理念是将Flutter像Webview那样来使用。在现有应用程序中同时管理Native页面和Flutter页面并不是易事。 FlutterBoost帮你处理页面的映射和跳转,你只需关心页面的名字和参数便可(一般能够是URL)。
FlutterBoost 是闲鱼开源处理 Flutter-Native 混合开发的解决方案,是一个热门的方案,但和官方方案对比我认为有两个重要的异同点:
最后,官方的两种方案一种已经被舍弃一种还处于实验性阶段,目前最新方案的Milestone
是12月,因此到时候再次评估可行性。而国内大厂基本上各自都有本身的解决方案,因此目前使用官方方案的话还须要仔细评估。