配置項(xiàng)目代碼關(guān)聯(lián)引擎源碼
通過(guò)下載引擎源碼可以進(jìn)行分析
以及動(dòng)態(tài)調(diào)試
-
Flutter引擎
編譯成功之后鞭莽,我們獲取到模擬器x86
架構(gòu)下的Xcode工程
(目錄:/src/out/ios_debug_sim_unopt
)坊秸; - 在
/ios_debug_sim_unopt
目錄下會(huì)有一個(gè)Flutter.framework/Flutter
引擎庫(kù),然后把我們的Flutter
項(xiàng)目配置成這個(gè)framework
澎怒,即配置自定義引擎
褒搔;
下面新建工程flutter_engine_demo
(注意iOS與Android平臺(tái)的語(yǔ)言選擇OC以及Java),讓其加載上面編譯好的自定義引擎
-
Flutter引擎
源碼最終編譯成了Xcode
工程喷面,我們是基于Xcode
進(jìn)行動(dòng)態(tài)調(diào)試的星瘾,因此這里要先在Xcode
中進(jìn)行配置,打開(kāi)flutter_engine_demo
工程中ios
目錄下的Runner
工程
Generated.xcconfig
是通用環(huán)境配置惧辈,我們?cè)谶@個(gè)文件中進(jìn)行配置
-
flutter_engine_demo
工程在模擬器上面運(yùn)行起來(lái)琳状,關(guān)閉Android Studio
,接下來(lái)我們?cè)?code>Runner工程中調(diào)試 查看工程執(zhí)行的腳本
-
xcode_backend.sh
腳本執(zhí)行完成之后盒齿,還會(huì)執(zhí)行xcode_backend.dart
腳本文件
-
Generated.xcconfig
文件中配置的變量算撮,會(huì)在xcode_backend.dart
文件中使用,比如FLUTTER_APPLICATION_PATH
環(huán)境變量
這就是Android Studio
執(zhí)行了Flutter
工程會(huì)調(diào)用Xcode
县昂,而Xcode
又關(guān)聯(lián)到Android Studio
的過(guò)程肮柜;關(guān)聯(lián)的過(guò)程都在腳本文件中處理好了,后面如果想配置持續(xù)集成
進(jìn)行打包倒彰,也需要配置相應(yīng)的腳本文件审洞。
-
Generated.xcconfig
文件中進(jìn)行配置,使其加載Flutter自定義引擎
-
Runner
工程添加斷點(diǎn)調(diào)試
通過(guò)斷點(diǎn)調(diào)試,我們發(fā)現(xiàn)了touchesBegan
的源碼實(shí)現(xiàn)芒澜,這里的源碼在FlutterViewController.mm
文件中仰剿,即編譯好的引擎中目錄:/src/out/ios_debug_sim_unopt
;
下面驗(yàn)證FlutterViewController.mm
就在關(guān)聯(lián)的引擎工程中
我們?cè)?code>Runner工程中進(jìn)行調(diào)試痴晦,添加了注釋南吮;然后打開(kāi)引擎工程
發(fā)現(xiàn)注釋存在,就證明了Runner
與自定義引擎
存在了關(guān)聯(lián)誊酌。
- 檢查
flutter_engine
部凑,進(jìn)行編譯
-
Runner
工程添加斷點(diǎn),調(diào)試到引擎中的源碼碧浊,在源碼中添加日志打印
- 在源碼中添加了
日志打印
涂邀,需要重新編譯Flutter引擎
才會(huì)執(zhí)行
重新運(yùn)行Runner
工程,點(diǎn)擊屏幕查看日志打印
Runner
成功與Flutter引擎
進(jìn)行關(guān)聯(lián)箱锐,而且還可以修改引擎源碼進(jìn)行調(diào)試
(每次修改源碼都需要重新編譯引擎)比勉。
查看引擎源碼
目錄
flutter_engine
引擎源碼中查看FlutterViewController.mm
文件的目錄,發(fā)現(xiàn)目錄結(jié)構(gòu)為/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm
我們發(fā)現(xiàn)引擎源碼
在flutter
目錄下驹止,并不在out
目錄浩聋,說(shuō)明源碼只有一份,根據(jù)真機(jī)臊恋、模擬器不同平臺(tái)衣洁,編譯出不同的Flutter.framework
庫(kù)。
查看工程Flutter.framework/Flutter
的哈希值是否相同捞镰?
-
Products/Runner.app
->Show in Finder
- 查看哈希值闸与,用于檢測(cè)
Flutter引擎
的二進(jìn)制文件是否發(fā)生變化
// 查看哈希值命令
$ md5 Flutter
// Flutter.framework/Flutter 的哈希值
4a794a8d53a0fadebc0453fa16e56518
// Runner/Flutter.framework/Flutter 的哈希值
4a794a8d53a0fadebc0453fa16e56518
通過(guò)對(duì)比毙替,哈希值相同岸售,說(shuō)明是一個(gè)產(chǎn)物。
- 現(xiàn)在我們把
Generated.xcconfig
文件中加載自定義引擎
的配置注釋掉厂画,再次編譯工程凸丸,查看哈希值
通過(guò)對(duì)比我們發(fā)現(xiàn)自定義引擎
與發(fā)布版本的引擎
哈希值不相同。
-
Generated.xcconfig
文件中再次加載自定義引擎
袱院,編譯工程查看哈希值
我們的工程在每次編譯生成Flutter.framework
的時(shí)候屎慢,可能會(huì)添加一些其它內(nèi)容,我們不能單純的比對(duì)Flutter.framwork
的哈希值來(lái)判斷Framework
是否發(fā)生更新忽洛。
疑問(wèn)腻惠?
Runnder
項(xiàng)目實(shí)際上獲取到的是編譯完成的Flutter.framework
,而Flutter.framework/Flutter
是如何定位到源代碼的路徑呢欲虚?跨工程是如何定位到的集灌?
Flutter
二進(jìn)制文件中包含一些調(diào)試信息,使其進(jìn)行關(guān)聯(lián)复哆。
檢查二進(jìn)制文件中是否包含調(diào)試信息
查看發(fā)布版本
的二進(jìn)制文件調(diào)試信息
-
Generated.xcconfig
文件中加載自定義引擎的配置注釋掉 Products/Runner.app -> Show in Finder
由打印信息可知欣喧,發(fā)布版本會(huì)把調(diào)試信息隱藏掉
腌零。
查看自定義引擎
的二進(jìn)制文件調(diào)試信息
-
Generated.xcconfig
文件中的自定義引擎配置打開(kāi) Products/Runner.app -> Show in Finder
通過(guò)終端成功查看到自定義引擎
的調(diào)試信息;說(shuō)明Runner
工程成功與自定義引擎
關(guān)聯(lián)到了一塊唆阿,就可以直接調(diào)試源碼
益涧。
檢查?進(jìn)制是否含有調(diào)試信息
- lipo命令
#可以查看包含的架構(gòu)
$ lipo -info xxx
#拆分架構(gòu)
$ lipo xxx -thin armv7 -output armv7_xxx
#合并多架構(gòu)
$ lipo -create xxx.a xxx.a -output xxx.a
- LLDB檢查是否含有調(diào)試信息
$ lldb --file Flutter_arm64
(lldb) target create "Flutter_arm64"
Current executable set to 'Flutter_arm64' (arm64)
// 查看有多少個(gè)編譯單元,即.o文件
.(lldb) script lldb.target.module['Flutter_arm64'] .GetNumCompileUnits()
1
(lldb)
- 使?python列出模塊的所有編譯單元的完整路徑
(lldb) target create "Flutter_arm64"
Current executable set to 'Flutter_arm64' (arm64).
(lldb) script
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
// 獲取到模型
>>> m = lldb.target.module['Flutter_arm64']
>>> for i in range(m.GetNumCompileUnits()):
... cu = m.GetCompileUnitAtIndex(i).file.fullpath
... print(cu)
...
None
>>>
調(diào)試引擎源碼Channel底層實(shí)現(xiàn)
下面我們就通過(guò)Runner
工程來(lái)調(diào)試Flutter引擎源碼
-
flutter_engine_demo
工程中添加給原生發(fā)送消息的代碼
<!-- main.dart文件 -->
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const EnginePage(),
);
}
}
class EnginePage extends StatefulWidget {
const EnginePage({Key? key}) : super(key: key);
@override
_EnginePageState createState() => _EnginePageState();
}
class _EnginePageState extends State<EnginePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('EnginePage'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
// 給原生發(fā)送消息
const MethodChannel('engine_page')
.invokeMapMethod('method_channel');
},
child: const Icon(Icons.add),
),
),
);
}
}
-
Generated.xcconfig
文件中進(jìn)行自定義引擎
相關(guān)配置(注意:如果引擎路徑發(fā)生了變化驯鳖,需要gn構(gòu)建
闲询、ninja編譯?程
) -
Runner
工程中AppDelegate.m
添加如下代碼
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
@interface AppDelegate ()
@property(nonatomic, strong) FlutterEngine* flutterEngine;
@property(nonatomic, strong) FlutterViewController* flutterVc;
@property(nonatomic, strong) FlutterMethodChannel* methodChannel;
@end
@implementation AppDelegate
// 懶加載引擎,通過(guò)引擎獲取VC
- (FlutterEngine *)flutterEngine {
if (!_flutterEngine) {
FlutterEngine * engine = [[FlutterEngine alloc] initWithName:@"hk"];
if (engine.run) {
_flutterEngine = engine;
}
}
return _flutterEngine;
}
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
self.flutterVc = [[FlutterViewController alloc] initWithEngine:self.flutterEngine nibName:nil bundle:nil];
// 通過(guò)VC獲取channel
// MethodChannel:傳遞方法的調(diào)用
self.methodChannel = [FlutterMethodChannel methodChannelWithName:@"engine_page" binaryMessenger:self.flutterVc.binaryMessenger];
[self.methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
NSLog(@"收到了:%@",call.method);
}];
// BasicMessageChannel:傳遞字符串和半結(jié)構(gòu)化信息
[FlutterBasicMessageChannel messageChannelWithName:@"123" binaryMessenger:self.flutterVc.binaryMessenger];
// EventChannel:傳遞數(shù)據(jù)流
[FlutterEventChannel eventChannelWithName:@"123" binaryMessenger:self.flutterVc.binaryMessenger];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
- 添加斷點(diǎn)進(jìn)行調(diào)試
通過(guò)methodChannelWithName
源碼我們發(fā)現(xiàn)臼隔,如果不傳解碼器
會(huì)有默認(rèn)的解碼器嘹裂,而且是一個(gè)單例
;eventChannelWithName
方法也是一樣的摔握,會(huì)默認(rèn)傳一個(gè)單例解碼器
寄狼。
保存name
、messenger
氨淌、解碼器
泊愧。
- 斷點(diǎn)調(diào)試
methodChannel
接收Flutter
發(fā)送過(guò)來(lái)的消息
如果有連接connection
先清空,否則設(shè)置消息回調(diào)
用字典把name
與handler
進(jìn)行保存
self.flutterVc.binaryMessenger
與setMethodCallHandler
方法中創(chuàng)建的messageHandler
是同一個(gè)對(duì)象盛正。
疑問(wèn)删咱?其實(shí)通訊的是數(shù)據(jù),那么傳遞的數(shù)據(jù)底層是怎么交互的豪筝?推薦跟蹤invokeMapMethod
底層源碼......
codec編解碼器
數(shù)據(jù)是怎么解析最終變成二進(jìn)制的痰滋?下面探索編解碼器
的底層實(shí)現(xiàn)...
前面我們學(xué)習(xí)的任何一種channel
,內(nèi)部都有一個(gè)編解碼器
续崖,編解碼器
其實(shí)是一種通訊協(xié)議
-
flutter_engine
源碼中搜索MessageCodec
敲街,發(fā)現(xiàn)是一種協(xié)議,而且編解碼都是對(duì)二進(jìn)制
進(jìn)行
- 搜索
MethodCodec
進(jìn)行查看
FlutterEventChannel
就是通過(guò)MethodCodec
編解碼的
- 查看
FlutterMethodCall
類
在Flutter
中严望,MessageCodec
有多種實(shí)現(xiàn):
-
FlutterStandardMessageCodec
:是FlutterBasicMessageChannel
中默認(rèn)使用的編解碼器多艇。(底層使用FlutterStandardReaderWriter
實(shí)現(xiàn)的)。用于數(shù)據(jù)類型和二進(jìn)制數(shù)據(jù)之間的編解碼像吻。支持基礎(chǔ)數(shù)據(jù)類型包(bool 峻黍、char 、double 拨匆、float 姆涩、int 、long 惭每、short 骨饿、String 、Array 、Dictionary)以及二進(jìn)制數(shù)據(jù)样刷。 -
FlutterBinaryCodec
:用于二進(jìn)制數(shù)據(jù)和二進(jìn)制數(shù)據(jù)之間的編解碼仑扑,在實(shí)現(xiàn)上只是原封不動(dòng)的將接收到的二進(jìn)制數(shù)據(jù)返回。 -
FlutterStringCodec
:用于字符串與二進(jìn)制數(shù)據(jù)之間的編解碼置鼻,對(duì)于字符串采用UTF-8
編碼格式镇饮。 -
FlutterJSONMessageCodec
:用于數(shù)據(jù)類型與二進(jìn)制數(shù)據(jù)之間的編解碼,支持基礎(chǔ)數(shù)據(jù)類型(bool 箕母、char 储藐、double 、float 嘶是、int 钙勃、long 、short 聂喇、String 辖源、Array 、Dictionary)希太。在iOS
端使用NSJSONSerialization
作為序列化的工具克饶。
查看FlutterStandardMessageCodec
源碼
查看FlutterStandardMethodCodec
源碼
編解碼器底層都使用的FlutterStandardReaderWriter
實(shí)現(xiàn)的,下面我們來(lái)分析FlutterStandardReaderWriter
的源碼