网络与项目实战

2021-12-14  本文已影响0人  浅墨入画

异步多线程结合

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();是一体的。

void main() {
  Future x = Future(() {
    print('异步任务1');
    scheduleMicrotask(() {
      print('微任务1');
    });
  });
  x.then((value) {
    print('微任务2');
  });
  x.whenComplete(() {
    print('完毕');
  });
}
查看日志

Future();.then. whenComplete是链式调用,可以看成是一体的。

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

添加完上面代码,滑动聊天页面并没有卡顿,说明FlutterTimer优化的很好,不会卡住主线程。切换页面也没有问题,是因为我们之前保存了页面状态,如果我们把保存页面状态的参数改为bool get wantKeepAlive => false;,切换页面就会发生内存泄漏

@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();
      }
    });
......

请思考一个问题:我们什么时候使用异步?什么时候使用多线程?

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库。

成功引入
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中的方法进行封装,使用我们自己封装的方法。下面进行网络库的封装......

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库进行封装
<!-- 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;
  }
}
<!-- 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

快捷键抽取小部件
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,
      ),
  ),
),
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))
              ],
            ),
          ],
        ),
      ),
    );
  }
}
return Scaffold(
      appBar: AppBar(
        // appBar下面添加黑色线条
        elevation: 0.0,
......
运行效果
上一篇下一篇

猜你喜欢

热点阅读