Android Native 工程集成 Flutter 的两种方式,androidsdk开发封装

  • app 模块下的 build.gradle

android {

}

// 在编译过程中产生的中间产物将会存放在该文件夹下
buildDir = new File(rootProject.projectDir, “…/build/host”)

dependencies {
implementation project(’:flutter’)

}

主工程 app 依赖了子模块 :flutter,但是我们在工程中并没有使用以flutter命名的子模块。

  • Flutter 模块下的 build.gradle

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 位置等等。

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文件夹下的代码就是源码。

  • .android/setting.gradle

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

  • .android/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 “: n a m e " g r a d l e . p r o j e c t ( " : name" gradle.project(": name"gradle.project(":name”).projectDir = pluginDirectory
}
}

这边是调用 flutter build aar 指令(仅在 flutter module 工程下可用)编译输出 aar 文件时会被调用的,其目的就是将 flutter 工程用到的第三方组件打到输出到 aar 中方便 Native 工程引入。

  • flutterRoot/packages/flutter_tools/gradle/flutter.gradle

flutterRoot 是 flutter sdk 所在的位置。具体内容可以查看 揭开Flutter工程编译的面纱(Android篇)。

二、接入

目前 Native 工程接入 Flutter 工程有两种方式:

  1. 将上文提到的Flutter模块作为依赖引入到 Native
  2. 以 aar 形式引入到 Native 工程

2.1 - 以 module 方式集成

该方式集成是最简单轻松的。native 工程名称为 FlutterNativeProject,flutter 工程名称为 flutter_module_project

2.1.1 - 步骤1

将 flutter module 工程整个拷入到 native 工程中。(本文只是从 git 管理的角度放在 native 工程下,当然也可以在其他位置,只需在配置文件配置)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SeSWpuYI-1637687768223)(https://user-gold-cdn.xitu.io/2019/10/15/16dce4ae71da4377?imageView2/0/w/1280/h/960/ignore-error/1)]

2.1.2 - 步骤2 :修改 setting.gradle

include ‘:app’
rootProject.name=‘FlutterNativeProject’
rootProject.name = ‘android_generated’
setBinding(new Binding([gradle: this]))
evaluate(new File(settingsDir,
'/flutter_module_project/.android/inc

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

lude_flutter.groovy’))

注意修改 include_flutter.groovy 位置为实际工程中的地址,这边引入 ':flutter',但 native 工程还没有依赖它。

2.1.3 步骤3:修改 native 工程下 app 的 build.gradle

dependencies {
implementation project(’:flutter’)
}

试一把:在 MainActivity 里面加入 Flutter 代码:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a8RUo38N-1637687768253)(https://user-gold-cdn.xitu.io/2019/10/15/16dce5a5b31b6c22?imageView2/0/w/1280/h/960/ignore-error/1)]

报红??????看一下报的错误吧。

Type mismatch.
Required: Lifecycle!
Found: androidx.lifecycle.Lifecycle

看来是类型不匹配啊,androidx 是 support 包整合之后,用以解决 support 包混乱的问题,没关系,换成 support(flutter 也支持 androidx,在新建工程的时候下方有个选项是否使用 androidX,28 版本是 support 库最后支持的版本,后面都要使用 androidX)。同时,将 gradle.properties 设置为useAndroidX=falseenableJetifier=false

// 将 androidx 依赖改成这个
implementation “com.android.support:appcompat-v7:28.0.0”

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CbBiAAib-1637687768254)(https://user-gold-cdn.xitu.io/2019/10/15/16dce69f0eeba0d5?imageView2/0/w/1280/h/960/ignore-error/1)]

再试一把:加入代码 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没有配置路由,给其配置一个路由:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IuBn0ZdA-1637687768255)(https://user-gold-cdn.xitu.io/2019/10/15/16dce73ae8ea6f15?imageView2/0/w/1280/h/960/ignore-error/1)]

再试亿把:编译成功了,下载到手机中试一试 方式1:as 里面的 run 方式2:打开 terminal ,输入 flutter run

作为未来的外卖员,该选哪个你心里没点数吗?肯定第一种啊!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jGsBuwMy-1637687768256)(https://user-gold-cdn.xitu.io/2019/10/15/16dce79bc790b5e2?imageView2/0/w/1280/h/960/ignore-error/1)]

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
}
}
}

到这里就成了!我们是冠军!

看一下成果(忽略这丑陋的一切):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m3uqU6dP-1637687768257)(https://user-gold-cdn.xitu.io/2019/10/15/16dce8165c0aedef?imageView2/0/w/1280/h/960/ignore-error/1)]

2.2 - 以 aar 形式引入

在开始骚操作之前,先解剖一下刚刚成功生成的 apk 文件:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9AP2zTaA-1637687768258)(https://user-gold-cdn.xitu.io/2019/10/15/16dce8957a615fb1?imageView2/0/w/1280/h/960/ignore-error/1)]

libflutter.soflutter_assets是 flutter 运行必备的资源,前者是flutter 框架基础,后者就是 lib 文件夹下的 dart 代码,这就是坑的开始。

2.2.1 - 步骤1:生成 aar

–> 进入 flutter module 工程

两种方式:

  1. 进入 .android 文件夹,打开 Terminal,输入指令:

./gradlew assembleDebug

编译结束后,在.android/Flutter/build/outputs/aar下找到flutter-debug.aar

  1. 在 flutter 工程下(注意位置,一定要在 pubspec.yaml 同级目录下)打开 Terminal,输入指令:

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 文件相同。

2.2.2 - 步骤2:在 native 工程中引用

为了引入 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)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y9L7MZMi-1637687768259)(https://user-gold-cdn.xitu.io/2019/10/15/16dceafb96f842d0?imageView2/0/w/1280/h/960/ignore-error/1)]

找不到依赖?

2.2.3 - 依赖找不到寻因

在1.1节中,以 module 方式引用依赖没有出现任何问题,看看 native 工程的依赖:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7KXNPoas-1637687768260)(https://user-gold-cdn.xitu.io/2019/10/15/16dcebcc9b40abba?imageView2/0/w/1280/h/960/ignore-error/1)]

再看看以 module 方式引入的依赖:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LBFbxMvZ-1637687768261)(https://user-gold-cdn.xitu.io/2019/10/15/16dcebe6bb50a43a?imageView2/0/w/1280/h/960/ignore-error/1)]

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 寻找关键字。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hCTuQLe4-1637687768262)(https://user-gold-cdn.xitu.io/2019/10/15/16dcecd9a89465be?imageView2/0/w/1280/h/960/ignore-error/1)]

原来,flutter_embedding_debug 是从远程仓库"download.flutter.io"下载下来的,那我们就在 native 工程中引用吧!

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 在哪个依赖下面:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gqtyCUAO-1637687768262)(https://user-gold-cdn.xitu.io/2019/10/15/16dcf251e3477e1a?imageView2/0/w/1280/h/960/ignore-error/1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eN91vmPq-1637687768263)(https://user-gold-cdn.xitu.io/2019/10/15/16dcf259f8f0cecc?imageView2/0/w/1280/h/960/ignore-error/1)]

libflutter.so 放在这个依赖里面,继续在 “flutter.gradle” 里面寻找答案:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JWmt5AuB-1637687768263)(https://user-gold-cdn.xitu.io/2019/10/15/16dcf26ea21a9aab?imageView2/0/w/1280/h/960/ignore-error/1)]

flutter 支持这四种 cpu 架构,并且将相应架构的 libflutter.so 加入到工程依赖中,这样工程不管依赖哪个,都会存在 libflutter.so。所以我们也需要让 native 工程拥有这些依赖。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hcLjaabq-1637687768264)(https://user-gold-cdn.xitu.io/2019/10/15/16dcf2a9c09ab0cf?imageView2/0/w/1280/h/960/ignore-error/1)]

我们把四个都加入到 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’)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3rfEJFpg-1637687768265)(https://user-gold-cdn.xitu.io/2019/10/15/16dcf36b84ff068e?imageView2/0/w/1280/h/960/ignore-error/1)]

我可太牛了,成功黑屏,"libflutter.so"和依赖库全都有了,而且 flutter.gradle 里面也是这样的,why?总觉得哪里有不对劲的地方。回顾我之前接百度地图 sdk 的时候,人家也没有让我在 app 的 libs 里面加这个so,那个so的,不都是直接 implementment 相应的aar就好了嘛,aar 里面不就应该有 so 吗?想到这里,扒开 flutter_debug-1.0.aar 的外衣:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W7ft46MZ-1637687768265)(https://user-gold-cdn.xitu.io/2019/10/15/16dcf39e5eb325f8?imageView2/0/w/1280/h/960/ignore-error/1)]

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”
}

endencies {
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”
}

你可能感兴趣的:(程序员,面试,android,移动开发)