为了踊跃拥抱新技术并优化RN的性能问题,所以决定在新业务需要中引入Flutter技术栈

Flutter混合栈开发大抵能够分为一下两种模式

native工程间接依赖开发

具体接入形式为,先在setting.gradle 中退出如下代码:

setBinding(new Binding([gradle: this]))evaluate(new File(        settingsDir,        '../../Flutter Module工程根目录/.android/include_flutter.groovy'))

其次在App的build.gradle 中退出如下代码:

 implementation project(':flutter')

最初在主工程的build.gradle 中退出如下代码即可:

repositories {buildscript {        maven {            url 'http://download.flutter.io'        }    }}    allprojects {    repositories {        maven {            url 'http://download.flutter.io'        }    }}

native工程接入aar

新建Flutter module工程

flutter create -t module xx_module

目录构造如下

xx_modlue           - .android // Android测试工程          - .ios  // iOS测试工程          - lib  // Flutter主工程                - main.dart // Flutter入口文件          - pubspec.yaml  // Flutter三方包配置文件

Flutter中提供了将module打包成aar的命令,生成的aar文件门路为 xx_modlue/build/host/outputs/repo

flutter build aar

将生成的aar文件引入Android开发工程即可实现aar的援用

到目前为止整个aar的引入根本是能够失常开发的,然而存在问题,那就是在每次开发都须要手动的将生成的aar包复制到主工程中进行依赖,不仅操作麻烦而且会出错,所以讲Flutter打包及引入流程变成日常开发罕用的模式是最佳实际

flutter 打包上传流程剖析:

为合乎日常开发流程,须要将Flutter打成的aar文件上传至maven,因而首要任务就是解决将aar上传至maven问题

查看生成的aar目录上面的pom文件会发现主工程依赖的第三方aar包也会被下载至xx_modlue/build/host/outputs/repo门路下,pom文件如下:

<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  <modelVersion>4.0.0</modelVersion>  <groupId>com.xxx.flutter</groupId>  <artifactId>xxx</artifactId>   <version>release-0.0.7</version>  <packaging>aar</packaging>  <dependencies>    <dependency>      <groupId>io.flutter</groupId>      <artifactId>flutter_embedding_release</artifactId>      <version>1.0.0-af51afceb8886cc11e25047523c4e0c7e1f5d408</version>      <scope>compile</scope>    </dependency>    <dependency>      <groupId>io.flutter</groupId>      <artifactId>armeabi_v7a_release</artifactId>      <version>1.0.0-af51afceb8886cc11e25047523c4e0c7e1f5d408</version>      <scope>compile</scope>    </dependency>    <dependency>      <groupId>io.flutter</groupId>      <artifactId>arm64_v8a_release</artifactId>      <version>1.0.0-af51afceb8886cc11e25047523c4e0c7e1f5d408</version>      <scope>compile</scope>    </dependency>    <dependency>      <groupId>io.flutter</groupId>      <artifactId>x86_64_release</artifactId>      <version>1.0.0-af51afceb8886cc11e25047523c4e0c7e1f5d408</version>      <scope>compile</scope>    </dependency>  </dependencies></project>

剖析pom文件可知在上传主工程生成的aar的时候咱们还须要将下载下来的第三方aar上传至maven库,因而咱们得悉具体工程化脚本流程如下:

1、获取生成的aar门路
2、上传第三方依赖的aar文件
3、更新主工程aar的artifactId
4、上传主工程aar文件

具体脚本如下:

deploy_aar(){    mvn deploy:deploy-file \    -DpomFile="$FILE_PATH/$NAME.pom" \    -DgeneratePom=false \    -Dfile="$FILE_PATH/$NAME.aar" \    -Durl="http://xxx.xxx.xxx:xxx/repository/public/" \    -DrepositoryId="nexus" \    -Dpackaging=aar \    -s="mvn-settings.xml" \    -Dversion="$VERSION"}projectDir=`pwd`# 革除Flutter生成文件flutter clean# 获取pub包flutter pub get# 删除文件夹rm -rf `pwd`/build/host/outputs/repo/# 批改版本号group="com.xxx.flutter"type="release"#type="debug"#type="profile"version="${type}-0.0.7"artifactId="xxx"echo "替换Flutter/build.gradle 中的group 为${group}"path=`pwd`/.android/Flutter/build.gradlesed -i '' '29s/^.*$/group "'${group}'"/'  ${path}echo "替换Flutter/build.gradle 中的version 为${version}"path=`pwd`/.android/Flutter/build.gradlesed -i '' '30s/^.*$/version "'${version}'"/'  ${path}# 打包AARflutter build aar --no-debug --no-profile# 找到AAR并上传path=`pwd`# shellcheck disable=SC2006p=`find ${path}/build/host/outputs/repo -type f  -name "*${type}*.aar"`echo "${p}"array=(${p//'\n'/})currentName=""currentPath=""currentPom=""currentDir=""# shellcheck disable=SC2068for item in ${array[@]}do    resFile=`basename ${item}`    echo "${item}"    result=$(echo ${resFile} | grep "flutter_release")    if [[ "$result" == "" ]]    then      lenght=${#item}      sub=${item:0:${lenght}-3}      pom="${sub}pom"      resFileLenght=${#resFile}      subDir=${item:0:${lenght}-${resFileLenght}}      curName=`echo ${resFile} | cut -d "-" -f 2`      curNameLenght=${#curName}      subVersion=${curName:0:${curNameLenght}-4}      nameLenght="${#resFile}"      subName=${resFile:0:${nameLenght}-4}      export FILE_PATH="${subDir}"      export NAME="${subName}"      export VERSION=${subVersion}      deploy_aar    else      nameLenght="${#resFile}"      subName=${resFile:0:${nameLenght}-4}      currentName="${subName}"      currentPath=${item}      currentPath=${item}      lenght=${#item}      sub=${item:0:${lenght}-3}      currentPom="${sub}pom"      resFileLenght=${#resFile}      subDir=${item:0:${lenght}-${resFileLenght}}      currentDir=${subDir}    fidonecd "${currentDir}"echo `pwd`mv "${currentName}.aar" "${artifactId}-${version}.aar"mv "${currentName}.pom" "${artifactId}-${version}.pom"cd ${projectDir}echo `pwd`currentName="${artifactId}-${version}"currentPath="${currentDir}${currentName}.aar"currentPom="${currentDir}${currentName}.pom"echo "current name is ${currentName}"echo "current path is ${currentPath}"echo "currentPom is ${currentPom}"echo "替换pom artifactId为${artifactId}"sed -i '' '6s/^.*$/  <artifactId>'${artifactId}'<\/artifactId> /'  ${currentPom}echo "currentDir is ${currentDir}"echo "currentVersion is ${version}"export FILE_PATH="${currentDir}"export NAME="${currentName}"export VERSION=${version}deploy_aar

上传maven胜利后,主工程依赖Flutter代码就和增加第三方SDK流程统一了。

选型比照

名称长处毛病
native工程间接依赖开发接入快工程结构复杂,无奈将Flutter开发从native开发流程中剥离
native工程接入aarFlutter开发与native开发流程解耦初期接入流程简单

最终抉择为通过maven形式接入aar不便后续拓展

Flutter 混合栈选型

在实现Flutter混合开发接入流程后,会有混合栈治理问题,在混合计划中解决的次要问题是如何去解决交替呈现的Flutter和Native页面。综合目前的开源框架,选型为FlutterBoost

flutterBoost Flutter端接入:

FlutterBoost.singleton.registerPageBuilders(<String, PageBuilder>{      testhome: (String pageName, Map<dynamic, dynamic> params, String _) =>          MyHomePage(title: ''),      shoppingcar: (String pageName, Map<dynamic, dynamic> params, String _) {        String platformItemNo = '';        if (params.containsKey("platformItemNo")) {          platformItemNo = params['platformItemNo'];          NativeChat.print(platformItemNo);        }        return ShoppingCar(platformItemNo: platformItemNo);      },      login: (String pageName, Map<dynamic, dynamic> params, String _) =>          LoginPage(),      overlay: (String pageName, Map<dynamic, dynamic> params, String _) =>          OverlayPage(),    });

android端接入:

application 初始化代码:

val router =            INativeRouter { context, url, urlParams, requestCode, exts ->                PageRouter.openPageByUrl(context, url, urlParams)            }        val boostLifecycleListener = object : FlutterBoost.BoostLifecycleListener {            override fun onEngineCreated() {            }            override fun onPluginsRegistered() {            }            override fun beforeCreateEngine() {            }            override fun onEngineDestroy() {            }        }        val platform = FlutterBoost.ConfigBuilder(application, router)            .isDebug(BuildConfig.DEBUG)            .whenEngineStart(FlutterBoost.ConfigBuilder.ANY_ACTIVITY_CREATED)            .renderMode(FlutterView.RenderMode.texture)            .lifecycleListener(boostLifecycleListener)            .build()        FlutterBoost.instance().init(platform)

路由配置代码

// PageRouter 路由跳转及配置页面object PageRouter {    /**     * 路由映射     */    val pageName: HashMap<String?, String?> =        object : HashMap<String?, String?>() {            init {                put("xxxx://shoppingCar", "shoppingCar")                put("xxxx://login", "login")                put("xxxx://home", "home")                put("xxxx://overlay", "overlay")            }        }    const val SHOPPING_CAR = "xxxx://shoppingCar"    const val LOGIN_PAGE = "xxxx://login"    const val OVERLAY = "xxxx://overlay"    const val BUYER_PRODUCT_DETAIL = "xxxx://buyer/productdetail"    const val TEST_SECOND = "xxxx://testSecond"    @JvmOverloads    fun openPageByUrl(        context: Context,        url: String,        params: Map<*, *>?,        requestCode: Int = 0    ): Boolean {        val path = url.split("\\?").toTypedArray()[0]        Log.i("openPageByUrl", path)        return try {            when {                pageName.containsKey(path) -> {                    val intent =                        BoostFlutterActivity.withNewEngine().url(pageName[path]!!)                            .params(params!!)                            .backgroundMode(BoostFlutterActivity.BackgroundMode.opaque)                            .build(context)                    if (context is Activity) {                        context.startActivityForResult(intent, requestCode)                    } else {                        context.startActivity(intent)                    }                    return true                }                url.startsWith(TEST_SECOND) -> {                    context.startActivity(                        Intent(                            context,                            SecondActivity::class.java                        )                    )                    return true                }                else -> false            }        } catch (t: Throwable) {            false        }    }}

native 跳转逻辑

// 初始化channel告诉 FlutterBoost.instance().channel().addMethodCallHandler { call, result ->            when (call.method) {                "baseUrl" -> {                    result.success(ApiConstant.getApiUrl())                }            }        }// 跳转代码 val params = hashMapOf<String, String>()            params["param"] = param            PageRouter.openPageByUrl(this, PageRouter.SHOPPING_CAR, params)

Flutter 测试环境搭建

在混合开发的工程中被吐槽最多的大略就是测试了吧,和native打包在一起调试费时费力,对前端开发要求高须要理解native的根本流程