本文對應github地址Flutter6,如果由於github調整導致資源找不到,請訪問github
異常
-
throw 丟擲異常用
-
Exception 異常(內建異常和自定義異常)
-
try {} 包裝可能丟擲異常的程式碼,後面必須有on/catch/finally中一個或多個
-
on exceptionOfKnown{} 捕獲指定異常
-
catch(e){} 捕獲異常(如果前面有指定的則排除指定的後的異常)
-
finally{} 是否異常都執行
testCatchException() { // try...on 方式捕獲某些型別異常(知道異常型別) try { testThrowException(0); } on IntegerDivisionByZeroException { print('捕獲除0異常'); } // 不確定甚至不知道異常型別 catch try { testThrowException(-1); } on IntegerDivisionByZeroException { print('捕獲除0異常'); } catch (e) { print(e); } finally { print('是否異常都執行finally'); } } testThrowException(int exceptionIndex) { if (exceptionIndex == 0) { throw new IntegerDivisionByZeroException(); // 丟擲內建異常 } else if (exceptionIndex < 0) { throw new Exception('Index need more then 0'); // 拋一個自定義異常 } else { print(exceptionIndex); } } 複製程式碼
-
常見內建異常型別
- DeferredLoadException 延遲庫無法載入時丟擲。
- FormatException 資料沒有預期格式且無法解析或處理
- IntegerDivisionByZeroException 當數字除以零時丟擲
- IOException 所有與輸入輸出相關的異常的基類
- IsolateSpawnException 無法建立隔離時丟擲
- Timeout 在等待非同步結果時發生計劃超時時丟擲
-
zone
- 一般try{}catch{}能捕獲到異常,但有時候無效
- Dart語言中有個zone概念,類似沙盒(sandbox),不同zone程式碼互不影響,zone還能建立子zone。zone可以重新定義自己的print,timer,microtasks等,還可以解決未捕獲的異常
testZone() {
// 未捕獲的異常
try {
Future.error('asynchronous error');
} catch(e) {
print(e);
}
// 將上面程式碼註釋後用下面程式碼捕獲異常
runZoned((){
Future.error('asynchronous error');
}, onError: (Object obj, StackTrace stack){
// 這裡可以呼叫日誌上報函式
print(obj); // asynchronous error
print(stack); // null
});
}
複製程式碼
* 可以給runZoned註冊方法,在需要時回撥
```
handleData(result) {
print('handleData $result'); // handleData 2
}
var onData = Zone.current.registerUnaryCallback<dynamic, int>(handleData);
Zone.current.runUnary(onData, 2);
// 非同步邏輯
Zone.current.scheduleMicrotask((){
print('Todo something'); // Todo something
});
```
複製程式碼
非同步
-
簡介
- Dart是單執行緒模型,預設單執行緒處理任務。
- Dart中有個介於程式和執行緒之間的概念被稱為Worker-Isolate(實際更偏向程式概念),但一般理解成帶有獨立記憶體的執行緒,防止歧義,統一稱作Isolate、隔離。
- Dart中Isolate不等同於執行緒,Isolate有執行緒和獨立記憶體構成,又比程式低階
- Dart中的非同步機制涉及到的關鍵字有Future、async、await、async、sync、Iterator、Iterable、Stream、Timer等
- Dart非同步呼叫中async和await要配套使用,且await可以多個。
-
Isolate
- 定義在
import 'dart:isolate';
- 每個Isolate包含一個事件迴圈(event loop)和兩個事件佇列(event queue和microtask queue)
- event queue負責I/O、繪製、手勢等事件或接收其他Isolate訊息的外部事件。
- microtask queue則可以自己向Isolate內部新增事件,事件優先順序高於event queue。
- Isolate開始執行後優先microtask queue,處理完才處理event queue,且處理microtask queue時阻塞event queue,所以儘量把耗時操作放event queue。
- Isolate擁有獨立記憶體,執行緒之間不共享,所以不存在搶奪資源,即不需要鎖。
- Isolate之間通過port來通訊,且port訊息傳遞過程非同步。
- Isolate例項化過程其實是例項化Isolate+堆內分配記憶體+配置port等過程。
- Isolate物件允許其他Isolate監聽、控制它代表的事件迴圈,如當這個Isolate發生未捕獲錯誤時,可以暫停(pause)此Isolate或獲取錯誤資訊(addErrorListener)
- controlPort識別並授予Isolate控制許可權,pauseCapability和terminateCapability會對某些控制操作進行許可權保護。如暫停一個無pauseCapability的Isolate物件是不生效的。
- 由spawn操作建立的Isolate物件具有控制介面和控制該物件的能力。當然,Isolate構造方法建立的Isolate物件可以不必帶這些能力。
- Isolate物件不能用SendPort傳送給另一個Isolate物件,但是控制介面和能力capability是可以傳送的,並且可以在另一個Isolate物件中用傳送的介面和capability建立一個新Isolate物件。
- SendPort和ReceivePort是Isolate隔離物件唯一通訊方式
- 呼叫需要頂級函式(類似main這樣不被包裹的函式)或靜態函式
// 可以在頂級函式main()中呼叫 startInsolate(); Isolate isolate; int i = 0; Capability capability = new Capability(); runTimer(SendPort port) { int counter = 0; Timer.periodic(const Duration(seconds: 1), (_){ counter++; i++; var msg = 'Notification $counter'; print('Send message:$msg i:$i'); port.send(msg); }); } startIsolate() async { final receivePort = ReceivePort(); isolate = await Isolate.spawn(runTimer, receivePort.sendPort); receivePort.listen((data){ print('Receive message:$data i:$i'); }); } resumeIsolate() { isolate.resume(capability); } pauseIsolate() { isolate.pause(capability); } stopInsolate() { isolate.kill(priority: Isolate.immediate); isolate = null; } 複製程式碼
- Compute函式對isolate的建立和底層的訊息傳遞進行了封裝,使得我們不必關係底層的實現,只需要關注功能實現
void main() async{ //呼叫compute函式,compute函式的引數就是想要在isolate裡執行的函式,和這個函式需要的引數 print( await compute(syncFibonacci, 20)); // 6765 runApp(MyApp()); } // 計算斐波那契數列 int syncFibonacci(int n){ return n < 2 ? n : syncFibonacci(n-2) + syncFibonacci(n-1); } 複製程式碼
- 定義在
-
async await
- Dart中可通過async和await進行非同步操作
- async開啟非同步操作,返回一個Future結果(沒有返回值則返回null的Future,耗時操作要執行一段時間但Future卻立即返回)。
- await表示式通常返回Future,若不是Future則Dart把該值放到Future中返回,await會阻塞當前執行直到物件返回
- await可以多次使用(只能在async包裝內)
getChatMessage1(String userName) { print('111: ${queryDatebase(userName)}'); } getChatMessage2(String userName) async { var msg1 = await queryDatebase(userName); print('222: $msg1'); } // 範型 Future<String> queryDatebase(String userName) { return Future.delayed(Duration(seconds: 3), () => '20190808 $userName'); } // 呼叫 getChatMessage1('LiLei'); // 111: Instance of 'Future<String>' getChatMessage2('LiBai'); // 3秒後列印 333: 20190808 LiBai 複製程式碼
-
Future
- Future 簡單了說就是對 Zone 的封裝使用,如Future.microtask主要執行了Zone的 scheduleMicrotask,而result._complete最後呼叫的是 _zone.runUnary等
- 定義在
dart:async
中,基於觀察者模式。 - 支援範型
- Future 需要 import 'dart:io';('dart:async';)
- Future中常用方法then(),whenComplete(),wait(),catchError()
- 一般來說,如果需要監聽“完畢”這個狀態,那麼用whenComplete,需要監聽“成功”這個狀態,用then,需要監聽“失敗”這個狀態,用catchError
class _TestFutureFunction { // then()回撥中帶引數,此引數為Future物件中包含的值 testThen() { getNumber1().then((aNum) { print('a = $aNum'); return getNumber2(aNum); }).then((_) { print('_ = $_'); getResult(_); }); } // whenComplete()中不帶引數,只指定了順序,此方法丟擲異常後使用相當於finally testWhenComplete() { getNumber1().whenComplete((){ getNumber2(9).whenComplete((){ getResult(7).then((_){ print('TestWhenComplete'); }); }); }); } // 使用async await testAwait() async { var number1 = await getNumber1(); var number2 = await getNumber2(number1); print(await getResult(number2)); } // wait()方法, wait()方法內的非同步方法,要有await表示式 testWait() { Future.wait([getNumber1(), getNumber2(2), getResult(4)]).then((List result){ result.forEach((num) => print(num)); }); } Future<int> getNumber1() async { await print('getNumber1 10086'); return 10086; } Future getNumber2(int num) async { await print('getNumber2 ${num + 2}'); return num + 2; } Future getResult(int num) async { await print('getRusult'); } } 複製程式碼
下面是一個then()和whenComplete()結合使用的例子
var client = new http.Client(); client.post( "http://example.com/fruit", body: {"name": "apple", "color": "red"}) .then((response) => client.get(response.bodyFields['uri'])) .then((response) => print(response.body)) .whenComplete(client.close); 複製程式碼
-
Stream
- Stream中也涉及到Zone
- Dart另一種非同步操作
async*/yield
或 Stream非同步(async*/yield
是語法糖,最終被編譯器轉為Stream) - Stream也支援同步操作
- Stream中有Stream、StreamController、StreamSink、StreamSubscription四個關鍵物件
- Stream:事件源本身,可用於監聽事件或轉換事件,如listen、where
- StreamController:用於整個Stream過程的控制,提供各類介面用於建立各種事件流
- StreamSink:一般作為事件入口,提供如add、addStream等
- StreamSubscription:事件訂閱後物件,管理訂閱等各類操作,如cancel、pause,同時在內部也是事件中專的關鍵
- 一般通過StreamController建立Steam,通過SteamSink新增事件,通過Stream監聽事件,通過StreamSubscription管理訂閱
- Stream中支援各種變化,比如map、expand、where、take等操作,同時支援轉換為Future
-
訂閱
- Stream單播只能有一個訂閱者
Duration interval = Duration(seconds: 1); Stream<int> stream1 = Stream.periodic(interval, (data) => data); stream1.listen((_){ print("訂閱者01"); }); // Stream has already been listened to // stream.listen((_){ // print("訂閱者2"); // }); 複製程式碼
- Stream廣播可以多個訂閱者
var stream2 = Stream.fromIterable([1,2,3,4]); var broadcastStream = stream2.asBroadcastStream(); broadcastStream.listen((i){ print("訂閱者11:${i}"); }); broadcastStream.listen((i){ print("訂閱者12:${i}"); }); 複製程式碼
-
delayed 延時函式
Future.delayed(Duration(seconds: 3), () => print('1234')); 複製程式碼
-
Timer 定時器
Timer _countdownTimer; String _codeCountdownStr = '獲取驗證碼'; int _countdownNum = 59; void reGetCountdown() { setState(() { if (_countdownTimer != null) { return; } // Timer的第一秒倒數計時是有一點延遲的,為了立刻顯示效果可以新增下一行。 _codeCountdownStr = '${_countdownNum--}重新獲取'; _countdownTimer = new Timer.periodic(new Duration(seconds: 1), (timer) { setState(() { if (_countdownNum > 0) { _codeCountdownStr = '${_countdownNum--}重新獲取'; } else { _codeCountdownStr = '獲取驗證碼'; _countdownNum = 59; _countdownTimer.cancel(); _countdownTimer = null; } }); }); }); } // 不要忘記在這裡釋放掉Timer @override void dispose() { _countdownTimer?.cancel(); _countdownTimer = null; super.dispose(); } 複製程式碼
包與庫
-
包
- 包是一種封裝程式設計單元機制
- Dart的包管理器是Pub(Cocoapods for iOS, Gradle for Java)
- Pub有助於在儲存庫中安裝包。
- 包後設資料在pubsec.yaml中定義(Yet Another Markup Language)
- pub get(獲取應用程式所依賴的所有包)
- pub upgrade(將所有依賴項升級到較新版本)
- pub build(用於構建Web應用程式,建立一個包含所有相關指令碼的構建資料夾)
- pub help(將提供所有pub命令的幫助。)
-
庫
- Dart2中可以用library宣告庫名(也可以不用)
- 不同型別庫的引入方式
- Dart語言內部庫,import 'dart:html'
- Flutter庫檔案,import 'path/name.dart'
- 三方庫,import 'package:path/name.dart'
- URI檔案,import 'https://xxx/name.dart'
- 引入的庫可以用as起別名,防止庫變數名或函式名衝突(呼叫時用newName.methodName)
- 引入時用show/hide關鍵字可以單獨引入或剔除庫中某Api
- export重新匯入庫
- 當匯入的庫過多或要重新整合庫,可以通過export重新匯入,把部分庫或全部庫來組合或重新打包庫
- export也有show和hide,但沒有as庫字首
- 使用export重新匯入多個庫,同名成員(類,方法,變數)會衝突
- export重新匯入的庫相當於把庫內程式碼複製到當前檔案,但在當前檔案並不能使用,因為Dart不存在過載機制,所以會出現衝突
- 部分衝突可以用hide來將衝突部分隱藏來解決
- part和part of關聯檔案和庫
- 庫的拆分
- 我們可以將較大的庫檔案拆成幾個比較小的檔案,通過part關聯
- part關聯的檔案,共享所有的名稱空間,共享所有物件(包括私有物件)
- 如果使用part拆分,則必須用library宣告
- part引入的檔案可以使用url但不推薦,如part "https://xxx/xx.dart";
- part優先順序高於import,所以當前庫可以直接用part中的內容
- 延遲載入
- 使用deferred as 可以實現庫的延遲載入。
- 使用loadLibrary()進行載入可以多次呼叫但只執行一次,返回Future物件
- 延遲載入能1.減少App啟動時間,2.延遲載入那些較少使用的功能
- 依賴第三方庫
- 在pubspec.yaml檔案內dependencies:標籤下新增依賴如
cupertino_icons: ^0.1.2
- 新增依賴後可以用
flutter packages get
命令或工具欄packages get 按鈕 下載包 - pubspec.lock檔案可檢視匯入依賴的相容版本
- ^version 表示version<=libVersion < big version
- 常規依賴:dependencies:此標籤下依賴在除錯和發版後都會生效
- Dev依賴:dev_dependencies:此標籤下的依賴均在除錯時生效
- 重寫依賴:dependency_overrides:強制下載依賴包,不管是否相容,不推薦使用
知識點
-
獲取一些尺寸
// 螢幕寬高 var ScreenSize = MediaQuery.of(context).size; // 狀態列高度 var StatusHeight = MediaQuery.of(context).padding.top; 複製程式碼
-
判斷裝置型別
import 'dart:io'; if ( Platform.isIOS) { } else if (Platform.isAndroid) { } // Platform.isMacOS // Platform.isWindows // Platform.isLinux // Platform.isFuchsia 複製程式碼
-
時間
var today = DateTime.now(); print('當前時間是:$today'); var date1 = today.millisecondsSinceEpoch; print('當前時間戳:$date1'); var date2 = DateTime.fromMillisecondsSinceEpoch(date1); print('時間戳轉日期:$date2'); // 拼接成date var dentistAppointment = new DateTime(2019, 6, 20, 17, 30,20); print(dentistAppointment); // 字串轉date DateTime date3 = DateTime.parse("2019-06-20 15:32:41"); print(date3); // DateTime轉formatString // formatDate(DateTime ,[yyyy,'-',mm,'-',dd]); // 時間比較 print(today.isBefore(date3));// 在之前 print(today.isAfter(date3)); // 在之後 print(date3.isAtSameMomentAs(date3));// 相同 print(date3.compareTo(today));// 大於返回1;等於返回0;小於返回-1。 // print(DateTime.now().toString()); // print(DateTime.now().toIso8601String()); // 時間增加 var fiftyDaysFromNow = today.add(new Duration(days: 5)); print('today加5天:$fiftyDaysFromNow'); // 時間減少 DateTime fiftyDaysAgo = today.subtract(new Duration(days: 5)); print('today減5天:$fiftyDaysAgo'); // 時間差 兩個時間相差 小時數 print('比較兩個時間 差 小時數:${fiftyDaysFromNow.difference(fiftyDaysAgo)}'); print('本地時區簡碼:${today.timeZoneName}'); print('返回UTC與本地時差 小時數:${today.timeZoneOffset}'); print('獲取年月日:${today.year}');//month、day、hour、minute、second、millisecond、microsecond print('星期:${today.weekday}');// 返回星期幾 複製程式碼
-
剪貼簿 ClipBoard
testClipBoard() { // 賦值 ClipboardData willSetData = new ClipboardData(text: "剪貼簿的文字"); Clipboard.setData(willSetData); // 取值 getClipboardData(); } getClipboardData() async { var clipboardData = await Clipboard.getData(Clipboard.kTextPlain); if (clipboardData != null) { // 一些操作,如setState()中給控制元件賦值 print(clipboardData.text); // 剪貼簿的文字 } } 複製程式碼
-
URI 編碼解碼與組裝拆分
// <scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag> // https://<host>:<埠>/<路徑>?<查詢>#<片段> var uri1 = 'https://github.com/starainDou?tab=repositories'; var encodeFullURI = Uri.encodeFull(uri1); var encodeComponentURI = Uri.encodeComponent(uri1); var uri2 = Uri(scheme: 'https', host: 'google.com', path: '/News/today', fragment: 'frag'); var uri3 = Uri.parse('htps://google.com/News/tody#frag'); print('${Uri.decodeFull(encodeFullURI)}'); // https://github.com/starainDou?tab=repositories print('${Uri.decodeComponent(encodeComponentURI)}'); // https://github.com/starainDou?tab=repositories print('${uri2}'); // https://google.com/News/today#frag print('${uri3}'); // htps://google.com/News/tody#frag 複製程式碼
蒐集一些三方庫
-
網路請求http
包含一組高階函式和類,可輕鬆使用HTTP資源。與平臺無關,可在命令列和瀏覽器上用
-
網路請求dio
一個強大的Http客戶端,支援攔截器、全域性配置、FormData、請求取消、檔案下載、超時等
-
圖片選擇photo
用於選擇影象,支援多選,而且這個是用Flutter做的UI,可以很方便的自定義修改
-
圖片載入image
提供以各種不同的檔案格式載入、儲存和操作影象的能力。該庫不依賴於dart:io
-
svg圖片flutter_svg 載入svg影象
-
圖片檢視zoomable_image
提供影象檢視和手勢縮放操作功能