[Flutter] Flutter之Android开发者教程(三)(自译)

文档地址:https://flutter.io/docs/get-started/flutter-for/android-devs

译者:Haocxx

异步UI

在Flutter中runOnUiThread相当于什么?

    Dart有一个单线程执行模型,支持Isolate(一种在其他线程运行Dart代码的方式),事件循环和异步编程。Dart代码在主线程上运行并由事件循环驱动,除非使用Isolate。Flutter的事件循环相当于是Android的主Looper。换句话说,这个Looper是与主线程绑定的。

    Dart的单线程模型并不意味着你要连阻塞UI的耗时操作都在里面同步运行。不像Android要求主线程一直处于空闲状态,在Flutter中,可以使用Dart语言提供的异步执行工具来实现异步操作,比如async/await。如果你曾经在C#/JavaScript使用过它或者使用过Kotlin的协程(Coroutine),就会对async/await非常熟悉。

    比如,你可以使用async/await让Dart异步执行网络请求操作,使其不阻塞UI更新:

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

    一旦处于await的网络请求完成了,就通过调用setState()来更新UI,使Widget的子树重新构建并更新数据。

    下面的例子实现了ListView的异步加载和显示数据:

import 'dart:convert';

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

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

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

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

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

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

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

    loadData();
  }

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

  Widget getRow(int i) {
    return Padding(
      padding: EdgeInsets.all(10.0),
      child: 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的区别。

如何实现后台线程执行任务?

    在Android中,如果想要获取网络资源的话,通常会将其放到后台线程中运行以防止阻塞UI线程,避免ANR。比如,可以使用AsyncTask,LiveData,IntentService,JobScheduler或者RxJava来实现后台线程执行。

    由于Flutter是单线程的,并且由事件循环驱动(像Node.js),你不必担心线程管理和线程的创建。如果你要进行IO操作,比如读取磁盘和网络数据请求,只需要使用async和await就可以了。如果你需要进行密集的运算,就需要将其放到Isolate中避免阻塞事件循环,就像在Android中避免耗时操作阻塞UI线程一样。

    对于IO操作,只需要讲操作方法声明为async方法,并将耗时的操作用await修饰:

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

    这就是通常进行网络或数据库操作的方法,两者都属于IO操作。

    在Android中,当继承AsyncTask的时候,通常要复写三个方法,onPreExecute(),doInBackground()和onPostExecute()。在Flutter中没有这样的用法。你只要用await修饰耗时的操作,Dart的事件循环就会自己处理。

    然而,有的时候可能要处理非常巨大的数据。在Flutter中,就可以使用Isolate来利用多核运算的优势来完成密集的耗时运算。

    Isolate是独立的执行线程,不会与主执行线程共享内存栈,也就无法获取主线程中的变量,或调用setState()来更新UI。不像Android的线程,Isolate名副其实(Isolate意为“隔离”),无法共享内存。

    下面是实现Isolate中获取的数据交给主线程并更新UI的方法:

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

  // The 'echo' isolate sends its 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 = 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 = ReceivePort();
  port.send([msg, response.sendPort]);
  return response.first;
}

    其中,dataLoader()中的逻辑将会在Isolate的独立执行线程中运行。在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(SampleApp());
}

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

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

  @override
  _SampleAppPageState createState() => _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 Center(child: CircularProgressIndicator());
  }

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

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

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

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

    // The 'echo' isolate sends its 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 = 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 = ReceivePort();
    port.send([msg, response.sendPort]);
    return response.first;
  }
}

在Flutter中,OkHttp等于什么?

    在Flutter中,如果使用http库,做网络数据请求是非常容易的。

    虽然http库没有实现OkHttp中所有的feature功能,但是它封装了很多常用的网络方法,使网络请求变得简单。

    要使用http库,需要在pubspec.yaml中添加如下依赖:

dependencies:
  ...
  http: ^0.11.3+16

    进行网络数据请求,可以使用await修饰的异步方法http.get():

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);
    });
  }
}

如何为一个耗时操作显示进度等待?

    在Android中,通常使用ProgressBar来显示后台进程在执行加载。

    在Flutter中,可以使用ProgressIndicator。在渲染Widget的时候,通过boolean值来控制ProgressIndicator的显示状态,并在后台线程执行的开始时候通知其显示,结束的时候控制其隐藏。

    在下面的例子中,build方法被分为三个方法。如果showLoadingDialog()为true,就显示ProgressIndicator。否则就显示获取到网络数据的ListView。

import 'dart:convert';

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

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

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

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

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

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

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

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

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

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

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

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

  Widget getRow(int i) {
    return Padding(padding: EdgeInsets.all(10.0), child: 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);
    });
  }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值