Dio vs HTTP: опыт перехода от одного к другому

Меня зовут Влад, я iOS/Flutter-разработчик в DexSys на проекте DexBee*. В этой статье:
расскажу о своём опыте перехода с одного HTTP клиента(HTTP) на другой(Dio)
объясню, для чего это было сделано, и какие преимущества имеет Dio над HTTP
кратко расскажу про кодогенератор Freezed и потоки Flutter’а. Конечно, с примерами кода. А в конце статьи вы найдете демо-проект под всё это дело)

*DexBee - это клубная система вовлечения клиентов в занятия фитнесом. В основе системы лежит контроль нагрузки клиента во время тренировки.


«DexBee Клуб» - приложение для управления настройками клуба в системе. Первый релиз приложения был в конце 2021 года, и работа над ним до сих пор продолжается. Приложение написано на Flutter.

Почему понадобился переезд?

Причина простая – легаси. Нам надо было сделать MVP продукта, проверить насколько он будет полезен клиентам, и от этого уже отталкиваться, принимая решение - есть ли необходимость в нашем продукте.

MVP – проект с минимальным функционалом, который покрывает некоторые потребности потребителя.

Поэтому решили попробовать кроссплатформу. На тот момент я писал только под iOS, и знания по Flutter у меня были нулевыми. Для флаттера на тот момент был выпущен уже второй мажорный релиз. Язык развивался, да и сейчас развивается, как на дрожжах, количество вакансий для него росло, сообщество разработчиков под него в России тоже немалое, что наталкивает на мысли о хорошем будущем фреймворка.

Так вот, мне дали 2-3 недельки на изучение и ещё немного времени на простенький демо-проект, чтобы проверить, как это вообще работает, и получится ли что-то дельное. Далее мы запустили первую версию Dexbee Клуб, в которой можно было авторизоваться и протестировать интернет на доступ к нашим сервисам. Этот функционал, в первую очередь, помогал нашей бравой техподдержке подсказывать клиентам как правильно разместить оборудование в клубе.

Здесь же хотел бы выразить огромную благодарность этим крутым ребятам, которые держат рубеж наплыва отзывов от клиентов, как хороших, так и плохих, не давая тем самым разрушиться моему хрупкому мирочку)

Нужно было создать два запроса на сервер – авторизацию и обновление логин-токена. Перечитав кучу статей и форумов с вопросом «А что выбрать-то?», я чаще всего получал ответы как раз про HTTP и Dio. Единогласного ответа, конечно же, не получить, но большинство рекомендовало именно эти пакеты. Я решил взять HTTP, он показался простеньким для изучения и справлялся с основными функциями, что и сейчас прекрасно делает.

Дальше мы собрали первые отзывы по приложению и решили развивать. Клиент серверных запросов жирел и матерел с каждым новым релизом. Возможно, я чуть-чуть преувеличиваю, но дополнять его с каждым новым запросом становилось всё труднее и труднее(из-за лени).

Тема для холивара: проектировать сразу хорошо или всё-таки ради скорости можно поступиться качеством?

Для проверки теории, а может из-за опыта написания на Flutter, я решил поступиться качеством и спроектировать, так сказать, не самое «резиновое» решение, но быстро внедряемое.

Проект нуждался в доработках клиент-серверной части, а в нашей команде есть возможность облегчить себе жизнь, если это несет плюсы для всего проекта: разработчик описывает, что хочет улучшить, какие выгоды от этого получит проект, сколько времени потребуется, и что тестировщику придётся проверять в итоге. Задача уходит руководителю, а он, в зависимости от степени важности исправлений, заносит её в один из будущих релизов.

Преимущества Dio над HTTP

На этот раз я взял в работу Dio, сейчас объясню почему: HTTP умеет в GET, POST, PUT, DELETE, PATCH, загружать файлы с помощью Multipart запроса.

Dio умеет всё тоже самое, но с некоторыми преимуществами:

  • Глобальные настройки - при создании Dio можно указать baseURL, таймауты соединения, получения ответа и так далее.

  • Interceptor - это перехватчик, в котором можно дополнить или вставить дополнительные действия перед отправкой запроса, скорректировать его ответ или ошибку. Применить можно для логирования, обновление токена авторизации и так далее.

  • Есть возможность отменить выполнение всех запросов разом. И отменить отдельно взятый.

Отмечу, для HTTP уже написаны пакеты, которые дополняют его функционал и есть возможность получить все плюшки, какие имеет Dio, но у Dio всё это есть «из коробки». Важно понимать, оба пакета работают с одинаковой скоростью и быстрее Dio отправлять/получать ответ от сервера не будет.

Кажется, будто бы HTTP – голый клиент, Dio – уже нарощенный дополнительными плюшками, но, как оказалось позже, есть ещё Chopper и Retrofit – кодогенераторы запросов. Для создания запроса надо описать сигнатуру метода: путь, параметры урла, боди, что должно вернуться в ответе, запустить генератор, и тело метода создастся. Ускорение ускорения или ещё один из этапов программистской лени?

Freezed

«yet another code generator» – как пишут создатели пакета. Я бы лучше назвал подобные пакеты «инструкцией для генерации», то есть генерирует код пакет build_runner, а во Freezed описано, как надо сгенерировать. Если вкратце, с помощью него можно описать структуру серверного ответа, запустить генерацию кода и, «вуаля», парсер готов. Описанный мной функционал лишь минимальная часть того, что умеет этот пакет, так что, если вы ещё не ознакомились с ним, то я рекомендую перейти и почитать подробнее здесь. Модель дополнится методами copyWith и toString. Всё это необходимо и, порой, занимает кучу монотонного времени.

Потоки Flutter

При запуске Flutter-приложения вызывается метод main, в котором создается главный изолят, т.е. главный поток или поток для отрисовки интерфейса. После main метода запускается цикл событий, Event Loop.
Event Loop, как и следует из названия, это цикл, конец которого зависит от жизни потока, в котором он работает, в данном случае цикл завершится, как только приложение будет выгружено из памяти.

В нём происходит выполнение очередей по принципу FIFO(First in - First out). Как следует из названия, хоть тут и имеется возможность создать асинхронные Future методы – они не выполняются синхронно. Очереди делятся на два типа:

  1. Microtask – служит для выполнения задач, не занимающих много времени. Создать можно при помощи Future.microtask.

  2. Event – все остальные операции. От взаимодействия с UI до работы с БД, API и т.д.

Очереди выполняются именно в такой последовательности. Сначала массив microtask, а далее events. После выполнения любой инструкции, будет запущена очередь microtasks. По работе Event Loop есть отличная видеопрезентация, смотрите тут.

Для выполнения реального синхронного кода есть две возможности: метод compute и, более сложное для реализации, использование класса Isolate. Оба варианта создают новый поток или, по-другому, – изолят. Назван он так не случайно. Каждый поток во Flutter изолирован от других и имеет свою собственную область памяти. Нет возможности допустить deadlock, и это круто. Но, чтобы воспользоваться объектами, созданными одним изолятом, надо передать их копию в другой изолят. Общение происходит через сообщения, поэтому оба изолята должны знать порты друг друга. Изоляты следует уничтожать, чтобы они не занимали память впустую .

Передать новому изоляту можно только функцию верхнего уровня, глобальную функцию, или статический метод класса.

Метод compute настраивает общение и уничтожение изолята внутри себя.

Вот отличная статья, иллюстрирующая, почему стоит пользоваться изолятами: https://dev.to/alphamikle/why-should-you-use-isolates-in-flu...

И ещё список для статей для более глубокого понимания работы изолятов:
https://habr.com/ru/articles/497278/#
https://blog.codemagic.io/understanding-flutter-isolates/
https://martin-robert-fink.medium.com/dart-is-indeed-multi-t...
https://dart.dev/language/concurrency
https://skondratev.com/futures-isolates-event-loop-vo-flutter/#:~:text=Модель исполнения в Dart,Дарта — Изолят ( Isolate ).

Пример

В этой секции я напишу простенькое клиент-серверное приложение с использованием Dio, покажу, как добавить в проект кодогенератор freezed и как им воспользоваться, и выведу отправку запроса и декодирование JSON в отдельный изолят. В свободном доступе есть куча бесплатных API, на которых можно попрактиковаться, найти их можно здесь. Я выбрал Spaceflight News API, а метод v3/articles с загрузкой 100000 элементов за один запрос.

Итак, чтобы добавить Dio в проект, надо написать название пакета в pubspec.yaml и выполнить команду pub get.

Dio vs HTTP: опыт перехода от одного к другому IT, Программирование, Программист, Flutter, Http, Dio, Длиннопост

Дабы не писать под каждый запрос отдельный метод, я описал класс NetworkRequest, в котором будут храниться необходимые параметры запроса.

Dio vs HTTP: опыт перехода от одного к другому IT, Программирование, Программист, Flutter, Http, Dio, Длиннопост

Чтобы ответ не сломался из-за нескольких неожиданных элементов, имеющих не тот тип, который необходим нам, я сразу же описал класс ответа на запрос списка «космических» новостей. Для этого обернул конструктор ArticleResponse в конструкцию try catch.

Dio vs HTTP: опыт перехода от одного к другому IT, Программирование, Программист, Flutter, Http, Dio, Длиннопост

Теперь и сам NetworkManager. При инициализации менеджера, создал клиент с настройками по умолчанию.

Dio vs HTTP: опыт перехода от одного к другому IT, Программирование, Программист, Flutter, Http, Dio, Длиннопост

Метод performRequest принимает параметры запроса и функцию, по которой будет парсить ответ, если запрос успешен. Model - это тот объект, что мы ожидаем на выходе.

Dio vs HTTP: опыт перехода от одного к другому IT, Программирование, Программист, Flutter, Http, Dio, Длиннопост

Вот и пример его применения.

Dio vs HTTP: опыт перехода от одного к другому IT, Программирование, Программист, Flutter, Http, Dio, Длиннопост

В текущем виде запрос выполняется в главном изоляте, что, при развитии проекта, может мешать отрисовке интерфейса. Да и с добавлением новых запросов придётся описывать всё больше ответов с сервера. Не вручную же это делать. Так что, первым делом добавлю пакет freezed в проект. Делается это при помощи всё того же pubspec.yaml.

Dio vs HTTP: опыт перехода от одного к другому IT, Программирование, Программист, Flutter, Http, Dio, Длиннопост

dependicies – пакеты с фреймворками и библиотеками, которые используются в проекте, например, элементы интерфейса или утилиты для использования возможностей телефона, локатора, блютуз и т.д.

dev-dependicies – пакеты, помогающие в разработке проекта, например: тесты, кодогенераторы и т.д.

Выполнить всё тот же pub get, и поехали пользоваться.

Dio vs HTTP: опыт перехода от одного к другому IT, Программирование, Программист, Flutter, Http, Dio, Длиннопост

Все подчеркивания это нормально. Теперь необходимо запустить команду генерации. Делается это следующим образом: надо открыть Терминал, перейти в папку с проектом и выполнить команду flutter pub run build_runner build. После успешного выполнения команды подчеркивания должны пропасть. Вот таким нехитрым образом и дополнилась модель респонса.

Далее, я выведу выполнение каждого запроса в отдельный изолят, воспользовавшись функцией compute. Созданный изолят не может получить доступ к уже настроенному Dio клиенту, поэтому я создам класс, в котором будет храниться клиент, запрос и функции для парсера.

Dio vs HTTP: опыт перехода от одного к другому IT, Программирование, Программист, Flutter, Http, Dio, Длиннопост

И опишу глобальный метод, который нужно будет выполнить.

Dio vs HTTP: опыт перехода от одного к другому IT, Программирование, Программист, Flutter, Http, Dio, Длиннопост

Таким образом, в performRequest я создам IsolatedNetworkRequest и выполню метод compute.

Dio vs HTTP: опыт перехода от одного к другому IT, Программирование, Программист, Flutter, Http, Dio, Длиннопост

Теперь каждый запрос будет выполняться в отдельном изоляте и не мешать работе главного.

Итог

У нас есть клиент с глобальными настройками, для которого можно написать перехватчики, если появится необходимость корректировать отправку и результаты запросов. На каждый новый ответ нет необходимости тратить время на описание класса. А запросы не мешают работе изолята, отвечающего за отрисовку интерфейса. Ознакомиться с демо проектом можно по этой ссылке: https://github.com/Wenomok/dio_example

Буду рад любым отзывам! Есть желание и дальше писать статьи и делать их качественными и максимально интересными.

Автор: Влад, iOS/Flutter-разработчик в DexSys

Лига программистов

1.5K постов11.4K подписчика

Добавить пост

Правила сообщества

- Будьте взаимовежливы, аргументируйте критику

- Приветствуются любые посты по тематике программирования

- Если ваш пост содержит ссылки на внешние ресурсы - он должен быть самодостаточным. Вариации на тему "далее читайте в моей телеге" будут удаляться из сообщества