Flutter是Google用以帮助开发者在Ios和Android两个平台开发高质量原生应用的全新移动UI框架java
优点:android
具体介绍可参考:你好,Fluttergit
如不作特殊说明,文中所操做的工程都是
Module
工程。github
在开始接入以前,先在Android Studio下载Flutter、Dart两个插件,插件安装完成以后,新建一个Flutter工程。swift
Flutter工程有四种类型选择,开发主要选择 Flutter Application 和 Flutter Module。api
Flutter
子模块能够做为其余工程的依赖。android
来保存android代码,后者使用.android
来保存,且后者的.android
文件夹是隐藏的。android
文件夹内部,前者只有app
一个工程,在新建的时候能够选择是否支持kotlin
或者swift
,后者有两个工程app
和Flutter
,Flutter
做为子模块被app
所依赖。android {
.......
}
// 在编译过程当中产生的中间产物将会存放在该文件夹下
buildDir = new File(rootProject.projectDir, "../build/host")
dependencies {
implementation project(':flutter')
......
}
复制代码
主工程 app
依赖了子模块 :flutter
,可是咱们在工程中并无使用以flutter
命名的子模块。bash
def localProperties = new Properties()
def localPropertiesFile = new File(buildscript.sourceFile.parentFile.parentFile, 'local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
复制代码
在初次点开该 gradle 文件时会在
GradleException
处标红,不用解决,不影响正常编译。架构
这边主要是从local.properties
获取一些配置数据,如 flutter sdk 位置等等。app
apply plugin: 'com.android.library'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 28
defaultConfig {
......
}
}
flutter {
source '../..'
}
dependencies {
......
}
复制代码
在第二行经过apply from 引入了 flutter.gradle
文件,其主要做用是为 Flutter
模块引入 flutter 相关依赖,.so文件等等。框架
注意:flutter 模块要求 compileSdkVersion >= 28,对于不少使用 kotlin 代码且工程 sdk 低于 28 的可能有毁灭性的打击,会报 onCreate() override nothing等类型不匹配的错误,须要一一手动修改错误。
flutter
结构体主要表示flutter源码的位置,../..
的意思就是Flutter
文件夹下的代码就是源码。
include ':app'
rootProject.name = 'android_generated'
setBinding(new Binding([gradle: this]))
evaluate(new File(settingsDir, 'include_flutter.groovy'))
复制代码
打开文件后,Binding类也会报错,此时不要为其 import ,否则会报错,报红不影响编译。
这几行代码的意思就是说,将 Flutter 模块引入到Android工程中。Flutter 模块并无显示地使用 include ':flutter'
,那为何 Flutter 模块会以 :flutter
这样的形式被依赖呢?带着问题,咱们看看include_flutter.groovy
。
gradle.include ':flutter'
gradle.project(':flutter').projectDir
= new File(flutterProjectRoot, '.android/Flutter')
复制代码
能够看到,文件内仍然是经过 include ':flutter'
语法引入到 Android 工程内,同时为其指定模块位置为 Flutter 文件夹
。
if (System.getProperty('build-plugins-as-aars') != 'true') {
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot, '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.toPath().resolve(path).resolve('android').toFile()
gradle.include ":$name"
gradle.project(":$name").projectDir = pluginDirectory
}
}
复制代码
这边是调用 flutter build aar
指令(仅在 flutter module 工程下可用)编译输出 aar 文件时会被调用的,其目的就是将 flutter 工程用到的第三方组件打到输出到 aar 中方便 Native 工程引入。
flutterRoot 是 flutter sdk 所在的位置。具体内容能够查看 揭开Flutter工程编译的面纱(Android篇)。
目前 Native 工程接入 Flutter 工程有两种方式:
- 将上文提到的
Flutter
模块做为依赖引入到 Native- 以 aar 形式引入到 Native 工程
该方式集成是最简单轻松的。native 工程名称为 FlutterNativeProject
,flutter 工程名称为 flutter_module_project
将 flutter module 工程整个拷入到 native 工程中。(本文只是从 git 管理的角度放在 native 工程下,固然也能够在其余位置,只需在配置文件配置)
include ':app'
rootProject.name='FlutterNativeProject'
rootProject.name = 'android_generated'
setBinding(new Binding([gradle: this]))
evaluate(new File(settingsDir,
'/flutter_module_project/.android/include_flutter.groovy'))
复制代码
注意修改 include_flutter.groovy
位置为实际工程中的地址,这边引入 ':flutter'
,但 native 工程尚未依赖它。
dependencies {
implementation project(':flutter')
}
复制代码
试一把:在 MainActivity 里面加入 Flutter 代码:
报红??????看一下报的错误吧。
Type mismatch.
Required: Lifecycle!
Found: androidx.lifecycle.Lifecycle
复制代码
看来是类型不匹配啊,androidx 是 support 包整合以后,用以解决 support 包混乱的问题,不要紧,换成 support(flutter 也支持 androidx,在新建工程的时候下方有个选项是否使用 androidX,28 版本是 support 库最后支持的版本,后面都要使用 androidX)。同时,将 gradle.properties
设置为useAndroidX=false
、enableJetifier=false
// 将 androidx 依赖改为这个
implementation "com.android.support:appcompat-v7:28.0.0"
复制代码
再试一把:加入代码
setContentView(flutterView)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
FlutterMain.startInitialization(this)
val flutterView = Flutter.createView(this, lifecycle, "/main")
setContentView(flutterView)
}
}
复制代码
能够简单理解为建立了一个 View,而后设置为布局
@NonNull
public static FlutterView createView(@NonNull final Activity activity,
@NonNull final Lifecycle lifecycle,
final String initialRoute)
复制代码
initialRoute
:在 flutter 中设置的页面路由,而在 flutter 工程默认生成的工程中,flutter_module_project/lib/main.dart
没有配置路由,给其配置一个路由:
再试亿把:编译成功了,下载到手机中试一试 方式1:as 里面的 run 方式2:打开 terminal ,输入
flutter run
做为将来的外卖员,该选哪一个你内心没点数吗?确定第一种啊!
Default interface methods are only supported starting with Android N (--min-api 24): void android.arch.lifecycle.DefaultLifecycleObserver.onCreate(android.arch.lifecycle.LifecycleOwner)
复制代码
在 native 工程的 build.gradle 里面添加以下代码:
android {
......
defaultConfig{
......
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
}
复制代码
到这里就成了!咱们是冠军!
看一下成果(忽略这丑陋的一切):
在开始骚操做以前,先解剖一下刚刚成功生成的 apk 文件:
libflutter.so
和flutter_assets
是 flutter 运行必备的资源,前者是flutter 框架基础,后者就是 lib 文件夹下的 dart 代码,这就是坑的开始。
--> 进入 flutter module 工程
两种方式:
./gradlew assembleDebug
复制代码
编译结束后,在.android/Flutter/build/outputs/aar
下找到flutter-debug.aar
flutter build aar --debug // 后面会解释 debug 与 release 的区别
复制代码
--> 注意:咱们在拉 flutter sdk 的时候,通常是在 github 上 clone,目前该指令只在 master 分支上有效。位置在flutter_module_project/build/host/outputs/repo/com/example/flutter_module_project/flutter_debug/1.0/flutter_debug-1.0.aar
两种方式生成的 aar 文件相同。
为了引入 aar,须要在 native 外层的 build.gradle 中添加以下代码,否则会出现找不到 aar 文件的问题:
allprojects {
repositories {
flatDir {
dirs 'libs'
}
}
}
复制代码
将 flutter_debug-1.0.aar 拷贝到 app/libs
文件夹下,在 app 下的 build.gradle 添加以下代码:
implementation(name : 'flutter_debug-1.0', ext : 'aar')
复制代码
回到 MainActivity 中,添加以下代码:
FlutterMain.startInitialization(this)
val flutterView = Flutter.createView(this, lifecycle, "/main")
setContentView(flutterView)
复制代码
找不到依赖?
在1.1节中,以 module 方式引用依赖没有出现任何问题,看看 native 工程的依赖:
再看看以 module 方式引入的依赖:
project :flutter
+--- com.android.support:support-v13:27.1.1
| +--- com.android.support:support-annotations:27.1.1 -> 28.0.0
| \--- com.android.support:support-v4:27.1.1
| +--- com.android.support:support-compat:27.1.1 -> 28.0.0 (*)
| +--- com.android.support:support-media-compat:27.1.1
| | +--- com.android.support:support-annotations:27.1.1 -> 28.0.0
| | \--- com.android.support:support-compat:27.1.1 -> 28.0.0 (*)
| +--- com.android.support:support-core-utils:27.1.1 -> 28.0.0 (*)
| +--- com.android.support:support-core-ui:27.1.1 -> 28.0.0 (*)
| \--- com.android.support:support-fragment:27.1.1 -> 28.0.0 (*)
+--- com.android.support:support-annotations:27.1.1 -> 28.0.0
+--- io.flutter:flutter_embedding_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852
| +--- android.arch.lifecycle:common:1.1.1 (*)
| +--- android.arch.lifecycle:common-java8:1.1.1
| | +--- android.arch.lifecycle:common:1.1.1 (*)
| | \--- com.android.support:support-annotations:26.1.0 -> 28.0.0
| +--- android.arch.lifecycle:runtime:1.1.1 (*)
| +--- com.android.support:support-fragment:28.0.0 (*)
| \--- com.android.support:support-annotations:28.0.0
+--- io.flutter:armeabi_v7a_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852
\--- io.flutter:arm64_v8a_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852
复制代码
原来咱们须要的都是在flutter_embedding_debug
这个依赖下面,可是以 aar 形式引入缺乏该依赖。
上文提到 flutter.gradle
的工程是为 flutter 工程添加必要的依赖读过揭开Flutter工程编译的面纱(Android篇)发现跟我本地的 flutter.gradle 有不一样之处,文章中说 flutter.jar 被直接做为依赖引入,而咱们工程中引用的是flutter_embedding_debug.jar
这个依赖,在 flutter.gradle 寻找关键字。
allprojects {
repositories {
......
maven { url "http://download.flutter.io" }
flatDir {
dirs 'libs'
}
}
}
复制代码
app 下的 build.gradle 添加以下依赖
dependencies {
implementation "io.flutter:flutter_embedding_release:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
}
复制代码
--> 注意:后面的长串字母是引擎号,要跟你的 flutter sdk 相匹配,不能直接用文章中的。
果真没让我失望,报错了:
java.lang.UnsatisfiedLinkError:
dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.flutternativeproject-1/base.apk"],nativeLibraryDirectories=[/vendor/lib64, /system/lib64]]]
couldn't find "libflutter.so" 复制代码
没找到 "libflutter.so",说明依赖尚未全,咱们继续找 libflutter.so 在哪一个依赖下面:
libflutter.so 放在这个依赖里面,继续在 "flutter.gradle" 里面寻找答案:
flutter 支持这四种 cpu 架构,而且将相应架构的 libflutter.so 加入到工程依赖中,这样工程无论依赖哪一个,都会存在 libflutter.so。因此咱们也须要让 native 工程拥有这些依赖。
dependencies {
implementation "io.flutter:flutter_embedding_release:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
implementation "io.flutter:arm64_v8a_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
implementation "io.flutter:armeabi_v7a_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
implementation "io.flutter:x86_64_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
implementation "io.flutter:x86_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
implementation(name : 'flutter_debug-1.0', ext : 'aar')
复制代码
我可太牛了,成功黑屏,"libflutter.so"和依赖库全都有了,并且 flutter.gradle 里面也是这样的,why?总以为哪里有不对劲的地方。回顾我以前接百度地图 sdk 的时候,人家也没有让我在 app 的 libs 里面加这个so,那个so的,不都是直接 implementment 相应的aar就行了嘛,aar 里面不就应该有 so 吗?想到这里,扒开 flutter_debug-1.0.aar 的外衣:
jni 文件都没有!为啥 aar 打包的时候,依赖都没有被打到 aar 里面,我认可在这时候我产生了自我怀疑,“谷歌爸爸确定不可能坑咱们的”,肯定本身操做没有问题以后,搜寻答案:
flutter build aar 指令并不会将依赖的 module 或者 libary 打入到 aar 中,须要搭配 fat-aar 。
github 中原版的 fat-aar 不支持 gradle 3.0+,可使用 kezong\fat-aar-android。按照教程配置以后的 Flutter 下 build.gradle:
// 是否将远程依赖也打包进去
configurations.embed.transitive = false
dependencies {
embed "io.flutter:flutter_embedding_release:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
embed "io.flutter:arm64_v8a_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
embed "io.flutter:armeabi_v7a_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
embed "io.flutter:x86_64_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
embed "io.flutter:x86_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
}
复制代码
再次 flutter build aar --debug
,将 native 工程 app 中的 flutter 依赖所有移除,仅依赖 flutter aar 便可。
打出的 aar 中在 libs 文件下确实包含了相应的 library,在 native 工程中再打包试一下吧!
若是你试过了就会发现,仍然出不来,黑屏也不报错!
对比flutter run 生成的 apk 与 native 工程生成的 apk:
在lib下的cpu架构不一致,缺乏x86相关的,可是 x86 是用于平板的,跟手机不要紧呀。
再仔细查看依赖,发现
embed "io.flutter:flutter_embedding_release:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
复制代码
个人指令是 flutter build aar --debug,应该依赖的是 debug,因此改为 debug 试一试
embed "io.flutter:flutter_embedding_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
复制代码
再打成 aar 集成到 native 里面,发现正常运行了!Ok,到这里 aar 编译成功!咱们又是冠军!
OK,上面讲的是踩坑排坑的过程,这一章将介绍如何快速集成(Flutter Module 工程)。
fat-aar
插件--> .android/build.gradle
buildscript {
dependencies {
...
classpath 'com.kezong:fat-aar:1.2.7'
}
}
复制代码
--> .android/Flutter/build.gradle
// 是否将远程依赖也打包进去
configurations.embed.transitive = false
// 添加 flutter 依赖
dependencies {
embed "io.flutter:flutter_embedding_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
embed "io.flutter:arm64_v8a_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
embed "io.flutter:armeabi_v7a_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
embed "io.flutter:x86_64_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
embed "io.flutter:x86_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
}
复制代码
--> 不要忘记 Maven 库
allprojects {
repositories {
......
maven { url "http://download.flutter.io" }
flatDir {
dirs 'libs'
}
}
}
复制代码
切到pubspec.yaml
同级目录,打开 terminal,写入指令flutter build aar --debug
,aar 生成于 你的flutter工程目录/build/host/outputs/repo/你的包名/flutter_debug/1.0/
答: 资料:深刻理解flutter的编译原理与优化
Debug模式:对应了Dart的JIT模式,又称检查模式或者慢速模式。支持设备,模拟器(iOS/Android),此模式下打开了断言,包括全部的调试信息,服务扩展和Observatory等调试辅助。此模式为快速开发和运行作了优化,但并未对执行速度,包大小和部署作优化。Debug模式下,编译使用JIT技术,支持广受欢迎的亚秒级有状态的hot reload。
Release模式:对应了Dart的AOT模式,此模式目标即为部署到终端用户。只支持真机,不包括模拟器。关闭了全部断言,尽量多地去掉了调试信息,关闭了全部调试工具。为快速启动,快速执行,包大小作了优化。禁止了全部调试辅助手段,服务扩展。
总言之:debug 没有优化,方便开发;release 不少优化,用于最终版本。同时在 flutter_embedding_debug
依赖包里面有个配置相关类 BuildConfig
:
public final class BuildConfig {
public static final boolean DEBUG = true;
public static final boolean PROFILE = false;
public static final boolean RELEASE = false;
public static final boolean JIT_RELEASE = false;
private BuildConfig() {
}
}
复制代码
因此当依赖包不匹配的时候,真机能运行,不报错,可是黑屏,应该就是 libflutter.so
没有正确初始化的缘由,因此在工程中必定要保证依赖库的正确。从集成过程来看,aar 方式
容易出错,仍是建议以 module 依赖
的形式进行混合开发。
答:
if (useLocalEngine()) {
String engineOutPath = project.property('localEngineOut')
File engineOut = project.file(engineOutPath)
if (!engineOut.isDirectory()) {
throw new GradleException('localEngineOut must point to a local engine build')
}
Path baseEnginePath = Paths.get(engineOut.absolutePath)
flutterJar = baseEnginePath.resolve("flutter.jar").toFile()
if (!flutterJar.isFile()) {
throw new GradleException("Local engine jar not found: $flutterJar")
}
localEngine = engineOut.name
localEngineSrcPath = engineOut.parentFile.parent
// The local engine is built for one of the build type.
// However, we use the same engine for each of the build types.
project.android.buildTypes.each {
addApiDependencies(project, it.name, project.files {
flutterJar
})
}
} else {
project.android.buildTypes.each this.&addFlutterDependencies
project.android.buildTypes.whenObjectAdded this.&addFlutterDependencies
}
复制代码
flutter 指令支持本地引擎的编译,可是引擎须要提早准备好,大概编译就须要一个半小时,咱们就用默认的引擎就行了。若是使用本地引擎,就会 sdk 目录下的 engine 里面使用 flutter.jar 做为依赖引入,它内部结构以下:
内容结构跟咱们经过 embed 引入的远程依赖是类似的,可是在 engine 目录下有不少 cpu 架构的 flutter.jar 文件,根据本身的须要引入。既然结构类似,那是否能够经过依赖 flutter.jar 的形式打 aar 引入到 native 工程呢?通过实测,答案是YES,我使用的是 android-arm 文件下的 flutter.jar。
请从下列选项中选出符合条件的选项:(D)
A:新技术
B:一份代码两端运行
C:google
D:“杀”程序猿省钱