In order to actively embrace new technologies and optimize the performance of RN, it is decided to introduce the fluent technology stack into new business requirements
The development of Flutter hybrid stack can be roughly divided into the following two modes
native projects rely directly on development
The specific access method is as follows: first, in setting Add the following code to gradle:
setBinding(new Binding([gradle: this])) evaluate(new File( settingsDir, '../../Flutter Module Project root directory/.android/include_flutter.groovy' ))
Secondly, in App build Add the following code to gradle:
implementation project(':flutter')
Finally, in the main project build Add the following code to gradle:
repositories { buildscript { maven { url 'http://download.flutter.io' } } } allprojects { repositories { maven { url 'http://download.flutter.io' } } }
native project access aar
New fluent module project
flutter create -t module xx_module
The directory structure is as follows
xx_modlue - .android // Android test project - .ios // iOS Test Engineering - lib // Flutter main works - main.dart // Fluent entry file - pubspec.yaml // Flutter 3rd party package profile
The command to package module s into AARS is provided in fluent, and the generated aar file path is xx_modlue/build/host/outputs/repo
flutter build aar
The reference of aar can be completed by introducing the generated aar file into the Android development project
So far, the introduction of the whole aar can be developed normally, but there is a problem, that is, in each development, the generated aar package needs to be manually copied to the main project for dependency, which is not only troublesome but also prone to errors. Therefore, it is the best practice to turn the fluent packaging and introduction process into a common mode for daily development
Analysis of the process of flitter packaging and uploading:
In order to comply with the daily development process, the aar file printed by Flutter needs to be uploaded to maven, so the first task is to solve the problem of uploading aar to Maven
Looking at the pom file under the generated aar directory, you will find that the third-party aar package that the main project depends on will also be downloaded to XX_ Under the path of modlue / build / host / outputs / repo, the pom file is as follows:
<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>
By analyzing the pom file, we can see that when uploading the aar generated by the main project, we also need to upload the downloaded third-party aar to the maven library. Therefore, we know that the specific engineering script process is as follows:
1. Get the generated aar path
2. Upload the aar file that the third party depends on
3. Update artifactId of main project aar
4. Upload main project aar file
The specific script is as follows:
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` # Clear fluent generation file flutter clean # Get pub package flutter pub get # remove folders rm -rf `pwd`/build/host/outputs/repo/ # Modified version number group="com.xxx.flutter" type="release" #type="debug" #type="profile" version="${type}-0.0.7" artifactId="xxx" echo "replace Flutter/build.gradle Medium group by ${group}" path=`pwd`/.android/Flutter/build.gradle sed -i '' '29s/^.*$/group "'${group}'"/' ${path} echo "replace Flutter/build.gradle Medium version by ${version}" path=`pwd`/.android/Flutter/build.gradle sed -i '' '30s/^.*$/version "'${version}'"/' ${path} # Pack AAR flutter build aar --no-debug --no-profile # Find AAR and upload 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 "replace pom artifactId by ${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
After uploading maven successfully, the main project relies on the fluent code, which is consistent with the process of adding a third-party SDK.
Selection comparison
name | advantage | shortcoming |
---|---|---|
native projects rely directly on development | Fast access | The engineering structure is complex, so it is impossible to separate the fluent development from the native development process |
native project access aar | Decoupling between fluent development and native development process | The initial access process is complex |
The final choice is to access aar through maven to facilitate subsequent expansion
Selection of Flutter hybrid stack
After completing the Flutter hybrid development access process, there will be a hybrid stack management problem. The main problem to be solved in the hybrid scheme is how to deal with the alternating Flutter and Native pages. Based on the current open source framework, the model is fluterboost
flutterBoost Flutter end access:
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 access:
application initialization code:
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)
Routing configuration code
// PageRouter route jump and configuration page object PageRouter { /** * Routing mapping */ 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 jump logic
// Initialize channel notification FlutterBoost.instance().channel().addMethodCallHandler { call, result -> when (call.method) { "baseUrl" -> { result.success(ApiConstant.getApiUrl()) } } } // Jump code val params = hashMapOf<String, String>() params["param"] = param PageRouter.openPageByUrl(this, PageRouter.SHOPPING_CAR, params)
Setup of fluent test environment
The most likely way to make complaints about mixed development is testing. It is time-consuming and difficult to debug with native. It requires high level of front-end development and needs to understand the basic process of native.