天天看點

flutter實作異步UI

1.runOnUiThread 在Flutter中等價于什麼

Dart是單線程執行模型,支援Isolates(在另一個線程上運作Dart代碼的方式)、事件循環和異步程式設計。 除非您啟動一個Isolate,否則您的Dart代碼将在主UI線程中運作,并由事件循環驅動。可以在UI線程上運作網絡請求代碼而不會導緻UI挂起,因為網絡請求是異步的:

loadData() async {
            String dataURL = "https://jsonplaceholder.typicode.com/posts";
            try{
      Response response = await Dio().get(dataURL,options: Options(responseType: ResponseType.json),);
              setState(() {// 更新UI
                json = response.data;
              });
            }catch(e){
              print(e);
            }
}      

要更新UI,可以調用setState,這會觸發build方法再次運作并更新資料。

2.AsyncTask和IntentService在Flutter中等價于什麼

在Android中,當你想通路一個網絡資源時,你通常會建立一個AsyncTask,它将在UI線程之外運作代碼來防止你的UI被阻塞。 AsyncTask有一個線程池,可以為你管理線程。

由于Flutter是單線程的,運作一個事件循環(如Node.js),是以您不必擔心線程管理或者使用AsyncTasks、IntentServices。

要異步運作代碼,可以将函數聲明為異步函數,并在該函數中等待這個耗時任務:

loadData() async {
            String dataURL = "https://jsonplaceholder.typicode.com/posts";
            try{
      Response response = await Dio().get(dataURL,options: Options(responseType: ResponseType.json),);
              setState(() {// 更新UI
                json = response.data;
              });
            }catch(e){
              print(e);
            }
}      

這就是典型的進行網絡或資料庫調用的方式

在Android上,當您繼承AsyncTask時,通常會覆寫3個方法,OnPreExecute、doInBackground和onPostExecute。 在Flutter中沒有這種模式的等價物,隻需等待一個長時間運作的函數,而Dart的事件循環将負責其餘的事情。

但是,有時可能需要處理大量資料,導緻UI可能會挂起。在Flutter中,可以利用多個CPU核心來執行耗時或計算密集型任務。 這是通過使用Isolates來完成的。

是一個獨立的執行線程,它運作時不會與主線程共享任何記憶體。這意味着你不能從該線程通路變量或通過調用setState來更新你的UI。

List messages;
  loadData() async {
    // 目前線程的資訊的接收者
    ReceivePort receivePort = new ReceivePort();
    /**
     * 建立并生成與目前隔離共享相同代碼的隔離。
     * Isolate.spawn 接受的方法必須是靜态的或是一個頂層函數
     * 傳遞目前接收者receivePort.sendPort給其他線程,那麼其他線程就可以通過它,與目前線程通信
     * 
     */
    await Isolate.spawn(dataLoader, receivePort.sendPort);
    
    SendPort sendPort = await receivePort.first;
    String dataURL = "https://jsonplaceholder.typicode.com/posts";
    /// 向其他線程發送消息
    var msg = await sendReceive(sendPort,dataURL);
    setState(() {
      /// 更新UI,setState會觸發build的執行
      messages = msg;
      print(messages);
    });
  }

  static dataLoader(SendPort sendPort) async{
    /// 其他線程的消息接收者
    ReceivePort receivePort = new ReceivePort();
    /// 通知其他isolate,目前isolate監聽的端口
    sendPort.send(receivePort.sendPort);
    await for(var msg in receivePort){
      String data = msg[0]; // 擷取URL
      SendPort replyTo = msg[1]; // 回傳消息用的
      String dataURL = data;
      /// 進行網絡請求
      Response response = await Dio().get(dataURL,options: Options(responseType: ResponseType.json),);
      /// 将結果回傳回去
      replyTo.send(response.data);
    }
  }

  /// 向其他線程發送消息
  Future sendReceive(SendPort port,msg){
    /// 目前線程的接收者
    ReceivePort receivePort =  new ReceivePort();
    /// 向其他線程發送消息
    port.send([msg,receivePort.sendPort]);
    return receivePort.first;
  }      

完整示例:

import 'dart:convert';
import 'dart:isolate';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';

void main() => runApp(DemoApp());

class DemoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: '導航示範1',
      home: new MyAppHome(),
    );
  }
}

class MyAppHome extends StatefulWidget {
  @override
  MyAppHomeState createState() => MyAppHomeState();
}

class MyAppHomeState extends State<MyAppHome> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Hello"),
      ),
      body: Center(
        child: RaisedButton(
          child: Text("Go"),
          onPressed: () {
            loadData();
          },
        ),
      ),
    );
  }

  List messages;
  loadData() async {
    // 目前線程的資訊的接收者
    ReceivePort receivePort = new ReceivePort();
    /**
     * 建立并生成與目前隔離共享相同代碼的隔離。
     * Isolate.spawn 接受的方法必須是靜态的或是一個頂層函數
     * 傳遞目前接收者receivePort.sendPort給其他線程,那麼其他線程就可以通過它,與目前線程通信
     *
     */
    await Isolate.spawn(dataLoader, receivePort.sendPort);

    SendPort sendPort = await receivePort.first;
    String dataURL = "https://jsonplaceholder.typicode.com/posts";
    /// 向其他線程發送消息
    List msg = await sendReceive(sendPort,dataURL);
    setState(() {
      /// 更新UI,setState會觸發build的執行
      messages = msg;
      print(messages[0]["title"]);
    });
  }

  static dataLoader(SendPort sendPort) async{
    /// 其他線程的消息接收者
    ReceivePort receivePort = new ReceivePort();
    /// 通知其他isolate,目前isolate監聽的端口
    sendPort.send(receivePort.sendPort);
    await for(var msg in receivePort){
      String data = msg[0]; // 擷取URL
      SendPort replyTo = msg[1]; // 回傳消息用的
      String dataURL = data;
      /// 進行網絡請求
      Response response = await Dio().get(dataURL);
      /// 将結果回傳回去
      replyTo.send(jsonDecode(response.toString()));
    }
  }

  /// 向其他線程發送消息
  Future sendReceive(SendPort port,msg){
    /// 目前線程的接收者
    ReceivePort receivePort =  new ReceivePort();
    /// 向其他線程發送消息
    port.send([msg,receivePort.sendPort]);
    return receivePort.first;
  }

}