以下主要是學(xué)習(xí)極客時間 Flutter
專欄相關(guān)學(xué)習(xí)記錄胁镐。
Dart 基礎(chǔ)
核心特性
JIT & AOT
Dart
同時支持 JIT
和 AOT
兩種編譯方式蜜自。
-
JIT:Just In Time碌宴,運行時即時編譯,開發(fā)效率高。可動態(tài)下發(fā)和執(zhí)行代碼崭庸,運行速度和性能受到影響。
Flutter
的熱重載基礎(chǔ)這一特性谊囚。( 開發(fā)Debug
模式使用)
將 `Dart` 編譯生成中間代碼 `Script Snapshot`怕享,由 `Dart VM` 解釋執(zhí)行。
-
AOT:Ahead Of Time镰踏,需要預(yù)先編譯函筋,開發(fā)效率低。但運行速度快余境,性能表現(xiàn)好驻呐。(發(fā)布
Release
模式使用)編譯生成設(shè)備對應(yīng)的二進(jìn)制。
內(nèi)存分配與垃圾回收
內(nèi)存分配
Dart VM
的內(nèi)存分配芳来,只需要在堆上移動指針含末,內(nèi)存增長是線性的。
Dart
通過 Isolate
實現(xiàn)并發(fā)即舌,但并不共享內(nèi)存佣盒,可以實現(xiàn)無鎖快速分配。
垃圾回收
垃圾回收顽聂,采用多生代算法肥惭。
-
調(diào)度器
檢測到程序處于空閑狀態(tài)盯仪,沒有用戶交互時,進(jìn)行回收蜜葱,減少
GC
對性能的影響全景。 -
年輕代
新生代空間收集器,清除壽命較短的短暫對象牵囤。
由于對象被分配在連續(xù)空間中爸黄,在創(chuàng)建對象時,他們被分配到可用空間揭鳞,直到分配的內(nèi)存填充完畢炕贵。
分配給新對象的連續(xù)空間由兩部分組成,任何時候只使用一半野崇,分為活動空間和非活動空間称开。新生成的對象在活動區(qū)間,一旦填充完畢乓梨,
不可回收對象
從活動空間復(fù)制到非活動空間鳖轰。活動空間進(jìn)行清理扶镀,非活動空間轉(zhuǎn)變?yōu)榛顒涌臻g脆霎。過程如下圖所示:
-
老年代
并行標(biāo)記掃描收集器,清除生命周期比較長的對象狈惫。采用標(biāo)記整理的方法回收對象。
a. 遍歷對象圖鹦马,標(biāo)記仍在使用的對象胧谈。
b. 掃描,回收未被標(biāo)記的對象荸频,清除標(biāo)記菱肖。
如下圖所示:
單線程模型
Dart
是單線程模型,通過 Event Loop
實現(xiàn)異步旭从。
- 微任務(wù)隊列:短時間會完成的任務(wù)稳强,比事件隊列優(yōu)先級更高,當(dāng)其不為空和悦,會一直占著事件循環(huán)退疫。一般情況不會用到。目前只有
Flutter
內(nèi)部用到鸽素,如手勢識別褒繁,滾動視圖等高優(yōu)先級操作。 - 事件隊列:用得較多馍忽,如定時器棒坏、IO 等燕差。
Future
使用 Future
將同步任務(wù)包裝成異步任務(wù),把函數(shù)體放到事件隊列中坝冕,立即返回徒探。后面代碼同步執(zhí)行,執(zhí)行完成后喂窟,從事件隊列中取出事件测暗,依次同步執(zhí)行函數(shù)體及后續(xù)的 then
。
Future(() => print('Running in Future 1'));//下一個事件循環(huán)輸出字符串
Future(() => print('Running in Future 2'))
.then((_) => print('and then 1'))
.then((_) => print('and then 2'));//上一個事件循環(huán)結(jié)束后谎替,連續(xù)輸出三段字符串
Future(() => print('Running in Future 3'));
print('hello');
結(jié)果:
hello
Running in Future 1
Running in Future 2
and then 1
and then 2
Running in Future 3
若要同步等待 Future
中完成偷溺,使用 await
。await
钱贯、async
用法 與 js
中的類似挫掏。
并發(fā)
Dart
中沒有線程,并發(fā)通過 Isolate
實現(xiàn)秩命,并且 Isolate
之間不共享內(nèi)存尉共。每個Isolate
有自己獨立的堆棧,Event Loop
和 Queue
弃锐。它們之間通過消息機制(發(fā)送管道 sendPort
)進(jìn)行通信袄友。
比如,主 Isolate
向并發(fā)Isolate
傳入自己的發(fā)送管道霹菊,并監(jiān)聽管道消息剧蚣。這樣,并發(fā) Isolate
就可以通過該管道發(fā)送消息旋廷。
Isolate isolate;
start() async {
ReceivePort receivePort= ReceivePort();//創(chuàng)建管道
//創(chuàng)建并發(fā)Isolate鸠按,并傳入發(fā)送管道
isolate = await Isolate.spawn(getMsg, receivePort.sendPort);
//監(jiān)聽管道消息
receivePort.listen((data) {
print('Data:$data');
receivePort.close();//關(guān)閉管道
isolate?.kill(priority: Isolate.immediate);//殺死并發(fā)Isolate
isolate = null;
});
}
//并發(fā)Isolate往管道發(fā)送一個字符串
getMsg(sendPort) => sendPort.send("Hello");
基礎(chǔ)語法
變量與類型
未初始化的變量值都為
null
。-
可自行指定類型饶碘,或由編譯器推導(dǎo)目尖。
var
:表示類型由編譯器判斷。 所有類型都是對象類型扎运,繼承自
Object
瑟曲。基本數(shù)據(jù)類型:num、bool豪治、String洞拨、List、Map鬼吵。
num:64 位整形
Int
扣甲,64 位浮點型Double
。bool:true、false琉挖。需要顯式進(jìn)行比較启泣。
if (a != 0) {}
String:單雙引號都可以,多行用三個單引號或雙引號示辈。
List寥茫、Map
與 Java
、Swift
類似矾麻。
List:
// 自動推導(dǎo)元素類型為 `String`
var arr1 = ["Tom", "Andy", "Jack"];
// 顯式聲明
var arr1 = <String>['Tom', 'Andy', 'Jack'];
Map:
// 自動推導(dǎo)類型為 `Map<String: String>`
var map1 = {"name": "Tom", 'sex': 'male'};
// 顯式聲明
var map1 = <String, String>{'name': 'Tom','sex': 'male',};
推薦顯式聲明纱耻,可讀性好,當(dāng)添加不匹配類型時险耀,編譯器根據(jù)聲明類型做錯誤提示弄喘。
常量定義
const:在編譯期確定的值,適用于定義字面量甩牺。
const count = 3;
final:在運行期確定蘑志,一旦確定,不能修改贬派。
var x = 70;
var y = 30;
final z = x / y;
函數(shù)
函數(shù)定義
bool isZero(int number) {
//判斷整數(shù)是否為0
return number == 0;
}
若函數(shù)只有一行急但,可以使用箭頭函數(shù)來簡化函數(shù)定義。
bool isZero(int number) => number == 0;
參數(shù)
可選命名參數(shù)
給參數(shù)添加 {}
搞乏,類似于 map
波桩,調(diào)用時指定傳入哪些參數(shù),位置隨意请敦。
定義:
// 可選命名參數(shù)
void enable1Flags({bool bold, bool hidden}) => print("$bold , $hidden");
調(diào)用:
enable1Flags(bold: true, hidden: false); //true, false
enable1Flags(bold: true); //true, null
可選參數(shù)
給參數(shù)加上 []
镐躲,即這些參數(shù)是可選的,可設(shè)置默認(rèn)值侍筛。
定義:
void enable4Flags(bool bold, [bool hidden = false]) => print("$bold ,$hidden");
調(diào)用:
enable4Flags(true); //true, false
類
定義與 Java
類似匀油,但沒有 public、protected勾笆、private
關(guān)鍵字,通過 _
來區(qū)分是 private
桥滨。private
的限制是庫訪問級別窝爪。
class Point {
num x, y;
static num factor = 0;
//語法糖,等同于在函數(shù)體內(nèi):this.x = x;this.y = y;
Point(this.x,this.y);
void printInfo() => print('($x, $y)');
static void printZValue() => print('$factor');
}
var p = new Point(100,200); // new 關(guān)鍵字可以省略
p.printInfo(); // 輸出(100, 200);
Point.factor = 10;
Point.printZValue(); // 輸出10
為了使得實例化語義清晰化齐媒,支持命名構(gòu)造函數(shù)蒲每,類似指定構(gòu)造函數(shù)。
// 命名構(gòu)造函數(shù)
Point.bottom(num x) : this(x, 0);
// 實例化
var p = Point.bottom(100);
復(fù)用
繼承與接口實現(xiàn)喻括。
- 繼承會自動獲取父類的成員變量和方法實現(xiàn)邀杏,子類可以根據(jù)需要覆寫構(gòu)造函數(shù)及父類方法;
- 接口實現(xiàn),需要重新實現(xiàn)成員變量望蜡,以及方法的聲明和初始化唤崭,否則編譯器會報錯。
class Point {
num x = 0, y = 0;
void printInfo() => print('($x,$y)');
}
Vector extends Point{
num z = 0;
@override
void printInfo() => print('($x,$y,$z)');
}
// Coordinate是對Point的接口實現(xiàn)
class Coordinate implements Point {
// 成員變量需要重新聲明
num x = 0, y = 0;
// 成員函數(shù)需要重新聲明實現(xiàn)
void printInfo() => print('($x,$y)');
}
Mixin
Mixin脖律,即混入谢肾,為了解決多繼承帶來的問題。使用混入
復(fù)用代碼小泉,并不是繼承關(guān)系 is a
芦疏,類似組合 has a
。通過這種方式可以調(diào)用混入類的變量和方法微姊,使用 with
關(guān)鍵字即可酸茴。
注意,如果混入的多個類中有同名的方法且被調(diào)用兢交,會以最后一個混入類為準(zhǔn)薪捍。
class Coordinate with Point {
}
var x = Coordinate();
// 調(diào)用 Point 類中的方法
x.printInfo();
運算符
-
?.
,p?.printInfo()
魁淳,若p
為空飘诗,則不調(diào)用。類似于Swift
中的可選類型界逛。 -
??=
昆稿,a ??= value
,若a
為null
息拜,則給a
賦值value
溉潭。 -
??
,a ?? b
少欺,若a
為空喳瓣,則取b
;否則取a
赞别。
自定義與復(fù)寫
class Vector {
num x, y;
Vector(this.x, this.y);
// 自定義 +
Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
// 覆寫 ==
bool operator == (dynamic v) => x == v.x && y == v.y;
}
final x = Vector(3, 3);
final y = Vector(2, 2);
print(x == y);
print((x + y).x);
Flutter
跨平臺 UI
渲染框架畏陕,重寫了底層渲染邏輯。在 iOS
和 Android
上, UI
層面表現(xiàn)高度一致仿滔。底層基于 Skia
惠毁,C++
編寫的強大的跨平臺圖形繪制引擎。
與RN
不同的是崎页,Fluter
是自己完成了組件的渲染鞠绰。
而 RN
是通過 JS 虛擬機
作為橋梁調(diào)用到原生組件,最終生成的還是各端的原生組件飒焦,兩端會有差異性蜈膨。
Widget
widget
:一種抽象的結(jié)構(gòu)化信息描述。
Flutter
中一切皆為 widget
,比如應(yīng)用翁巍、視圖驴一、布局等。
Widget 渲染過程
涉及到三部分:Widget
曙咽、Element
蛔趴、RenderObject
,其中 Element
連接 Widget
和 RenderObject
例朱,分別持有孝情。
Widget:不可變,配置信息發(fā)生變化時洒嗤,會重建
Widget
樹箫荡,但它并不涉及到視圖的渲染,重建成本低渔隶。Element:可變羔挡,可以看成
Widget
對應(yīng)的一種數(shù)據(jù)結(jié)構(gòu),一個實例化的對象间唉,是視圖配置信息到最終渲染的橋梁绞灼。將Widget
的變化做了一層封裝,類似于React
中的虛擬DOM diff
呈野,只將需要改動的部分同步給RenderObject
低矮,提高渲染效率。RenderObject:真正的渲染對象被冒,負(fù)責(zé)布局與繪制军掂。之后的合成和渲染,交給
Skia
昨悼。
深度優(yōu)先遍歷 Widget
樹 --> 生成對應(yīng)節(jié)點的 Element
對象 --> 生成 RenderObject
蝗锥。
Widget
變化時,持有該 Widget
的 Element
會設(shè)置為 dirty
率触,觸發(fā) Element
和 RenderObject
樹的更新终议。
與原生混編
將 Flutter
工程成為原生工程的子模塊,抽離 Fluter
工程葱蝗,按照不同平臺的構(gòu)建產(chǎn)物按照組件化的方式進(jìn)行管理痊剖。
其中原生工程對 Fluter
的依賴主要是:
- Flutter 的 Framework 和引擎庫。
- Flutter 工程垒玲,即我們自己實現(xiàn)的功能。
通過 flutter build
生成各自平臺的產(chǎn)物找颓。
Android:打成 aar
引入合愈,在 build.gradle
中添加依賴。
iOS:作為獨立 pod
引入,創(chuàng)建 podSpec
佛析,在 podFile
中添加依賴益老。
調(diào)用原生能力
因為 Fluter
只接管了渲染層,所以原生系統(tǒng)能力無法提供寸莫,如藍(lán)牙捺萌、拍照、定位等等膘茎。
通過方法通道MethodChannel
方式桃纯,與原生進(jìn)行通信。類似網(wǎng)絡(luò)請求披坏,是請求 - 響應(yīng)式态坦。
Flutter 調(diào)用:
//聲明MethodChannel
const platform = MethodChannel('samples.chenhang/utils');
//異步等待方法通道的調(diào)用結(jié)果
result = await platform.invokeMethod('openAppMarket');
原生處理:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//創(chuàng)建命名方法通道
FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"samples.chenhang/utils" binaryMessenger:(FlutterViewController *)self.window.rootViewController];
//往方法通道注冊方法調(diào)用處理回調(diào)
[channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
//方法名稱一致
if ([@"openAppMarket" isEqualToString:call.method]) {
//打開App Store
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"itms-apps://itunes.apple.com/xy/app/foo/id414478124"]];
//返回方法處理結(jié)果
result(@0);
} else {
//找不到被調(diào)用的方法
result(FlutterMethodNotImplemented);
}
}];
...
}
同樣原生也可以調(diào)用 Flutter
方法,首先在 Flutter
方實現(xiàn)接口回調(diào)棒拂。
Native
使用 [channel invokeMethod:@"xx" arguments:xx result:^(id _Nullable result) {}];
進(jìn)行調(diào)用伞梯。