面试题答案
一键面试架构设计
- 分层架构:
- 表现层(Presentation Layer):负责UI展示,将异步数据变化通过
StatefulWidget
的setState
方法更新UI。尽量减少在UI层处理复杂逻辑,避免阻塞UI线程。例如,在build
方法中只进行简单的UI渲染,而不进行数据处理。 - 业务逻辑层(Business Logic Layer):将异步任务的逻辑封装在服务类中。使用
Provider
、GetIt
等状态管理库来管理业务状态。比如,创建一个UserDataService
类,负责处理与用户数据相关的异步任务,如获取用户信息、更新用户设置等。 - 数据访问层(Data Access Layer):处理与数据源(如网络、本地存储)的交互。使用
http
库进行网络请求,shared_preferences
进行本地存储操作。对不同类型的数据源进行封装,便于复用和维护。例如,创建NetworkApi
类来处理所有网络请求,LocalStorage
类来管理本地数据存储。
- 表现层(Presentation Layer):负责UI展示,将异步数据变化通过
- 隔离长时间运行任务:对于长时间运行的异步任务,如文件下载或大数据处理,可以使用
Isolate
将其与主线程隔离。这样主线程不会被阻塞,保证UI的流畅性。例如,在进行大文件下载时,启动一个新的Isolate
来处理下载逻辑,主线程可以继续处理其他UI相关事务。
数据结构选择
- 不可变数据结构:在Flutter中,使用不可变数据结构可以减少不必要的内存开销和状态冲突。例如,使用
ImmutableList
、ImmutableMap
等。当数据发生变化时,创建新的不可变对象,而不是直接修改原对象。这有助于简化状态管理和内存回收,因为旧对象可以在不再被引用时及时被垃圾回收。 - 缓存数据结构:根据业务需求,合理使用缓存。对于频繁访问且不经常变化的数据,可以使用
Map
来缓存。例如,在一个新闻应用中,缓存已加载的文章列表,避免重复从网络获取。同时,要注意设置合适的缓存过期策略,防止缓存数据占用过多内存。 - 队列数据结构:对于需要按顺序处理的异步任务,可以使用
Queue
。比如,在处理多个网络请求时,如果某些请求有顺序要求,可以将请求添加到Queue
中,依次处理。这有助于避免任务冲突和数据不一致问题。
异步操作调度
- Future和Stream的合理使用:
- Future:对于单个异步操作且只需要一次结果的场景,使用
Future
。例如,获取用户的基本信息,使用Future
来包装网络请求。在处理多个相互依赖的Future
时,可以使用Future.then
链式调用或者Future.wait
方法来按顺序或并行执行多个Future
。 - Stream:对于有频繁数据更新的场景,使用
Stream
。例如,监听实时数据变化(如传感器数据、聊天消息)。使用StreamController
来控制数据流的发送和接收。可以通过StreamSubscription
来订阅和取消订阅数据流,避免内存泄漏。
- Future:对于单个异步操作且只需要一次结果的场景,使用
- 节流和防抖:对于频繁触发的异步操作(如用户频繁点击按钮触发网络请求),使用节流和防抖策略。
- 节流:在一定时间内只允许执行一次操作。例如,使用
Timer
来实现节流,在用户频繁点击按钮时,设置每1秒只允许触发一次网络请求,避免过多无效请求和资源浪费。 - 防抖:在用户操作结束后一定时间内才执行操作。比如,在搜索框输入时,用户输入结束后1秒再触发搜索请求,而不是每次输入都触发,减少不必要的异步任务。
- 节流:在一定时间内只允许执行一次操作。例如,使用
- 任务优先级调度:根据业务需求,为异步任务设置优先级。对于重要且紧急的任务(如用户登录验证),优先执行。可以使用优先级队列(如
PriorityQueue
)来管理任务。在调度任务时,从优先级队列中取出优先级最高的任务执行,确保关键任务的及时处理。