面试题答案
一键面试内存管理
- SharedPreferences
- 按需加载:SharedPreferences默认是懒加载的,但在复杂场景下,尽量避免一次性读取过多数据。例如,对于一些不常用的配置数据,可以在真正需要使用时再读取,而不是在应用启动时全部加载到内存中。这可以减少应用启动时的内存开销,提升响应速度。
- 数据清理:定期清理不再使用的SharedPreferences数据。如果某些配置在应用的特定阶段不再需要,通过
SharedPreferences.remove()
方法删除相应的键值对,释放内存空间。比如,用户临时的登录状态在用户注销后就可清理。
- SQLite
- 游标及时关闭:在使用SQLite查询数据时,会返回游标(
Cursor
)对象。游标会占用一定的内存资源,在使用完游标后,务必及时调用close()
方法关闭游标。例如:
var queryResult = await database.query('your_table'); queryResult.forEach((row) { // 处理数据 }); queryResult.close();
- 批量操作:减少数据库连接的创建次数。对于多次插入或更新操作,可以使用事务进行批量处理。例如,要插入多条数据:
这样可以减少每次操作时数据库连接创建和销毁的开销,同时也减少了内存占用。await database.transaction((txn) async { for (var data in listOfData) { await txn.insert('your_table', data); } });
- 游标及时关闭:在使用SQLite查询数据时,会返回游标(
数据缓存机制
- SharedPreferences
- 应用内缓存:在Flutter应用内部建立一个缓存层,将频繁读取的SharedPreferences数据缓存起来。可以使用
Map
数据结构在内存中暂存数据。例如:
这样,对于相同数据的多次读取,优先从内存缓存中获取,提高响应速度。Map<String, dynamic> sharedPrefsCache = {}; Future<dynamic> getFromSharedPrefsCache(String key) async { if (sharedPrefsCache.containsKey(key)) { return sharedPrefsCache[key]; } var value = await SharedPreferences.getInstance().then((prefs) => prefs.get(key)); sharedPrefsCache[key] = value; return value; }
- 缓存过期策略:对于缓存的数据设置过期时间。可以通过在缓存数据时记录时间戳,在每次读取缓存数据时检查是否过期。例如:
Map<String, Map<String, dynamic>> sharedPrefsCacheWithExpiry = {}; Future<dynamic> getFromSharedPrefsCacheWithExpiry(String key, int expiryInSeconds) async { if (sharedPrefsCacheWithExpiry.containsKey(key)) { var cacheEntry = sharedPrefsCacheWithExpiry[key]; var currentTime = DateTime.now().millisecondsSinceEpoch; if (currentTime - cacheEntry['timestamp'] < expiryInSeconds * 1000) { return cacheEntry['value']; } } var value = await SharedPreferences.getInstance().then((prefs) => prefs.get(key)); sharedPrefsCacheWithExpiry[key] = {'value': value, 'timestamp': DateTime.now().millisecondsSinceEpoch}; return value; }
- 应用内缓存:在Flutter应用内部建立一个缓存层,将频繁读取的SharedPreferences数据缓存起来。可以使用
- SQLite
- 查询结果缓存:对于一些不经常变化且查询代价较高的SQLite查询结果,可以在应用内缓存。同样可以使用
Map
数据结构,以查询语句作为键,查询结果作为值。例如:
Map<String, List<Map<String, dynamic>>> sqliteQueryCache = {}; Future<List<Map<String, dynamic>>> getFromSqliteQueryCache(String query) async { if (sqliteQueryCache.containsKey(query)) { return sqliteQueryCache[query]; } var result = await database.rawQuery(query); sqliteQueryCache[query] = result; return result; }
- 基于表变化的缓存更新:当SQLite表中的数据发生变化(插入、更新、删除)时,需要相应地更新缓存。可以在执行数据库写操作后,清除相关的查询缓存。例如,在插入数据后:
await database.insert('your_table', data); sqliteQueryCache.removeWhere((key, value) => key.contains('your_table'));
- 查询结果缓存:对于一些不经常变化且查询代价较高的SQLite查询结果,可以在应用内缓存。同样可以使用
数据库索引优化
- 分析查询模式:通过分析应用中SQLite的查询语句,确定哪些字段经常用于
WHERE
子句、JOIN
子句等。例如,如果经常根据user_id
查询用户相关数据,就需要为user_id
字段创建索引。 - 创建合适索引:
- 单列索引:对于单个字段的查询,创建单列索引。例如,为
users
表的user_id
字段创建索引:
await database.execute('CREATE INDEX idx_user_id ON users (user_id)');
- 复合索引:当查询涉及多个字段时,创建复合索引。比如,查询同时涉及
user_id
和order_date
字段,创建复合索引:
注意复合索引的字段顺序,最常使用的字段应放在前面,以提高索引的使用效率。await database.execute('CREATE INDEX idx_user_id_date ON orders (user_id, order_date)');
- 单列索引:对于单个字段的查询,创建单列索引。例如,为
- 定期维护索引:随着数据量的增长,索引可能会变得碎片化,影响查询性能。可以定期重建索引,例如在应用的维护时段或数据量增长到一定阈值时:
这样可以优化索引结构,提升查询速度。await database.execute('DROP INDEX idx_user_id'); await database.execute('CREATE INDEX idx_user_id ON users (user_id)');
通过上述内存管理、数据缓存机制和数据库索引优化策略,可以有效提升在大量并发数据读写操作、数据量不断增长且对响应速度要求极高的Flutter复杂应用场景中SharedPreferences与SQLite综合使用的整体性能。