0、Dart是值傳遞還是引用傳遞?
Dart是值傳遞。每次調用函數,傳遞過去的都是對象的內存地址,不是對象的復制。
知識點擴展:Swift的struct是值類型,class是引用類型;意思是:聲明一個新的變量指向這個結構體,改變某個屬性,原本的結構體屬性不會發生變化;而類會隨著改變
1、Flutter的核心樹
Widget:
1、使用配置和狀態,描述view的樣子
2、當一個Widget發生改變時,Widget會重新build它的描述
Element:
1、Element是widget的實例,在樹中詳細的位置。
RenderObject:
1、渲染樹上的一個對象。
2、RenderObject是渲染庫的核心。
2、flutter 中Widget的分類
1、組合類
statelesswidget、statefullwidget
2、代理類
inheritedwidget:用于狀態共享,Theme 、Localizations 、 MediaQuery 等,都是通過它進行狀態共享,通過context獲取共享的狀態,ThemeData theme = Theme.of(context);
ParentDataWidget
3、繪制類RenderObjectWidget
RenderObject 的布局相關方法調用順序是 : layout -> performResize -> performLayout -> markNeedsPaint
3、mixin extends implement 之間的關系?
繼承extends 混入mixin 接口實現implement,三者可以同時存在,前后順序是extends,mixin,implement。
Flutter的繼承是單繼承,子類重寫超類的方法用override,子類調用超類的方法用super。
mixin是為了解決繼承方面的問題而引入的機制,Dart為了支持多重繼承,引入了mixin關鍵字。mixins的對象是類,mixins絕不是繼承,也不是接口,而是一種全新的特性,可以mixins多個類,mixins的使用需要滿足一定條件。
mixin的使用條件:
1、mixin類只能繼承自object
2、mixin類不能有構造函數
3、一個類可以mixins多個mixins類,但是不破壞flutter的單繼承
mixin就是為了解決Dart的多繼承問題,但是mixin不能有構造方法,避免繼承多個類產生構造方法沖突
4、Dart的語言特性
1、Dart一切都是對象,所有的對象繼承自Object。
2、Dart和Swift一樣都是強類型語言,可以用var或 dynamic來聲明一個變量,Dart會自動推斷其數據類型。
3、沒有賦值初始值的變量都會默認null
4、Dart支持頂層方法,如main方法,可以在方法內部創建方法
5、Dart支持頂層變量,也支持類變量或對象變量
6、Dart沒有public protected private等關鍵字,如果變量以下劃線開頭(_button),表示這個變量在庫中是私有的。
5、Dart 中的級聯操作符 (未使用疑問)
Dart 當中的 「..」意思是 「級聯操作符」,為了方便配置而使用。「..」和「.」不同的是 調用「..」后返回的相當于是 this,而「.」返回的則是該方法返回的值 。
6、Dart 的單線程模型是如何運行的?
Dart在單線程中是以消息循環機制來運行的,包含兩個任務隊列,微任務隊列
(microtask queue)、事件隊列
(event queue)。
flutter啟動后,消息循環就啟動了。按照先進先出原則逐個執行微任務隊列
中的任務,執行完畢后執行事件隊列中的任務,事件隊列中的任務執行完畢之后,在執行微任務隊列,如此循環往復。
7、await for 與 stream流 (待深入研究)
Stream<String> stream = new Stream<String>.fromIterable(['1', '2', '3', '4']);
main() async{
print('start');
await for(String s in stream){
print(s);
}
print('end..');
}
結果
start
1
2
3
4
end..
await for是不斷獲取stream流中的數據,然后執行循環體中的操作。await for一般用在知道Stream什么時候完成,并且必須等待傳遞完成后才能使用,不然就會一直阻塞。
8、Stream 與 Future的關系?
1、Future表示稍后獲得的一個數據,所有異步操作的返回值都用Future來表示。
2、Future只能表示一次異步獲取的數據。
3、Stream表示多次異步獲取的數據。例如按鈕多次點擊,這個點擊事件就是Stream。
4、Future只返回一 次值,Stream返回多次值。
5、Dart中統一使用Stream流處理數據。
9、Stream 有哪兩種訂閱模式?分別是怎么調用的?
Stream單訂閱
和多訂閱
兩種。
Stream 默認處于單訂閱模式,所以同一個 stream 上的 listen 和其它大多數方法只能調用一次,調用第二次就會報錯。
Stream 可以通過 transform() 方法(返回另一個 Stream)進行連續調用。通過 Stream.asBroadcastStream() 可以將一個單訂閱模式的 Stream 轉換成一個多訂閱模式的 Stream,isBroadcast 屬性可以判斷當前 Stream 所處的模式。
10、Flutter中的Widget、State、Context 的核心概念?是為了解決什么問題?
主要是為了解決多個部件之間的交互和部件自身狀態的維護。
1、Widget: 在Flutter中,幾乎所有東西都是Widget。將一個Widget想象為一個可視化的組件(或與應用可視化方面交互的組件),當你需要構建與布局直接或間接相關的任何內容時,你正在使用Widget。
2、Widget樹: Widget以樹結構進行組織。包含其他Widget的widget被稱為父Widget(或widget容器)。包含在父widget中的widget被稱為子Widget。
3、Context: 僅僅是已創建的所有Widget樹結構中的某個Widget的位置引用。簡而言之,將context作為widget樹的一部分,其中context所對應的widget被添加到此樹中。一個context只從屬于一個widget,它和widget一樣是鏈接在一起的,并且會形成一個context樹。
4、State: 定義了StatefulWidget實例的行為,它包含了用于”交互/干預“Widget信息的行為和布局。應用于State的任何更改都會強制重建Widget。
11、Dart異步編程中的 Future關鍵字
Dart中,執行一個異步任務使用Future來處理。
在 Dart 的每一個 Isolate 當中,執行的優先級為 :Main > MicroTask > EventQueue。
12、什么是Isolate?
Dart只有一個主線程,其實在Dart中并不是叫 Thread ,而是有個專門名詞叫 「isolate(隔離)。
用官方文檔中定義一句話來概括: An isolated Dart execution context .大概的意思就是「isolate實際就是一個隔離的Dart執行的上下文環境(或者容器)」。「isolate是有自己的內存和單線程控制的事件循環」
其實在Dart也會遇到一些耗時計算的任務,不建議把任務放在主isolate中,否則容易造成UI卡頓,需要開辟一個單獨isolate來獨立執行耗時任務,然后通過消息機制把最終計算結果發送給主isolate實現UI的更新。」 在Dart中異步是并發方案的基礎,Dart支持單個和多個isolate中的異步。。
「Dart沒有共享內存的并發」,沒有競爭的可能性所以不需要鎖,也就不存在死鎖的問題。
Flutter異步編程-Isolate
13、Flutter 中的生命周期
13.1 statefulWidget
1、initState→didChangeDependence→build→didupdateDependence→deactivate→dispose
2、initState:只調用一次,widget創建執行的第一個方法,這里可以做初始化工作,不如初始化state變量。
3、didChangeDependence:多次調用。
1、initState調用
2、依賴的InheritedWidget rebuild會被調用。
3、build:多次調用
1、initState調用
2、setState觸發的時候調用
4、didupdateDependence:多次調用
組件狀態改變的時候調用
5、deactivate:調用一次
當State對象從樹中被移除,會調用此回調,會在dispose之前調用。
頁面銷毀的時候會依次執行:deactivate → dispose
6、dispose:調用一次
當State對象從樹中被永久移除時調用;通常在此方法中釋放資源。
13.2 statelessWidget
1、builde
2、update
14、flutter和RN
flutter與RN的相同點:
1.都是移動開發跨臺解決方案
2.界面的編寫都采用響應式視圖,維護了一個狀態機,只更新改變的最小區域界面
3.都支持熱重載hot reload,開發調試非常方便
4.調用系統的service仍然需要封裝接口,仍然還是需要懂得native開發
flutter與RN 的區別:
- 性能方面:
Flutter由于是基于Dart語言, 所以避免了RN的那種通過橋接器與Javascript通訊導致效率低下的問題,所以在性能方面比RN更高一籌,會更接近原生的體驗. - 學習成本方面:
Flutter是基于Dart語言,相對來說,由于要學習一門新的開發語言所以學習成本比較高, 而RN采用JS語言開發,基于React,對前端工程師更友好. - UI 樣式方面:
flutter實現跨平臺采用了更為徹底的方案, 因為它基于canvas自己實現了一套UI框架, 所以兼容性更好, 而 RN 在在樣式方面還是會遇到比較多的問題,且解決起來會有點麻煩.
4.成熟度的方面:
React Native 是在 2015 年發布的,經過 3 年多的發展,已經比較成熟, 雖然也還不完善, 但是Flutter 是在今年 6 月份才推出發布預覽版,社區也剛剛發展, 在github上還有兩千多個待解決的問題,所以flutter需要更多時間。
flutter的有一套自己的UI框架,所以兼容性更好,且由于RN需要橋接器和JavaScript通訊,導致效率低下,flutter更接近原生體驗。
15、Flutter 線程管理模型 (待加強)
Flutter Engine層會創建一個Isolate,并且Dart代碼默認就運行在這個主Isolate上。必要時可以使用spawnUri和spawn兩種方式來創建新的Isolate,在Flutter中,新創建的Isolate由Flutter進行統一的管理。
事實上,Flutter Engine自己不創建和管理線程,Flutter Engine線程的創建和管理是Embeder負責的,Embeder指的是將引擎移植到平臺的中間層代碼。
Flutter 中存在的四大線程:分別為 UI Runner、GPU Runner、IO Runner, Platform Runner (原生主線程) ,在 Flutter 中可以通過 isolate 或者 compute 執行真正的跨線程異步操作。
16、Flutter狀態管理
Flutter的狀態可以分為全局狀態和局部狀態兩種。
常用的狀態管理都是基于InheritedWidget封裝的用于Widget樹的數據傳遞與共享的的一套框架。
Provider是繼承于InheritProvider,而InheritProvider本質上是一個InheritWidget,所以Provider本質上是依托于InheritProvider的機制來實現的widget樹的狀態共享。
總結:Provider本質上是依托于InheritProvider的機制來來實現widget樹的共享。
17、isolate是怎么進行通信和實例化的?
1、isolate是一個隔離Dart執行的上下文環境。
2、isolate有自己的內容和單線程控制的事件循環。
3、isolate之間的內存邏輯上是隔離的,不共享內存。
4、任何Dart程序的并發都是運行多個isolate的結果。Dart沒有共享內存的并發,所以不存在死鎖問題。
isolate線程之間的通信主要通過port來進行,這個port消息傳遞過程是異步的。
18、Future還是isolate場景分析?
1、如果一段代碼不會被中斷,那么就直接使用正常的同步執行就行。
2、如果代碼段可以獨立運行而不會影響應用程序的流暢性,建議使用 Future (需要花費幾毫秒時間)
3、如果繁重的處理可能要花一些時間才能完成,而且會影響應用程序的流暢性,建議使用 isolate (需要幾百毫秒)
使用 isolate 的具體場景:
1、JSON解析: 解碼JSON,這是HttpRequest的結果,可能需要一些時間,可以使用封裝好的 isolate 的 compute 頂層方法。
2、加解密: 加解密過程比較耗時
3、圖片處理: 比如裁剪圖片比較耗時
4、從網絡中加載大圖
19、Flutter 是如何與原生Android、iOS進行通信的?(重點了解)
Flutter 通過 PlatformChannel 與原生進行交互,其中 PlatformChannel 分為三種:
BasicMessageChannel :用于傳遞字符串和半結構化的信息。
MethodChannel :用于傳遞方法調用(method invocation)。
EventChannel : 用于數據流(event streams)的通信。
20、Flutter 繪制流程
GPU的VSync信號同步到UI線程,UI線程使用Dart來構建抽象視圖結構。然后,這些數據結構在GPU中進行圖層合成,視圖數據提供給 Skia引擎渲染為 GPU數據,這些數據通過 OpenGL或者 Vulkan提供給 GPU。
21、PlatformView
platform view 就是 AndroidView 和 UIKitView 的總稱,允許將 native view 嵌入到了 flutter widget 體系中,完成 Dart 代碼對 native view 的控制。
鏈接:http://www.lxweimin.com/p/9306d7ecde35
22、Flutter的理念架構
1、Framework層則是使用Dart編寫的一套基礎視圖庫,包含了動畫、圖形繪制和手勢識別等功能,是使用頻率最高的一層。
2、Engine層負責圖形繪制、文字排版和提供Dart運行時,Engine層具有獨立虛擬機,正是由于它的存在,Flutter程序才能運行在不同的平臺上。
3、Embedder是操作系統適配層,實現了渲染。
參考:http://www.lxweimin.com/p/9064a68a05ae
23、flutter的key和context
1、key
:是flutter用來標記widget的唯一標識。
- Local Key(局部key):分為Value Key、Object Key和Unique Key,必須要有唯一性。
Local Key顧名思義,指的是在當前Widget層級下,有唯一的Key屬性。可以用int或者string來表示。
1.1 Value Key使用事例如下
使用學生的學號或者身份證號等唯一的標識來標識。
1.2 Object Key
通過對比對象的地址來判斷是否相同,下同new了兩次Student,所以key是不同的,如果使用同一個對象就會報錯。
1.3 Unique Key
實在找不到什么標識的話我們可以使用UniqueKey來進行標記,自動生成唯一的key。
- Global Key (全局key):在全局APP中,具有唯一性。Global Key的性能會比Local Key差很多。
Global Key使用場景,一是讓widget在Widget Tree發生大幅改動的時候仍然保留狀態,二是像JavaScript里面getElementById那樣查找某個元素并得到它的各種信息。
final _globalKey1 = GlobalKey ();
final _globalKey2 = GlobalKey ();
獲取全局的數據信息:
2、context
:是指widget在Widget樹中的具體位置,flutter就是根據context包含的信息,來繪制widget及其對應的位置。
24、Flutter 空安全
空安全(Sound null safety)是Flutter 更新之后 Dart 中新增的一項特性,swift語言也有此特性。有了空安全,Dart 分析器可以進行更好的檢查。與空安全相關的新操作符和關鍵字有 ?、!。
在空安全之前,是可以直接賦值為null,且可以通過編譯,這個就說空安全與之前最大的不同,而且是在編譯階段就直接報異常錯誤。
在使用空安全的情況下,我們想讓變量賦值為null時,我們可以這樣處理:
int? count = null;
String? name = null;
只需在類型后面添加 ? 即可
類型后面跟操作符 ? 表示當前變量可為null。
總結:
操作符 ? :放在類型后面表示當前變量可為null,例如 String a 和 String ? b ,a 不能為null,而 b 可以。
操作符 !:表示此變量值不為null,如果為null則會拋出異常。使用請慎重考慮。
25、flutter和原生交互傳參的方式
Flutter和Xcode混編
重點總結:
1、flutter調用OC傳參,需要使用MethodChannel
1、flutter內部點擊事件傳參
// 用于調用原生方法 "hometestmethod"標識符與OC中的監聽標識符保持一致
var homechannelmethod = MethodChannel("hometestmethod");
// 點擊
onTap: (){
//給原生發送消息并傳入參數,原生根據標識homePageCallNativeMethond來做對應的處理
homechannelmethod.invokeMethod('homePageCallNativeMethond',{"key":"value","key1":"value1"});
},
2、OC接受傳參
// 1.創建方法通道對象,用于監聽flutter調用原生時的回調,唯一標識“hometestmethod”與flutter要保持一致
FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"hometestmethod" binaryMessenger:flutterViewController.binaryMessenger];
//2. 設置監聽回調block,flutter端通過通道調用原生方法時會進入以下回調
[channel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
//call的屬性method是flutter調用原生方法的方法名,我們進行字符串判斷然后寫入不同的邏輯
if ([call.method isEqualToString:@"homePageCallNativeMethond"]) {
//flutter傳給原生的參數
id para = call.arguments;
NSLog(@"flutter傳給原生的參數:%@", para);
//可以做界面跳轉
[self.navigationController pushViewController:[TestViewController new] animated:YES];
//獲取一個字符串
NSString *nativeFinalStr = @"原生給flutter回傳的值";
if (nativeFinalStr!=nil) {
//把獲取到的字符串傳值給flutter
result(nativeFinalStr);
}else{
//異常(比如改方法是調用原生的getString獲取一個字符串,但是返回的是nil(空值),這顯然是不對的,就可以向flutter拋出異常 進入catch處理)
result([FlutterError errorWithCode:@"001" message:[NSString stringWithFormat:@"進入異常處理"] details:@"進入flutter的trycatch方法的catch方法"]);
}
}else{
//調用的方法原生沒有對應的處理 拋出未實現的異常
result(FlutterMethodNotImplemented);
}
}];
2、OC調用FLuttter傳參,需要調用EventChannel,通過代理傳參。
//1. 創建事件通道對象,唯一標識 “hometest”,到時flutter是根據該標識來監聽原生發送給flutter的參數信息
FlutterEventChannel *evenChannel = [FlutterEventChannel eventChannelWithName:@"hometest" binaryMessenger:flutterViewController.binaryMessenger];
//2. 當原生跳往flutter時會觸發下面的- (FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)events回調方法,可以在該方法中給flutter傳遞參數
[evenChannel setStreamHandler:self];
//原生跳轉flutter時,會觸發該方法,在該方法中可以傳遞參數給flutter界面,需要注意的是flutter代碼中必須寫上對應的監聽代碼,這里才會被執行
- (FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)events{
if (events) {
events(@{@"key":@"value"});
}
return nil;
}
26、本地數據存儲方式
文件、sharedPreferences、sqlite數據庫
和原生通訊方式
Flutter定義了三種不同類型的channel,分別是
eventChannel:用于數據流通信,原生向Flutter傳參,通過代理eventSink傳參。
methodChannel:flutter向原生傳參。定義好方法名
BaseMessageChannel:未使用
27、鍵盤高度超出解決
Scaffold(
resizeToAvoidBottomPadding: false, //輸入框抵住鍵盤
)
28、ListView報Vertical viewport was given unbounded height錯誤解決方法
原因:寬高溢出。導致widget不顯示
解決:給ListView的shrinkWrap屬性設置為ture(收縮)。父視圖的大小跟隨子組件內容大小。
29、Flutter報setState() or markNeedsBuild() called during build.錯誤解決辦法
錯誤原因:在運行中,控件和響應事件沒有構建完,延時加載就可解決問題
解決方法:使用Future延時
void _addIndex() {
Future.delayed(Duration(milliseconds: 200)).then((e) {
setState(() {});
});
}
}
30、Flutter報setState() called after dispose()錯誤解決辦法
錯誤解析:防止頁面關閉執行setState()方法
解決方法:mounted, mounted 表明 State 當前是否正確綁定在View樹中。State的生命周期里面,這個 mounted 屬性不會改變,直至 framework 調用 State.dispose,當改變之后, State 對象再也不會調用 build 方法 mounted = false。
31、實現水波紋
InkWell
32、安卓和iOS 控件區別
Flutter提供了兩種不同風格安卓的Material 和 iOS的Cupertino風格
33、StatelessWidget的生命周期有哪些,請按生命周期順序說一下?
只有build,update。
34、Flutter是怎么實現熱重載的,說說具體實現原理?
Flutter的 Debug 模式支持 JIT(Just In Time),指的是即時編譯或運行時編譯,
JIT 編譯器將 Dart 代碼編譯成可以運行在 Dart VM 上的 Dart Kernel, Dart Kernel可以動態更新。
35、為什么說Flutter的性能好,與RN的區別?
1、flutter沒有橋接層。
2、自帶渲染引擎。skia
3、編譯執行。JS是解釋執行。
4、Flutter Engine虛擬機
36、Flutter中是怎么實現并發操作的?
兩種方式:isolate 和 Future
Future:短時間的幾毫秒。
isolate:長時間幾百毫秒
37、isolate是怎么進行通信和實例化的?
通信:通過port實現通信,sendPort和reservePort,這個port消息傳遞過程是異步的。
實例化: Isolate isolate =await Isolate.spawn<SendPort>(dataLoader, mainThreadPort.sendPort);
重點,深刻解析: https://www.bbsmax.com/A/kjdwe1NBJN/
38、Future和isolate有什么區別?
Future: 是一個異步執行并且在未來的某一個時刻完成(或失敗)的任務。Future本質上 并非并行執行,而是遵循事件循環處理事件的順序規則執行。
isolate:為了并行運行代碼。每個isolate都有自己的事件循環。
總結:
- 如果一個方法需要幾毫秒使用Future
- 如果一個處理流程需要幾百毫秒使用isolate
使用isolate場景:
- JSON解碼
- 加密
- 圖像處理:裁剪
- 從web加載圖像
39、Stream與Future是什么關系?
Future:只能接收一次返回的數據
Stream:接收多次返回的數據。flutter就是基于Stream流的處理。
40、setState是同步還是異步操作
setState是異步操作。下面代碼的打印先輸出2然后在輸出1.
onChanged:(value) async{
setState(){
print('1111111') ;
}
print('222222') ;
}
41、setState無效
Flutter開發App時,偶爾會遇到SetState()不起作用或界面更新不完全的Bug,是什么原因導致的呢?
比如在彈窗中點擊,標記對號。我們是使用系統的彈窗SimpleDialog來展示,但是點擊之后標記框狀態沒有改變,這是為什么呢?因為SimpleDialog是無狀態組件,要解決問題,我們需要包裹StateFulBuilder 如下: