[译]Flutter for Android Developers - Async UI,这份火爆全网的452页Android Framework内核解析

);
}
}

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

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

class _SampleAppPageState extends State {
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);
});
}
}

界面展示的部分与FFAD-Views中介绍的StatefulWidget的展示没有太大区别,我们声明的loadData异步方法在_SampleAppPageState的initState方法中调用,于是触发异步加载数据,await表达式挂起整个异步操作,直到http.get(dataURL)返回时通过setState更新widgets成员变量,进而触发build方法重新调用以更新ListView中的item。

通过协程实现的异步方法通常能够帮助我们在main isolate去执行一些耗时操作并且不会阻塞界面更新。但是有时候我们需要处理大量的数据,就算我们将该操作声明为异步方法依然可能会导致阻塞界面更新,因为通过协程来实现的异步方法说到底还是运行于一个线程之上,在一个线程上去调度执行毕竟算力有限。

这时候我们可以利用多核CPU的优势去完成这些耗时的或CPU密集型的操作。这正是通过前面介绍的isolate来实现。下面的例子展示了如何创建一个isolate,并且如何在创建的isolate和main isolate之间通信来将数据传递回main isolate进而更新界面:

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

简单解释一下,这段代码主要声明了三个方法。

loadData被声明为一个异步方法,其内部的代码运行于main isolate中。该方法首先声明了一个用于main isolate从其他isolate接受消息的ReceivePort。接着通过spawn命名构造方法生成了一个isolate,为了后续描述简单这里姑且叫它x isolate。该isolate将会以构造时传入的第一个参数dataLoader方法作为运行的入口函数。即生成x isolate后,在x isolate中会开始执行dataLoader方法。构造x isolate时传入的第二个参数是通过main isolate中的ReceivePort获得的一个SendPort,这个SendPort会在dataLoader被执行时传递给它。在x isolate中可以用该SendPort向main isolate发送消息进行通信。 接下来通过receivePort.first获取x isolate发送过来的消息,这里获取到的其实是一个x isolate的SendPort对象,在main isolate中可以利用这个SendPort对象向x isolate中发送消息。 接下来调用sendReceive方法并传入刚刚获得的x isolate的SendPort对象和一个字符串作为参数。 最后调用setState方法触发界面更新。

dataLoader也被声明为一个异步方法,其内部的代码运行于x isolate中。在构建了x isolate后该方法开始在x isolate中执行,要注意的是dataLoader方法的参数是一个SendPort类型的对象,这正是前面构造x isolate时传入的第二个参数,也就是说,前面通过Isolate.spawn命名构造方法构造一个isolate时,传入的第二个参数的用途就是将其传递给第一个参数所表示的入口函数。在这里该参数表示的是main isolate对应的SendPort,通过它就可以在x isolate中向main isolate发送消息。 在dataLoader方法中首先生成了一个x isolate的ReceivePort对象,然后就用main isolate对应的SendPort向main isolate发送了一个消息,该消息其实就是x isolate对应的SendPort对象,所以回过头去看loadData方法中通过receivePort.first获取到的一个SendPort就是这里发送出去的。在main isolate中接收到这个SendPort后,就可以利用该SendPort向x isolate发送消息了。 接下来dataLoader方法则挂起等待x isolate的ReceivePort接受到消息。

sendReceive被声明为一个普通方法,该方法运行于main isolate中,它是在loadData中被调用的。调用sendReceive时传入的第一个参数就是在main isolate中从x isolate接收到的其对应的SendPort对象,所以在sendReceive方法中利用x isolate对应的这个SendPort对象就可以在main isolate中向x isolate发送消息。在这里发送的消息是一个数组[msg, response.sendPort]。消息发送后在dataLoader方法中await挂起的代码就会开始唤醒继续执行,取出传递过来的参数,于是在x isolate中开始执行网络请求的逻辑。 接着将请求结果再通过main isolate对应的SendPort传递给main isolate。于是在sendReceive方法中通过response.first获取到x isolate传递过来的网络请求结果。 最终在setState方法中使用网络请求回来的结果更新数据集触发界面更新。

完整的例子代码:

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

}

小结:

  1. 在Flutter中一般情况下不需要runOnUiThread,AsyncTask,IntentService等类似的概念,因为Dart是基于单线程模型的。异步方法的执行也是通过协程实现的,其实际也还是运行于main isolate中。
  2. Dart中的代码都是运行在isolate中的,各个isolate之间的内存是没法直接共享的。但是可以通过ReceivePort和SendPort来实现isolate之间的通信。每个isolate都有自己对应的ReceivePort和SendPort,ReceivePort用于接受其他isolate发送过来的消息,SendPort则用于向其他isolate发送消息。关于ReceivePort和SendPort更多详情可以参阅官方文档

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

710929757605)]
[外链图片转存中…(img-2pp1DbfP-1710929757605)]
[外链图片转存中…(img-kttLmSEe-1710929757606)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-cirs8Dka-1710929757606)]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值