[ { "content": "以下情景发生的 Flutter 版本为 3.16.9", "type": "text" }, { "level": 1, "title": "问题背景", "type": "title" }, { "content": "当时应用页面需要实时获取当前连接的WiFi信息。而根据新的API获取当前连接的WiFi信息,已经不只是调用API这么简单了,还需要获取定位权限、确保定位服务开启、当前已连接上WiFi这些条件。", "type": "text" }, { "content": "为了让 Controller 中调用方便,于是把一整套的逻辑都封装在了 Service 中。Service 调用方法实现如下,重点是红框的这块区域", "type": "text" }, { "description": "", "previewable": true, "type": "image", "url": "https://cs-blog-1300605857.cos.ap-guangzhou.myqcloud.com/article/250225-flutter-future-task-chain/%E9%97%AE%E9%A2%98%E4%BB%A3%E7%A0%81.png" }, { "content": "按照当时的认知来说,理论上绿框的这部分是先执行完 BSSIDPickerService.getWiFiStatus() 获取到数据后,再等待3秒再启动任务。而刚才说的这个方法是原生方法,在iOS平台会先检查权限,在没有权限的情况下会申请权限,得到权限结果后再返回当前连接上的 WiFi 信息。", "type": "text" }, { "content": "然而实际运行时,并没有等待原生返回再启动信息,而是每隔3秒就会触发权限申请弹窗(自己应用的弹窗,并非系统申请权限弹窗)。随着时间的流逝,弹窗会一个一个的叠加在应用顶层,导致应用卡死。", "type": "text" }, { "level": 1, "title": "第一次尝试", "type": "title" }, { "content": "首先最直接想到的就是这个 Future 并没有生效,于是向手动尝试使用 await 进行等待,等待接口返回数据后再进行返回到下一个 Future 中。于是对代码进行了以下的调整", "type": "text" }, { "description": "", "previewable": true, "type": "image", "url": "https://cs-blog-1300605857.cos.ap-guangzhou.myqcloud.com/article/250225-flutter-future-task-chain/await%E5%B0%9D%E8%AF%95.png" }, { "content": "经过这次调整后,代码逻辑可以正常运行了。这几本可以确定是最后 BSSIDPickerService.getWiFiStatus() 并没有等待返回结果就直接继续往下执行。但是这只能确定了问题所在,还是没能解决我的疑问,于是便有了接下来的尝试", "type": "text" }, { "level": 1, "title": "第二次尝试", "type": "title" }, { "content": "这次尝试,我把 await 去掉,选择在 BSSIDPickerService.getWiFiStatus() 的最后面再次加上了一个看上去没有实际意义的 Future 任务。令我感到惊讶的是,这次代码也能正常执行。它等待了获取到 WiFi 信息后才等待3秒开启下一个执行循环", "type": "text" }, { "description": "", "previewable": true, "type": "image", "url": "https://cs-blog-1300605857.cos.ap-guangzhou.myqcloud.com/article/250225-flutter-future-task-chain/then%E5%B0%9D%E8%AF%95.png" }, { "content": "到了这里已经隐隐约约感觉到大概的方向了,应该是和 Flutter 的异步任务调度有关系,但是还是比较模糊,于是再次做了第三次尝试", "type": "text" }, { "level": 1, "title": "第三次尝试", "type": "title" }, { "content": "第三次尝试是把后面的 “没意义” 的 Future 移除,改为在原本的 Future 加上了返回值(返回什么值都可以,但是不要没有返回值)。", "type": "text" }, { "description": "", "previewable": true, "type": "image", "url": "https://cs-blog-1300605857.cos.ap-guangzhou.myqcloud.com/article/250225-flutter-future-task-chain/return%E5%B0%9D%E8%AF%95.png" }, { "content": "将代码重新运行起来,发现功能也能正常执行,这样大概也能确定问题所在了", "type": "text" }, { "level": 1, "title": "总结", "type": "title" }, { "content": "结合这3次修改的尝试对比原本有问题的代码。区别在于这3次尝试都为这个 Future 增加了返回值(或者说是后续操作需要读取上次操作的返回)。", "type": "text" }, { "content": "简单理解来说,Flutter 在对异步任务的处理可以抽象理解为一个异步任务链条。在 Future 任务中一块一块的相连接时,如果没有直接连接的下一个Future任务或者没有指明返回,Flutter将会把它当成一个不影响流程的任务调度(不会成为完整链条的一部分,而是链条上的不关键的分支),只需要在对应的阶段执行就可以而不会等待它的返回值。", "type": "text" }, { "content": "如果需要让它成为一个影响主流程的关键节点任务,可以尝试以上3种方式进行调整代码。只要确保有后续操作有返回值或者后续有操作需要读取你的返回值即可", "type": "text" }, { "content": "原本错误的代码执行的链条是这样", "type": "text" }, { "description": "", "previewable": true, "type": "image", "url": "https://cs-blog-1300605857.cos.ap-guangzhou.myqcloud.com/article/250225-flutter-future-task-chain/%E9%94%99%E8%AF%AF%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E9%93%BE%E6%9D%A1.png" }, { "content": "而修改后的代码的异步任务执行链条是这样的", "type": "text" }, { "description": "", "previewable": true, "type": "image", "url": "https://cs-blog-1300605857.cos.ap-guangzhou.myqcloud.com/article/250225-flutter-future-task-chain/%E6%AD%A3%E5%B8%B8%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E9%93%BE%E6%9D%A1.png" } ]