异步多线程结合
isolate
与进程
间的区别
-
isolate
除了拥有线程
,还有独立的内存空间
,但这个内存空间是局部的
,主要存储自己创造的对象或者数据
;传递数据的时候需要借助进程间的通信
。 -
isolate
并不像一个完整的进程那样,拥有虚拟内存
且操作系统会分配一个很大的内存空间。 - 开辟一个新的进程,计算机操作系统会分配一个
独立内存
,拥有独立的堆、栈
等;而isolate
是一个轻量级的,并不会去开辟堆、栈等
。
下面我们来学习异步
与多线程
相结合的用法
- 案例一:使用
箭头函数
// 忽略当前文件的警告
// ignore_for_file: avoid_print
import 'package:flutter/foundation.dart';
void main() => isoLoadfunc();
void isoLoadfunc() {
Future(() => compute(func, 123).then((value) => print('1结束')));
Future(() => compute(func, 123).then((value) => print('2结束')));
Future(() => compute(func, 123).then((value) => print('3结束')));
Future(() => compute(func, 123).then((value) => print('4结束')));
Future(() => compute(func, 123).then((value) => print('5结束')));
}
func(int message) {}
查看日志
打印结果是随机的,说明是多线程异步执行
;其中.then
的处理是在子线程中做的。
- 案例二:使用
花括号
// ignore_for_file: avoid_print
import 'package:flutter/foundation.dart';
void main() => isoLoadfunc();
void isoLoadfunc() {
Future(() {
compute(func, 123);
}).then((value) => print('1结束'));
Future(() {
compute(func, 123);
}).then((value) => print('2结束'));
Future(() {
compute(func, 123);
}).then((value) => print('3结束'));
Future(() {
compute(func, 123);
}).then((value) => print('4结束'));
Future(() {
compute(func, 123);
}).then((value) => print('5结束'));
}
func(int message) {}
查看日志
这里为什么又变成同步执行
呢?
注意: 箭头函数
默认包含return
,所以上面需要添加return
// ignore_for_file: avoid_print
import 'package:flutter/foundation.dart';
void main() => isoLoadfunc();
void isoLoadfunc() {
Future(() {
return compute(func, 123);
}).then((value) => print('1结束'));
Future(() {
return compute(func, 123);
}).then((value) => print('2结束'));
Future(() {
return compute(func, 123);
}).then((value) => print('3结束'));
Future(() {
return compute(func, 123);
}).then((value) => print('4结束'));
Future(() {
return compute(func, 123);
}).then((value) => print('5结束'));
}
func(int message) {}
查看日志
不加return
的话,.then
是在主线程中处理的;添加return
,.then
是在子线程中处理的;其中Future
中添加compute
子线程是同步添加的。
- 案例三:
异步任务
与微任务
的结合
void main() {
Future x = Future(() {
print('异步任务1');
scheduleMicrotask(() {
print('微任务1');
});
});
x.then((value) {
print('微任务2');
});
}
查看日志
由日志可以看出,.then
是与Future();
是一体的。
- 案例四:下面验证
.then
与Future();
是一体的
void main() {
Future x = Future(() {
print('异步任务1');
scheduleMicrotask(() {
print('微任务1');
});
});
x.then((value) {
print('微任务2');
});
x.whenComplete(() {
print('完毕');
});
}
查看日志
Future();
中.then
、. whenComplete
是链式调用,可以看成是一体的。
- 案例五:
Timer
开启异步任务
void main() {
Timer.run(() {
print('异步任务');
});
print('来了');
}
查看日志
下面打开之前的wechat_demo
工程,切到chat_page.dart
页面,添加如下代码
@override
void initState() {
super.initState();
int _count = 0;
// 添加异步任务
Timer.periodic(Duration(seconds: 1), (timer) {
_count++;
print(_count);
if (_count == 99) {
timer.cancel();
}
});
......
添加完上面代码,滑动聊天页面
并没有卡顿,说明Flutter
中Timer
优化的很好,不会卡住主线程。切换页面也没有问题,是因为我们之前保存了页面状态,如果我们把保存页面状态的参数改为bool get wantKeepAlive => false;
,切换页面就会发生内存泄漏
。
- 添加页面销毁方法,查看切换页面的时候,
wechatPage
页面是否销毁
@override
void dispose() {
// TODO: implement dispose
super.dispose();
print('chatPage销毁了');
}
查看日志
由日志可以看出wechatPage
页面销毁了,但是Timer
并没有销毁,依然在打印。所以要在页面销毁的时候,销毁定时器
。
Timer _timer;
@override
void dispose() {
// TODO: implement dispose
print('chatPage销毁了');
if (_timer != null && _timer.isActive) {
_timer.cancel();
}
// super.dispose();要放在最后面
super.dispose();
}
@override
void initState() {
super.initState();
int _count = 0;
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
_count++;
print(_count);
if (_count == 99) {
timer.cancel();
}
});
......
请思考一个问题:我们什么时候使用异步?什么时候使用多线程?
-
AppBar
里面添加一个耗时操作
GestureDetector(
onTap:() {
Future(() {
print('开始');
// 耗时操作
for(int i = 0; i < 1000000; i ++) {}
print('结束了');
});
},
child: Container(
child: Icon(Icons.add),
),
)
添加耗时操作
上面的耗时操作必须放入子线程
GestureDetector(
onTap:() {
Future(() {
return compute(func, 123);
});
},
child: Container(
child: Icon(Icons.add),
),
)
// 注意:该方法写在class _ChatPageState外面
func (int message) {
print('开始');
// 耗时操作
for(int i = 0; i < 1000000; i ++) {}
print('结束了');
}
三方库Dio下载
打开future_demo
工程,我们来使用Dio
下载文件
- 配置
Dio
三方库
Pub get
引入之后,External Libraries
-> Dart Packages
目录下能看到dio-4.0.4
库。
- 使用
Dio
下载文件
import 'package:dio/dio.dart';
void main() {
// 发送网络请求
// 1. 创建dio对象
final dio = Dio();
// 2. 下载数据
var downloadUrl =
'https://edu-files-1251502357.cos.ap-shanghai.myqcloud.com/CourseTeacher_2.8.1.13_DailyBuild.dmg';
// 3. 开始下载,保存目录
// 这里如果没有123目录,会自己创建
dio.download(downloadUrl, '/Users/wn/Desktop/123/TX.dmg',
// 下载进度
onReceiveProgress: showDownloadProgress)
.then((value) => print(value))
.whenComplete(() => print('下载结束'))
.catchError((e) => print(e));
}
void showDownloadProgress(int count, int total) {
print('count:$count total:$total');
if (total != -1) {
print((count / total * 100).toStringAsFixed(0)+'%');
}
}
正在下载
下面把下载逻辑抽取出来
import 'package:dio/dio.dart';
void main() {
// 发送网络请求
// 1. 创建dio对象
final dio = Dio();
// 2. 下载数据
var downloadUrl =
'https://edu-files-1251502357.cos.ap-shanghai.myqcloud.com/CourseTeacher_2.8.1.13_DailyBuild.dmg';
// 3. 保存目录
String savePath = '/Users/wangning/Desktop/123/TX.dmg';
// 4. 开始下载
download(dio, downloadUrl, savePath);
}
// 下载方式一
void download(Dio dio, String url, savePath) {
dio.download(url, savePath,
onReceiveProgress: showDownloadProgress)
.then((value) => print(value))
.whenComplete(() => print('下载结束'))
.catchError((e) => print(e));
}
// 下载方式二
void download1(Dio dio, String url) {
// 手机端保存路径,沙盒路径
String iOSPath = Directory.systemTemp.path+'/TX.dmg';
print(iOSPath);
dio.download(url, (header) {
// 回调的方式,返回保存路径
return iOSPath;
}, onReceiveProgress: showDownloadProgress)
.whenComplete(() => print('下载结束'))
.catchError((e) => print(e));
}
void showDownloadProgress(int count, int total) {
print('count:$count total:$total');
if (total != -1) {
print((count / total * 100).toStringAsFixed(0)+'%');
}
}
保存沙盒路径
flutter: /Users/wn/Library/Developer/CoreSimulator/Devices/141ABB45-E8A8-461C-86EF-9F1A3493DD0C/data/Containers/Data/Application/A18A7F52-FB54-4C7A-8F07-D74DB71703FF/tmp/TX.dmg
封装网络请求与切换项目请求库
我们在wechat_demo
工程的chat_page.dart
文件中进行网络请求,代码如下
Future<List<Chat>> getDatas() async {
_cancelConnect = false;
final url =
Uri.parse('http://rap2api.taobao.org/app/mock/256798/api/chat/list');
//发送请求
final response = await http.get(url);
if (response.statusCode == 200) {
......
我们使用的是http
中的方法http.get
,如果以后我们需要更换网络请求库,就需要在很多地方修改,所以我们要把http
中的方法进行封装,使用我们自己封装的方法。下面进行网络库的封装......
- 新建
tools
目录,再创建网络请求文件http_manager.dart
import 'package:http/http.dart' as http;
class HttpManager {
// Url是Uri的子集,只是Url定义的更详细一些,我们一般常用的是Url
Future<http.Response> get(Uri url, {Map<String, String> headers}) {
return http.get(url);
}
}
// 外面使用方式
HttpManager().get(url);
将来如果需要替换,只需要更改HttpManager
中的方法即可。
下面我们把dio
库进行封装
-
pubspec.yaml
文件中配置dio: ^4.0.4
,点击Pub get
引入 - 下面对
dio
网络请求进行封装
<!-- http_manager.dart文件 -->
import 'package:dio/dio.dart';
// 网络请求方法类型
enum HttpMethod { GET, POST }
class HttpManager {
//创建Dio单例对象,防止每次发送网络请求都重新创建Dio()对象
static Dio _dioInstance;
static Dio _getDioInstance() {
if (_dioInstance == null) {
_dioInstance = Dio();
}
return _dioInstance;
}
//返回我们自己的Response
static Future<Response> get(String url,
{Map<String, dynamic> queryParameters}) async {
return await _sendRequest(HttpMethod.GET, url,
queryParameters: queryParameters);
}
static Future<Response> post(String url,
{Map<String, dynamic> queryParameters, dynamic data}) async {
return await _sendRequest(HttpMethod.POST, url,
queryParameters: queryParameters, data: data);
}
// queryParameters请求参数,添加下划线表示私有方法
static Future _sendRequest(HttpMethod method, String url,
{Map<String, dynamic> queryParameters, dynamic data}) async {
try {
switch (method) {
case HttpMethod.GET:
return await HttpManager._getDioInstance()
.get(url, queryParameters: queryParameters);
case HttpMethod.POST:
return await HttpManager._getDioInstance()
.post(url, queryParameters: queryParameters, data: data);
default:
throw Exception('请求方式错误');
}
} on DioError catch (e) {
print(e.message);
} on Exception catch (e) {
print(e.toString());
}
return null;
}
}
-
HttpManager
封装类的使用
<!-- chat_page.dart文件 -->
Future<List<Chat>> getDatas() async {
_cancelConnect = false;
//发送请求
final response = await HttpManager.get(
'http://rap2api.taobao.org/app/mock/256798/api/chat/list');
if (response.statusCode == 200) {
//获取响应数据,转成Map类型 不需要转换了!不是Json,直接是Map了
// final responsBody = json.decode(response.data);
//map 作为List的遍历方法。
List chatList = response.data['chat_list']
.map<Chat>((item) => Chat.formMap(item))
.toList();
return chatList;
} else {
throw Exception('statusCode:${response.statusCode}');
}
}
自定义searchCell
聊天页面chat_page.dart
文件添加搜索框
滑动聊天页我们发现搜索框跟着页面一块滑动,说明搜索框是ListView
里面的元素;最简单的思路就是添加一个Cell
。
- 选中下面代码,按快捷键
Cmd + option + M
,弹出的输入框中输入_itemBuilderForRow
,会把选中的代码抽成Widget
小部件。
- 查看抽取出来的小部件
Widget _itemBuilderForRow(BuildContext context, int index) {
// index==0 引用自定义的SearchCell
if (index == 0) {
return SearchCell(
datas: _datas,
);
}
//保证从模型数据正确取数据。从0开始!
index--;
return ListTile(
title: Text(_datas[index].name),
subtitle: Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.only(
right: 10,
),
height: 25,
child: Text(
_datas[index].message,
overflow: TextOverflow.ellipsis,
),
),
leading: Container(
width: 44,
height: 44,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
image:
DecorationImage(image: NetworkImage(_datas[index].imageUrl))),
),
);
}
// 查看引用小部件的代码
body: Container(
child: Container(
child: _datas.length == 0
? Center(
child: Text('Loading...'),
)
: ListView.builder(
// 因为顶部要多一个搜索Cell,所以_datas.length + 1
itemCount: _datas.length + 1,
itemBuilder: _itemBuilderForRow,
),
),
),
- 新建
search_cell.dart
文件封装SearchCell
搜索框
import 'package:flutter/material.dart';
import 'package:wechat/const.dart';
class SearchCell extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
print('点击了搜索框!');
},
child: Container(
height: 44,
color: WeChatThemeColor,
padding: EdgeInsets.all(5),
child: Stack(
alignment: Alignment.center, //搜索图标文字上下居中
children: [
Container(
// 设置圆角
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6.0),
),
), //白底
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image(
image: AssetImage('images/放大镜b.png'),
width: 15,
color: Colors.grey),
Text(' 搜索', style: TextStyle(fontSize: 15, color: Colors.grey))
],
),
],
),
),
);
}
}
-
appBar
底部添加黑色线条
return Scaffold(
appBar: AppBar(
// appBar下面添加黑色线条
elevation: 0.0,
......
运行效果
网友评论