Ускоряем отладку и прототипирование мобильных QML-приложений на живом устройстве
Привет.
Хочу поделиться простым способом оптимизации своего рабочего времени при разработке QML приложений под Android/iOS/Embedded.
Возможно, сказанное мной кому-то покажется бояном, но пока про такой элементарный метод нигде не читал.
Суть проблемы — при разработке, отладке или прототипировании любого мобильного приложения на любом языке мы как правило всегда проходим одни и те же этапы: правка кода, деплой, запуск. И так до бесконечности. В случае с мобильной разработкой, этап деплоя кода может растянуться на внушительное время — от 2 до 10 минут, в течение которого заниматься в общем-то нечем. Возможно для кого-то это и хорошо, но точно не для тех, кто ценит своё время. В общем, думаю, не только меня выбешивает такое положение вещей ;-)
Ситуация усугубляется для нативных средств разработки, к примеру, под Android, где нам всегда, без вариантов нужно перекомпилировать Java-код перед следующим запуском на устройстве.
Qt на первый взгляд обладает той же проблемой — каждый новый билд проекта также деплоится на устройство продолжительное время. Вот только есть одна особенность — мы ведь можем писать приложения не на Qt/C++, а на чистом QML. В этом случае, если мы не меняем логику в C++ части приложения, нам ничего не нужно компилировать под целевую платформу. А значит — было бы круто просто обновлять набор qml файлов приложения и перезапускать приложение на устройстве. Ведь экономия времени на 10 запусков составила бы не менее часа!
Ну что ж, раз есть потенциальная возможность — грех ею не воспользоваться. Читайте под катом, что у меня получилось.
Начав рыть информацию на тему, я во первых наткнулся на когда-то подававший надежды проект QML Live Preview (статья на хабре), который позволял разработчикам запускать режим отладки в Qt Creator, на лету изменять код и видеть свои правки на устройстве. Это было волшебство и то что мне нужно, вот только данный модуль Qt Creator-а исключили из общей кодовой базы (ссылка на коммит). Может быть Digia что-то мутит и перенесла его в коммерческую версию, а может он был нестабилен. Не знаю. Главное — что его больше нет.
В общем пришлось искать обходной путь. Он нашёлся в на странице документации Qt, где рассказывается о такой теме, как сетевая прозрачность при загрузке ресурсов. Идея в том, что Qt и QML приложение пофигу, откуда загружать QML файлы и ресурсы типа шрифтов, изображений и прочего. Источником может быть локальный диск, ресурсы Qt (qrc), smb-шара или http-сервер.
Идея реализации возникла мгновенно! Разворачиваем на локальной машине HTTP-сервер, корневой директорий которого будет наш проект, меняем сценарий загрузки ресурсов в C++ части приложения, проверяем работоспособность на десктопе, собирает под мобилку, проверяем работоспособность на мобилке, радуемся!
Полное описание алгоритма:
1) Убеждаемся что наш ПК и устройство находятся в одной локальной сети.
2) Приводим проект к требованиям Qt Network Transparent и определённому шаблону:
— все ресурсы в каталоге /assets;
— все qml файлы в каталоге /qml;
— внутри каталога qml структура файлов каталогов должна быть максимум двухуровневой;
— все включения типа import «dir» приводим к виду import «dir» ad Dir;
— все обращения к компонентам в поддиректориях приводим от вида MyComp {… } к виду Dir.MyComp {… };
3) Скачиваем, настраиваем и запускаем nginx. В настройках по умолчанию нужно поменять всего два параметра — порт и root каталог. Затем запускаем nginx. Пример:
server {
listen 8085;
...
location / {
root D:/Dropbox/work/anyway;
4) В файле main.cpp в месте загрузке qml-файлов меняем логику на следующую:
bool qmlDebug = true;
QString url = "http://192.168.1.22:8085/";
QString qmlLoadPrefix;
QString assetsLoadPrefix;
if (qmlDebug) {
qmlLoadPrefix = url+"qml/";
assetsLoadPrefix = url+"assets/";
} else {
qmlLoadPrefix = "qrc:/";
assetsLoadPrefix = "qrc:/";
}
engine.rootContext()->setContextProperty("qmlLoadPrefix", qmlLoadPrefix);
engine.rootContext()->setContextProperty("assetsLoadPrefix", assetsLoadPrefix);
engine.load(QUrl(qmlLoadPrefix+"main.qml"));
Здесь мы задаём URL нашего ПК в локальной сети, а также две переменные qmlLoadPrefix и assetsLoadPrefix. Как не сложно догадаться это просто префиксы для доступа к ресурсам и qml-файлам приложения в зависимости от текущей конфигурации приложения. Так во время активной фазы разработки мы сможем быстро загружать требуемые ресурсы через http, а при выпуске приложения — загружать всё из файлов ресурсов самого приложения.
5) Ну и последний шаг — меняем все вхождения типа «qrc://myImg.png» на assetsLoadPrefix+«myImg.png» во всех qml файлах проекта.
Вот собственно и всё. Осталось отметить, что теперь запускать приложение для отладки нужно не с помощью большой зелёной кнопки «Play» на левой панели инструментов Qt Creator, а используя маленькую кнопку «Play» на нижней панели инструментов в разделе «Вывод приложения». Правда, эта кнопка будет недоступна сразу после запуска Qt Creator и нужно будет запустить приложение впервые по старинке, но затем достаточно пользоваться только её. При этом в лог Qt Creator всё также будут сыпаться все необходимые debug сообщения, а если Вам понадобится полноценная отладка, то и она будет работать в штатном режиме.
Конечно, это не ахти какой хак и он всё равно не спасёт от пересборки приложения при написании C++ кода, но даже в таком виде мы получаем гораздо больше удобства при разработке приложений под любую из платформ.
Предлагаю в комментариях поделиться своими мыслями и предложениями по поводу ещё большей оптимизации процесса разработки Qt-приложений и их преимущества перед нативными приложениями для iOS и Android.