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 工程擁有這些依賴。
咱們把四個都加入到 native 工程中,完整的app 下 build.gradle 以下: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:「殺」程序猿省錢