为了积极拥抱新技术并优化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.gradle
sed?-i?''?'29s/^.*$/group?"'${group}'"/'??${path}
echo?"替换Flutter/build.gradle?中的version?为${version}"
path=`pwd`/.android/Flutter/build.gradle
sed?-i?''?'30s/^.*$/version?"'${version}'"/'??${path}
#?打包AAR
flutter?build?aar?--no-debug?--no-profile
#?找到AAR并上传
path=`pwd`
#?shellcheck?disable=SC2006
p=`find?${path}/build/host/outputs/repo?-type?f??-name?"*${type}*.aar"`
echo?"${p}"
array=(${p//'\n'/})
currentName=""
currentPath=""
currentPom=""
currentDir=""
#?shellcheck?disable=SC2068
for?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}
????fi
done
cd?"${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工程接入aar | Flutter开发与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的基本流程
???????
?
|