Flutter与android的对比---UI 中的异步

2019-01-10  本文已影响20人  韩明泽

本文是在GitHub上一个flutter项目的资料中看到的,由于原文过于太长,因此对其进行了章节拆分方便阅读,此篇为原文的部分内容,后续内容会在今后陆续更新完;如果想查看该项目请跳转GitHub查看。

注:GitHub的这篇资料是转载的flutter中文官网的文章,这里是原文地址。

UI 中的异步

  • runOnUiThread 在 Flutter 中对应什么方法?
  • Android 中的 AsyncTask 或 IntentService 在 Flutter 对应什么?
  • Android 中的 OkHttp 在 Flutter 中对应什么?
  • 在 Flutter 中,当有任务在执行时,如何显示进度?

runOnUiThread 在 Flutter 中对应什么方法?

Dart 是单线程执行模型、支持 Isolate(一种多线程模型)、事件循环和异步编程的。除非使用 Isolate,不然你的 Dart 代码都是在 UI 线程中进行并由事件循环器进行驱动。

例如你可以在 UI 线程执行网络请求而不会导致 UI 线程的阻塞:

loadData() async {
  String dataURL = "https://jsonplaceholder.typicode.com/posts";
  http.Response response = await http.get(dataURL);
  setState(() {
    widgets = JSON.decode(response.body);
  });
}

通过调用 setState 方法触发界面的重新构建来刷新并更新数据。

下面是一个完整的获得网络数据并在 ListView 上进行更新的例子:

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List widgets = [];

  @override
  void initState() {
    super.initState();

    loadData();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("Sample App"),
        ),
        body: new ListView.builder(
            itemCount: widgets.length,
            itemBuilder: (BuildContext context, int position) {
              return getRow(position);
            }));
  }

  Widget getRow(int i) {
    return new Padding(
        padding: new EdgeInsets.all(10.0),
        child: new Text("Row ${widgets[i]["title"]}")
    );
  }

  loadData() async {
    String dataURL = "https://jsonplaceholder.typicode.com/posts";
    http.Response response = await http.get(dataURL);
    setState(() {
      widgets = JSON.decode(response.body);
    });
  }
}

Android 中的 AsyncTaskIntentService 在 Flutter 对应什么?

在 Android 中进行网络操作时通常会使用 AsyncTask,以避免主线程遭到阻塞。与此同时 AsyncTask 会有一个线程池专门为你管理线程。

由于 Flutter 是单线程并由事件驱动(类似 Node.js),因此你不必为线程管理或需要类似的 AsyncTask 和 IntentService 而感到担忧。

需要异步执行的时候只要将方法声明为异步方法并在方法中使用 await 来等待即可。

loadData() async {
  String dataURL = "https://jsonplaceholder.typicode.com/posts";
  http.Response response = await http.get(dataURL);
  setState(() {
    widgets = JSON.decode(response.body);
  });
}

以上就是你通常进行网络或数据库操作的方式。

在 Android 中,当继承 AsyncTask 的时候,通常要重载它的 3 个方法,OnPreExecute, doInBackground 和 onPostExecute。而在 Flutter 中没有这种麻烦事,你要做的仅仅是 await 一个长时间的操作,剩下的事 Dart 的事件循环机制会帮你搞定。

但是,有时你可能会处理一些数据量较大较密集的操作,Flutter 的 UI 还是可能会受到影响。

在这种情况下,Flutter 中还是有和 AsyncTask 类似的解决方案。在 Flutter 中可以利用 CPU 多核的性质来并行处理事务,而这一工作则是由 Isolate 完成。

Isolate 是独立的执行线程,和主线程不共享任何内存。这意味着你不能在 Isolate 中给主线程的变量赋值或者调用 setState 方法来更新 UI。

让我们看一个 Isolate 的简单例子,学习下 Isolate 如何与主线程交流并共享数据来更新 UI

loadData() async {
    ReceivePort receivePort = new ReceivePort();
    await Isolate.spawn(dataLoader, receivePort.sendPort);

    // The 'echo' isolate sends it's SendPort as the first message
    SendPort sendPort = await receivePort.first;

    List msg = await sendReceive(sendPort, "https://jsonplaceholder.typicode.com/posts");

    setState(() {
      widgets = msg;
    });
  }

// the entry point for the isolate
  static dataLoader(SendPort sendPort) async {
    // Open the ReceivePort for incoming messages.
    ReceivePort port = new ReceivePort();

    // Notify any other isolates what port this isolate listens to.
    sendPort.send(port.sendPort);

    await for (var msg in port) {
      String data = msg[0];
      SendPort replyTo = msg[1];

      String dataURL = data;
      http.Response response = await http.get(dataURL);
      // Lots of JSON to parse
      replyTo.send(JSON.decode(response.body));
    }
  }

  Future sendReceive(SendPort port, msg) {
    ReceivePort response = new ReceivePort();
    port.send([msg, response.sendPort]);
    return response.first;
  }

dataLoader方法在它独立的 Isolate 中运行,你可以在其中执行更多的 CPU 密集型处理,例如解析一万行以上的 JSON 数据或执行密集型数学计算。

可以参考下面完整的例子:

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:async';
import 'dart:isolate';

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List widgets = [];

  @override
  void initState() {
    super.initState();
    loadData();
  }

  showLoadingDialog() {
    if (widgets.length == 0) {
      return true;
    }

    return false;
  }

  getBody() {
    if (showLoadingDialog()) {
      return getProgressDialog();
    } else {
      return getListView();
    }
  }

  getProgressDialog() {
    return new Center(child: new CircularProgressIndicator());
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("Sample App"),
        ),
        body: getBody());
  }

  ListView getListView() => new ListView.builder(
      itemCount: widgets.length,
      itemBuilder: (BuildContext context, int position) {
        return getRow(position);
      });

  Widget getRow(int i) {
    return new Padding(padding: new EdgeInsets.all(10.0), child: new Text("Row ${widgets[i]["title"]}"));
  }

  loadData() async {
    ReceivePort receivePort = new ReceivePort();
    await Isolate.spawn(dataLoader, receivePort.sendPort);

    // The 'echo' isolate sends it's SendPort as the first message
    SendPort sendPort = await receivePort.first;

    List msg = await sendReceive(sendPort, "https://jsonplaceholder.typicode.com/posts");

    setState(() {
      widgets = msg;
    });
  }

// the entry point for the isolate
  static dataLoader(SendPort sendPort) async {
    // Open the ReceivePort for incoming messages.
    ReceivePort port = new ReceivePort();

    // Notify any other isolates what port this isolate listens to.
    sendPort.send(port.sendPort);

    await for (var msg in port) {
      String data = msg[0];
      SendPort replyTo = msg[1];

      String dataURL = data;
      http.Response response = await http.get(dataURL);
      // Lots of JSON to parse
      replyTo.send(JSON.decode(response.body));
    }
  }

  Future sendReceive(SendPort port, msg) {
    ReceivePort response = new ReceivePort();
    port.send([msg, response.sendPort]);
    return response.first;
  }

}

Android 中的 OkHttp 在 Flutter 中对应什么?

在 Flutter 中使用http扩展库将使网络通信变得异常简单。

虽然 http 扩展库没有实现 OkHttp 的所有功能,但其抽象出了很多常用的功能,使得原本你要自己实现的网络调用变成一些极为简单的方法。
可以去pub上面下载,链接:https://pub.dartlang.org/packages/http

你也可以在 pubspec.yaml 添加 http 包的依赖来使用它:

dependencies:
  ...
  http: '>=0.11.3+12'

接着就可以进行网络请求了,如下:

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
[...]
  loadData() async {
    String dataURL = "https://jsonplaceholder.typicode.com/posts";
    http.Response response = await http.get(dataURL);
    setState(() {
      widgets = JSON.decode(response.body);
    });
  }
}

一旦得到了需要的数据,就可以通过调用 setState 方法通知 Flutter 将网络调用的结果更新到 UI 上。

在 Flutter 中,当有任务在执行时,如何显示进度?

在 Android 中,当你执行长时间的任务时,会在界面上显示一个进度指示器表明当前有任务在执行。

在 Flutter 中可以通过进度指示器Widget来实现。你可以通过一个 boolean 值来告诉 Flutter 是否需要显示进度指示器。

在下面这个例子中,我们将 build 方法分解为三个不同的方法。如果 showLoadingDialog 为 true 时显示进度指示器,否则将显示带有数据的 ListView:

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List widgets = [];

  @override
  void initState() {
    super.initState();
    loadData();
  }

  showLoadingDialog() {
    if (widgets.length == 0) {
      return true;
    }

    return false;
  }

  getBody() {
    if (showLoadingDialog()) {
      return getProgressDialog();
    } else {
      return getListView();
    }
  }

  getProgressDialog() {
    return new Center(child: new CircularProgressIndicator());
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("Sample App"),
        ),
        body: getBody());
  }

  ListView getListView() => new ListView.builder(
      itemCount: widgets.length,
      itemBuilder: (BuildContext context, int position) {
        return getRow(position);
      });

  Widget getRow(int i) {
    return new Padding(padding: new EdgeInsets.all(10.0), child: new Text("Row ${widgets[i]["title"]}"));
  }

  loadData() async {
    String dataURL = "https://jsonplaceholder.typicode.com/posts";
    http.Response response = await http.get(dataURL);
    setState(() {
      widgets = JSON.decode(response.body);
    });
  }
}
上一篇 下一篇

猜你喜欢

热点阅读