在對 flutter module 進行修改的時候,不知道為什麼,會發現 .android 目錄下和 .ios 目錄下的檔案會被修改覆蓋掉;後來發現,只要我們變動 pubspec.yaml
的檔案, 然後執行命令 flutter packages get
,就會重新從flutter 模板中替換 .android
和.ios
目錄; 因為我們的 .android
目錄下有自己定義 gradle 指令碼(主要為了解決打包aar的問題);就不希望這個 gradle 被覆蓋;
解決方法
- 在 flutter-sdk 中修改模板,把自己寫好的 gradle 放到模板中;
- 找到執行
flutter packages get
背後的邏輯,通過修改邏輯程式碼,不去覆蓋現有的程式碼;
替換模板的目錄
所有的模板目錄都在Flutter_HOME/packages/flutter_tools/templates
下面:
以上紅色箭頭的地方,是我替換和新增的模板程式碼;ios 應該也可以找到相應的模板進行新增和修改;
檢視 flutter 的 shell 指令碼命令
1. flutter 命令的開始
因為是 flutter 命令所以我們可以去看下 flutter 命令的原始碼;用文字編輯器開啟編輯 FLUTTER_HOME/bin/flutter 檔案;最後一行;
"$DART" --packages="$FLUTTER_TOOLS_DIR/.packages" $FLUTTER_TOOL_ARGS "$SNAPSHOT_PATH" "$@"
複製程式碼
為了方便觀察列印(echo)一下這個命令:
echo "$DART" --packages="$FLUTTER_TOOLS_DIR/.packages" $FLUTTER_TOOL_ARGS "$SNAPSHOT_PATH" "$@"
複製程式碼
執行命令 flutter --no-color packages get
得到如下:
/Users/xxxx/flutter/bin/cache/dart-sdk/bin/dart --packages=/Users/xxxx/flutter/packages/flutter_tools/.packages /Users/xxxx/flutter/bin/cache/flutter_tools.snapshot --no-color packages get
複製程式碼
很明顯他是根據 flutter_tools.snapshot
這個檔案進行執行的;引數是 --no-color packages get
; 那麼 flutter_tools.snapshot
這個二進位制檔案是怎麼生成的呢?
2. flutter_tools.snapshot 檔案的生成
可以繼續檢視一下 bin/flutter
這個檔案;
"$DART" $FLUTTER_TOOL_ARGS --snapshot="$SNAPSHOT_PATH" --packages="$FLUTTER_TOOLS_DIR/.packages" "$SCRIPT_PATH"
複製程式碼
這個命令主要是用來編譯flutter_tools.snapshot
的,我們可以列印(echo)一下這個命令; 修改一下 shell 指令碼,讓它進入 if 語句(怎麼修改可以看下第3
段)裡面;列印出來的結果如下;
/Users/xxxx/flutter/bin/cache/dart-sdk/bin/dart --snapshot=/Users/xxxx/flutter/bin/cache/flutter_tools.snapshot --packages=/Users/xxx/flutter/packages/flutter_tools/.packages /Users/xxxx/flutter/packages/flutter_tools/bin/flutter_tools.dart
複製程式碼
從上面的命令我們可以看出來所有的原始碼主要了來自 /Users/xxxx/flutter/packages/flutter_tools
這裡目錄裡面,進去看一下,都是dart 原始碼;
3. 稍微看下需要編譯 flutter_tools.snapshot 的條件是什麼
if [[
! -f "$SNAPSHOT_PATH"
|| ! -s "$STAMP_PATH"
|| "$(cat "$STAMP_PATH")" != "$revision"
|| "$FLUTTER_TOOLS_DIR/pubspec.yaml" -nt "$FLUTTER_TOOLS_DIR/pubspec.lock"
]]; then
echo Building flutter tool...
fi
複製程式碼
從上面來看,只要滿足這裡4個條件其中一個,就會去編譯 flutter_tools 生成一個 flutter_tools.snapshot
;
- flutter_tools.snapshot 檔案不存在
- STAMP_PATH 這個檔案的 size 為0, 主要用來快取一個 git commit
- 如果被快取的 git commit 和 revision 不相同也會觸發,revision =
git rev-parse HEAD
- 如果
pubspec.yaml
檔案最後修改時間大於pubspec.lock
檔案 (nt: new then)
經過以上3 點,接下來,我們可以去看下 flutter_tools 的原始碼了;
檢視 flutter_tools 的原始碼
我們把原始碼匯入 Android studio 檢視更加方便;main 方法從 flutter_tools/bin/flutter_tools.dart
檔案開始;這個類非常簡單:
import 'package:flutter_tools/executable.dart' as executable;
void main(List<String> args) {
executable.main(args);
}
複製程式碼
所以,所有的開始應該在 executeable.dart 裡面;裡面主要是把所有的命令都封裝成一物件,然後放到一個陣列裡面註冊,因為我們主要是觀察 flutter packages get
這個命令,所以我們去看下 PackagesCommand
從類構造來看,他含有子命令; PackagesGetCommand
; 先不管;因為所有的 FlutterCommand 都會執行 Future<FlutterCommandResult> runCommand()
這個方法;所以我們來看下這個的邏輯;
@override
Future<FlutterCommandResult> runCommand() async {
// .........省略沒必要的
await _runPubGet(target);
final FlutterProject rootProject = FlutterProject.fromPath(target);
// 下面這行程式碼主要是用來重新整理 .android 和 .ios的目錄
await rootProject.ensureReadyForPlatformSpecificTooling(checkProjects: true);
// Get/upgrade packages in example app as well
if (rootProject.hasExampleApp) {
final FlutterProject exampleProject = rootProject.example;
await _runPubGet(exampleProject.directory.path);
await exampleProject.ensureReadyForPlatformSpecificTooling(checkProjects: true);
}
// 省略沒必要的
}
}
複製程式碼
主要重新整理邏輯在 ensureReadyForPlatformSpecificTooling
/// Generates project files necessary to make Gradle builds work on Android
/// and CocoaPods+Xcode work on iOS, for app and module projects only.
Future<void> ensureReadyForPlatformSpecificTooling({bool checkProjects = false}) async {
if (!directory.existsSync() || hasExampleApp) {
return;
}
refreshPluginsList(this); // 這裡更新 .flutter-plugin 檔案
if ((android.existsSync() && checkProjects) || !checkProjects) {
await android.ensureReadyForPlatformSpecificTooling();// 這裡更新
}
if ((ios.existsSync() && checkProjects) || !checkProjects) {
await ios.ensureReadyForPlatformSpecificTooling();// 這裡更新
}
await injectPlugins(this, checkProjects: checkProjects);// 把一些channel 註冊到對應的平臺
}
複製程式碼
- refreshPluginsList(this): 重新整理
.flutter-plugins
檔案 - android.ensureReadyForPlatformSpecificTooling(): 重新整理 .android 檔案
- await ios.ensureReadyForPlatformSpecificTooling(): 重新整理 .ios 檔案
- injectPlugins(this, checkProjects: checkProjects): 把一些channel 註冊到對應的平臺, GeneratedPluginRegistrant 裡面相關的程式碼生成;
從上面的程式碼可以看出, 要想 flutter package get
不去重新整理,重新建立模板,只要把對應 ensureReadyForPlatformSpecificTooling() 程式碼不執行就好了;然後修改原始碼,重新編譯;編譯的方法在 flutter 指令碼中的 4 種方式裡面;