#dart #flutter
#dart #flutter
Вопрос:
Я хочу спросить, как я должен обрабатывать большой список во Flutter. Мое приложение работает очень медленно, когда я нахожусь в элементе данных, который действительно находится глубоко в списке, который я ищу. Мой список состоит из более чем 70 000 объектов большой структуры данных.
Ниже показано, как я выполняю «поиск» по списку.
Future<Iterable<SomeDataStruct>> _getAllData() async {
return allData.where((a) => (a.dataTitle.toLowerCase().contains(querySearch.toLowerCase().trim())));
}
Построение списка с использованием ListView.builder внутри FutureBuilder.
Когда я выполняю поиск и заполняется результат или результаты из глубины списка, приложение работает чрезвычайно медленно, вплоть до того момента, когда я нажимаю на элемент списка, и требуется несколько секунд, прежде чем оно выполнит onTap. И если мне нужно изменить поисковый запрос, требуется время, чтобы программная клавиатура вернулась после того, как я нажму на текстовое поле.
Где я допускаю ошибку или неправильно обрабатываю это, что мне следует сделать, чтобы мой огромный список был доступен для поиска, не делая приложение невыносимым.
РЕДАКТИРОВАТЬ: Как я сделал так, чтобы это не замедляло работу приложения после изменения кода. Это правильно?
String tempQuery;
List<SomeDataStruct> searchResults = [];
Future<List<SomeDataStruct>> _getAllData() async {
if(querySearch!=tempQuery) {
tempQuery = querySearch;
searchResults = allData.where((a) => (a.dataTitle.toLowerCase().contains(querySearch.toLowerCase().trim()))).toList();
}
return searchResults;
}
Комментарии:
1. Кажется, я исправил это, изменив повторяющийся<SomeDataStruct> на список, а затем добавив . Вывод списка за возвратом.
Ответ №1:
Содержит дорого
Запросы на содержание- стоят дорого, потому что каждая запись должна проверяться на каждой позиции (вплоть до value.length
— searchTerm.length
), если может быть найден поисковый запрос.
Ограничение поддержки поиска началом строки уже значительно повысило бы производительность. Кроме того, вы могли бы создавать вспомогательные структуры данных, в которых весь список значений разбивается на части с одинаковым символом в начале. Если фрагменты все еще слишком велики, можно добавить другой уровень для 2-го символа. Поиск будет быстрым, потому что количество символов ограничено.
Использование базы данных может потребовать некоторой работы по программированию (поддержание индексов). База данных, подобная SQLite, может использоваться с индексами, специализированными для вашего типа запросов.
Разделите работу на более мелкие части, чтобы фреймворк мог выполнять свою работу
Если вы не можете ограничить поиск «началом строки», вы все равно можете разбить структуру данных на более мелкие фрагменты и вызвать поиск для каждого фрагмента асинхронно. Таким образом, пользовательский интерфейс получает «немного воздуха для дыхания», чтобы повторно отобразить пользовательский интерфейс перед поиском следующего фрагмента. Результат поиска будет обновляться постепенно.
Перенести работу из потока пользовательского интерфейса
Другим способом было бы запустить другой изолированный файл и выполнить поиск там. Другой изолят может выполняться на другом процессоре (ядре) и, следовательно, не будет блокировать поток пользовательского интерфейса при поиске. Таким образом, не было бы необходимости разбивать на куски. Все же может быть выгодно постепенно обновлять пользовательский интерфейс вместо того, чтобы заставлять пользователя ждать, пока весь результат поиска станет доступен.
Смотрите также
- https://api.dartlang.org/stable/2.2.0/dart-isolate/dart-isolate-library.html
- https://pub.dartlang.org/packages/isolate
- https://codingwithjoe.com/dart-fundamentals-isolates/
Кэширование
Сохранение результатов поиска в памяти также может помочь повысить производительность. Например, если пользователь вводит, foo
а затем нажимает пробел, вы можете повторно использовать результат поиска для fo
того, который вы ранее уже рассчитали, но это помогает только в некоторых случаях.
Измерение
Еще один важный момент, конечно, заключается в проведении сравнительного анализа. Что бы вы ни пытались улучшить производительность, создавайте тесты, чтобы узнать, какие меры дают какой эффект и стоит ли оно того. Вы узнаете много нового о вашем сценарии, ваших данных, Dart, …, и это позволит вам принимать правильные решения.
Комментарии:
1. После попытки переместить работу потока пользовательского интерфейса и не получить результатов, которые мне были нужны, другие методы не соответствовали моим потребностям. Кажется, я исправил это, изменив повторяющийся<SomeDataStruct> на List<SomeDataStruct> и затем добавив . Вывод списка за возвратом.
2. Интересно. Не могу представить, почему это помогает.
3. Да, теперь это работает. Приложение не замедляется, как это было бы раньше. Я внес правку в свой пост, показывая новый код.
4. @punjabi4life Сомневаюсь, что это поможет. Похоже, это сработает, если то, что вы ищете, находится не далеко от списка или, возможно, ваш список недостаточно велик, но приложение определенно снова вылетит, если у вас очень большой список, например, около 150 тыс. элементов.
Ответ №2:
Поиск по списку приведет к сбою вашего приложения, если список достаточно большой. Я тестировал поиск по списку из 150 000 элементов, и мое приложение разбилось. Кажется, это работает, если то, что вы ищете, находится в начале списка, но если вы ищете что-то, что может находиться глубоко в списке, например, индекс 100 000, тогда работа значительно замедляется, и приложение зависает.
Чтобы решить эту проблему, я перешел к решению для базы данных, используя moor
библиотеку, которая построена поверх sqlite. Вы просто следуете документам, чтобы настроить шаблонный материал, это не займет много времени.
Вы можете создать простую таблицу базы данных, в которой всего 2 поля, id
и еще одно для хранения значений вашего списка. Для этого примера я вызываю другое поле itemName
В вашем классе database у вас может быть функция для выполнения фильтрации, например
Future<List<Item>> getFilteredItems(search) => (select(Items)..where((t) => t.itemName.like(search))).get();
Тогда ваш код будет выглядеть примерно так:
String tempQuery;
List<SomeDataStruct> searchResults = [];
Future<List<SomeDataStruct>> _getAllData() async {
if(querySearch!=tempQuery) {
tempQuery = querySearch;
query = await MyDatabaseClass().getFilteredItems("%$querySearch%");
for (int i = 0; i<query.length; i ) {
searchResults.add(query[i].itemName); //itemName
}
}
return searchResults;
}
После этого больше не будет сбоев или зависаний.
Ответ №3:
Может быть, таким образом :
ListView.builder(
itemBuilder: (BuildContext context, int index) {
return Text(data[index]);
},
)
Комментарии:
1. нет, это просто помогает построить список. Что я уже делаю.