前言
在之前的開發(fā)過程中,我們一直采用的是Flutter官方推薦的方式集成到iOS原生項(xiàng)目中。這種方式的弊端很明顯:對原生工程的侵入太大,并且每次在編譯過程中都會重新生成Flutter的編譯文件,在多人開發(fā)的項(xiàng)目中非常影響開發(fā)效率。所以現(xiàn)改為另一種方式集成:以編譯產(chǎn)物的方式集成到原生項(xiàng)目。
目錄
一、Flutter的module集成方式
- 創(chuàng)建一個(gè)
module
工程 - 將
Flutter
通過cocoapods
集成到項(xiàng)目中 - 在
Xcode
中添加Flutter Script
腳本 - 關(guān)閉
Xcode
中的bitcode
選項(xiàng)
二、了解Flutter編譯過程
- 了解
podhelper.rb
ruby文件 - 了解
xcode_backend.sh
shell文件 - 總結(jié)
Flutter
集成過程
三、使用sh腳本收集Flutter編譯產(chǎn)物
- 找到對應(yīng)文件位置
- 編寫sh腳本文件
- 為編譯產(chǎn)物配置
podspec
文件 -
iOS
原生項(xiàng)目集成Flutter
產(chǎn)物 - 注意事項(xiàng)
四、參考文章
一、Flutter的module集成方式
前面已經(jīng)提到我們之前采用的是Flutter官方推薦的方式集成到iOS原生項(xiàng)目中,所以Flutter模塊是一個(gè)module
工程。即:
1、創(chuàng)建一個(gè)module工程
$ cd some/path/
$ flutter create -t module my_flutter
2、將Flutter通過cocoapods集成到項(xiàng)目中
//將以下代碼添加到Podfle文件中,并執(zhí)行pod install
flutter_application_path = 'path/to/my_flutter/'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
3、在Xcode中添加Flutter Script腳本
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed
4、關(guān)閉Xcode中的bitcode選項(xiàng)
至此,F(xiàn)lutter已集成完成,此處不涉及代碼。
二、了解Flutter編譯過程
在進(jìn)行Flutter編譯產(chǎn)物收集之前,我們需要了解Flutter在編譯過程中進(jìn)行了哪些步驟。
1、了解podhelper.rb
ruby文件
首先Flutter在Podfile
中添加了一個(gè)podhelper.rb
的ruby文件。
podhelper.rb
文件與Podfile文件都為ruby語法。該文件主要是通過Flutter.podspec
文件將Flutter.framework
庫以及FlutterPluginRegistrant
插件注冊類文件導(dǎo)入進(jìn)來,并且獲取每個(gè)Plugin模塊中的xx.podspec
文件將插件中的文件導(dǎo)入。各個(gè)插件模塊以及路徑均保存在module
工程中的.flutter-plugins
文件中。
執(zhí)行完pod install
之后,文件目錄如下
2、了解xcode_backend.sh
shell文件
xcode_backend
文件中包含三個(gè)主要的方法:BuildApp
、ThinAppFrameworks
、EmbedFlutterFrameworks
。
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed
在Xcode
中,首先傳入了build參數(shù)
xcode_backend
腳本先執(zhí)行BuildApp
方法,下面了解下BuildApp
主要進(jìn)行了哪些操作:
方法開始進(jìn)行了文件以及路徑檢測,確認(rèn)完成后開始進(jìn)行Flutter aot(Ahead Of Time)
模式編譯。并將編譯后的module工程/build/aot
文件夾下的App.framework
拷貝到module工程/.ios/Flutter
目錄下。
接著build bundle
進(jìn)行資源文件打包,存放路徑在App.framework/flutter_assets
中。
BuildApp
方法執(zhí)行完成后,開始執(zhí)行
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed
傳入embed
參數(shù), 執(zhí)行EmbedFlutterFrameworks
方法。
該方法的主要目的是將
App.framework
和Flutter.framework
拷貝到項(xiàng)目的xx.app
包中的Frameworks
目錄下。
ThinAppFrameworks
方式是移除不需要的framework架構(gòu)。
3、總結(jié)Flutter集成過程
經(jīng)過上面幾個(gè)步驟,我們可以總結(jié)出Flutter集成的幾個(gè)主要依賴關(guān)系:
1、Flutter
框架層主要依賴于Flutter.framework
和App.framework
兩個(gè)庫,Flutter.framework
是Flutter
框架的核心,App.framework
則包含的是我們的dart
源碼二進(jìn)制文件以及bundle
資源文件。
2、Flutter
的插件注冊類FlutterPluginRegistrant
以及Plugin
模塊的iOS原生插件代碼。
三、使用sh腳本收集Flutter編譯產(chǎn)物
1、找到對應(yīng)文件位置
在對編譯產(chǎn)物進(jìn)行收集之前,我們需要了解Flutter
編譯產(chǎn)物所在的目標(biāo)位置。
在對Flutter
進(jìn)行了build操作之后,module工程/.ios/Flutter
目錄下會生成的文件如上圖。我們可以找到Flutter
的兩個(gè)依賴庫Flutter.framework
和App.framework
。
上圖是其中一個(gè)Plugin
倉庫的文件目錄結(jié)構(gòu),我們主要收集的內(nèi)容即為Classes目錄及以下的全部文件。
2、編寫sh腳本文件
需要修改腳本文件中的framework_dir
文件位置
#! /bin/bash
echo "=== 正在進(jìn)行編譯 ==="
flutter build ios --release
framework_dir="保存編譯產(chǎn)物的位置/build_framework"
echo "===清理文件==="
rm -rf "$framework_dir/App.framework"
rm -rf "$framework_dir/Flutter.framework"
echo "===正在拷貝 App.framework和Flutter.framework==="
cp -r "./.ios/Flutter/App.framework" $framework_dir
cp -r "./.ios/Flutter/engine/Flutter.framework" $framework_dir
echo "===拷貝資源文件==="
resourcesDestPath=${framework_dir}/resources
resourcesOriginPath=${framework_dir}/App.framework/flutter_assets
rm -rf $resourcesDestPath
mkdir -p $resourcesDestPath
cp -af $resourcesOriginPath $resourcesDestPath
echo "===拷貝注冊器類:FlutterPluginRegistrant==="
regist_dir="./.ios/Flutter/FlutterPluginRegistrant/Classes/"
model_class=${framework_dir}/flutter/FlutterPluginRegistrant
mkdir -p $model_class
cp -r $regist_dir $model_class
temp_dir="${framework_dir}/temp"
mkdir -p ${temp_dir}
current_path="$PWD"
# 遍歷.flutter-plugins文件
cd /Users/apple/Desktop/HealthBuddy/hb_flutter_module
cat .flutter-plugins | while read line
do
array=(${line//=/ })
plugin_name=${array[0]}
echo "===修改注冊器(修正引用)==="
perl -pi -e "s|\<${plugin_name}\/|\"|g" ${model_class}/GeneratedPluginRegistrant.m
perl -pi -e "s|.h\>|.h\"|g" ${model_class}/GeneratedPluginRegistrant.m
temp_library=${temp_dir}/lib${plugin_name}.a
cd $current_path
echo "===拷貝 ${plugin_name} 文件==="
plugin=${framework_dir}/${plugin_name}
rm -rf $plugin
classes=${array[1]}ios/Classes
class=$framework_dir/Classes/flutter/${plugin_name}
rm -rf $class
mkdir -p $class
cp -af $classes $class
done
echo "===拷貝 注冊器類 FlutterPluginRegistrant==="
registrantFilePath=$framework_dir/Classes/flutter
cp -r ${framework_dir}/flutter/FlutterPluginRegistrant $registrantFilePath
echo "===移除臨時(shí)文件夾==="
rm -rf ${temp_dir}
將上述腳本文件放到module工程
中并執(zhí)行。執(zhí)行完成后,會生成如圖以下產(chǎn)物:
3、為編譯產(chǎn)物配置podspec
文件
flutter_module.podspec
為cocoapods
的配置文件,用于將產(chǎn)物集成到iOS項(xiàng)目中,配置如下:
Pod::Spec.new do |spec|
spec.name = "flutter_module"
spec.version = "0.0.1"
spec.summary = "A short description of flutter_module."
spec.homepage = "http://EXAMPLE/flutter_module"
spec.license = "MIT"
# spec.license = { :type => "MIT", :file => "FILE_LICENSE" }
spec.author = { "author" => "xxx@163.com" }
spec.platform = :ios
spec.platform = :ios, "8.0"
spec.source = { :git => "~/Documents/code/mycode/ManageLocalCode" }
# { :git => "http://EXAMPLE/flutter_module.git", :tag => "#{spec.version}" }
spec.source_files = "Classes", "Classes/flutter/**/*.{h,m}"
spec.resources = "resources/**"
spec.frameworks = "UIKit", "Foundation"
spec.ios.vendored_frameworks = "App.framework", "Flutter.framework"
spec.requires_arc = true
#如果插件中有依賴三方庫,需要配置在這里,多個(gè)庫可以添加多個(gè)spec.dependency
# spec.dependency "xxx", "~> 0.1.3"
end
4、iOS原生項(xiàng)目集成Flutter產(chǎn)物
在iOS工程中的Podfile
文件中添加以下代碼:
pod 'flutter_module', :path => "編譯產(chǎn)物路徑"
上述方式是采用本地路徑保存編譯產(chǎn)物,如有需要可以上傳到遠(yuǎn)程github
或者自定義云倉庫。
5、注意事項(xiàng)
現(xiàn)在項(xiàng)目已經(jīng)集成好了Flutter運(yùn)行的所有編譯文件以及代碼文件。但是在執(zhí)行過程中依然有幾個(gè)需要注意的點(diǎn):
① 如果是由原來官方的集成方式改為產(chǎn)物集成方式時(shí),需要?jiǎng)h除原Build Phases
中的以下腳本
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed
以及原Plugin
文件在Header Search Paths
和Other linker Flags
中的文件引用。
② 保證module工程/.ios
項(xiàng)目能運(yùn)行成功
運(yùn)行不成功的原因有可能是你的Podfile
文件中未添加Plugin
工程中的三方庫依賴。只有.ios
工程中的項(xiàng)目能正常運(yùn)行,Flutter
產(chǎn)物收集腳本中的flutter build ios
命令才能執(zhí)行成功。
③ 每次的產(chǎn)物更新都需要在iOS工程中執(zhí)行pod install
,更新產(chǎn)物文件的引用。
④ module工程
中的.ios
文件夾中的內(nèi)容被誤刪了如何恢復(fù)?
由于在寫收集Flutter產(chǎn)物腳本時(shí),我不小心有刪除過.ios
文件夾下的內(nèi)容,在module
工程中執(zhí)行以下代碼可以恢復(fù):
flutter create -i objc .