Boost сериализация и QString

Время от времени кому то в голову приходит мысль использовать boost сериализацию в проектах с Qt. Тут встает вопрос как быть со строками, т.к. хочется просматривать  и редактировать.

Просто приведу пример кода без объяснений, думаю что все и так знают как можно работать с юникодом

namespace boost
{
    namespace serialization
    {

        template<class Archive>
        inline void save(Archive& ar, const QString& s,
                const unsigned int /*version*/)
        {
            auto ba = s.toUtf8();
            std::string str(ba.data(), ba.size());

            ar << make_nvp("QString", str);
        }

        template<class Archive>
        inline void load(Archive& ar, QString& s,
                const unsigned int /*version*/)
        {
            std::string str;
            ar >> make_nvp("QString", str);
            QByteArray ba(&str[0], str.size());
            s.append(ba);
        }

        template<class Archive>
        inline void serialize(Archive& ar, QString& s,
                const unsigned int file_version)
        {
            boost::serialization::split_free(ar, s, file_version);
        }
    }
}

Qt+GTest функциональное тестирование сетевых компонентов

Для тех кто не знает, что такое GTest отправляю на wiki: Google C++ Testing Framework(рус.). И путь вас не смущает, что это библиотека для модульного тестирования.

Использование фреймворка семейства xUnit, для функционального тестирования пришло в голову, когда реализовывал REST интерфейс к конфигурации Service Quality Management, в разработке которой я принимаю участие(на момент написания заметки). Это приложение на PHP с использованием SLIM, json-schema. PHPUnit использовался для автоматизированного тестирования функциональности. Косвенно тестировалась работа с базой, которая не тестируется при модульном тестировании. Все медленные операции(дисковые, взаимодействие с базой данной), а так же ненадежные операции(обрыв сети) заменяются специальной прослойкой и  mock-объектами.

Заканчиваю отступление и перехожу к теме. Сейчас развлекаюсь с MBaaS(mobile backend as a service) решением от QuickBlox. Что бы быть уверенным, что делаю все правильно, а так же для изучения поведения, стал писать тесты.

Еще одно отступление. В соответствие с книгой «Непрерывная интеграция: улучшение качества программного обеспечения и снижение риска» вместе с модульными тестами, можно размещать любые другие автоматические тесты, и если эти тесты довольно долго выполняются, то следует их выполнять по расписанию.

При использования Qt+GTest иногда требуется использовать свой код запуска тестов, т.к. внутри Qt используются то ли синглетоны, то ли статические переменные, которые должны быть инициализированы.

#include <QtCore/QCoreApplication>

#include "gtest/gtest.h"

int main(int argc, char** argv)
{
	QCoreApplication app(argc, argv);
	::testing::InitGoogleTest(&argc, argv);
	 return RUN_ALL_TESTS();
}

Дальше используется привычное сочетание с QEventLoop(для него и нужен был инстанс QXXXApplication)
Пример запуска сессии:

#include "Auth.h"
#include "QBloxGlobal.h"

#include <QtCore/QCoreApplication>
#include <QtCore/QEventLoop>

#include "gtest/gtest.h"

using namespace QBlox;

TEST(Auth, startSession)
{
	Auth auth(QBLOX_API_SERVER, QBLOX_AUTH_KEY, QBLOX_AUT_SECRET, QBLOX_APP_ID);

	auth.startSession();

	QEventLoop loop;
	QObject::connect(&auth, &Auth::sessionStarted,
			&loop, &QEventLoop::quit);
	loop.exec();
	const auto token = auth.token();

	ASSERT_FALSE(token.isEmpty());
}

Асинхронный запуск в Qt

Иногда необходимо выполнить достаточно длительный код в отдельном потоке, что бы не терять отзывчивость интерфейса в программе.

Обычно для этих целей используют два подхода:

  • наследование от QThread;
  • подход создания объекта рабочего и помещение его в отдельный поток.

Оба они приведены в документации к QThread, изначально второй подход был описан в How To Really, Truly Use QThreads; The Full Explanation.

Это конечно все здорово, но хочется простые вещи делать просто и с наименьшем количеством кода. И тут нам может помочь асинхронный запуск.

Асинхронный запуск в Qt представлен функцией QtConcurrent::run которая входит в состав API высокого уровня многопоточного программирования QtConcurent.  QtConcurrent::run достаточно хорошо описано, я лишь хочу продемонстрировать связку QtConcurrent::run, QFutureWatcherQEventLoop для создания последовательного когда не блокирующего UI

#include <QtCore/QThread>
#include <QtCore/QEventLoop>
#include <QtCore/QFutureWatcher>

#include <QtConcurrent/QtConcurrentRun>

#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
#include <QtWidgets/QProgressDialog>

class Foo
{
public:
    int bar ()
    {
        QThread::sleep(10);
        return 5;
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    Foo foo;
    QFutureWatcher<int> watcher;
    QEventLoop loop;
    QProgressDialog progress;
    progress.setRange(0,0);
    progress.setCancelButton(0);
    progress.show();

    QObject::connect(&watcher,	&QFutureWatcher<int>::finished,
                     &loop, &QEventLoop::quit);

    watcher.setFuture (QtConcurrent::run(&foo, &Foo::bar));

    loop.exec ();
    progress.close ();

    QLabel lbl;
    lbl.setText(QString("Result: %1").arg (watcher.result()));
    lbl.show();

    return a.exec();
}

Продолжение экспериментов с программирование под Andoid

Изменения внесенные перед отпуском:

  • Главное меню;
  • Страница отсчета времени тренировки;
  • Дополнительное меню, для переключения между страницами;
  • Фоновое изображение

Скриншоты:

menu

time

stopwatch

А так же демонстрация на живом устройстве HUAWEI U8825 на rutube

Анатомия Qt5 для Android

Вольный перевод Anatomy of a Qt 5 for Android application.

Предпосылки

Это выходит за рамки блога, чтобы объяснить, как работает слой абстракции Qt(Qt Platform Abstraction (QPA) ) . Кроме того, за рамками остается  подробное введение в разработку и сопровождение приложений в Android.  Она должна быть понятной и полезной без глубоких знаний технологий, если хотите знать больше, есть документация и блоги в интернете.

Достаточно сказать: Qt абстрагирует от оконной системы(windowing system) в API под названием «QPA«, так что зависимый от платформы код может быть выделен в плагин. Этот плагин будет управлять всеми обработчиками, от отображения, до передачи сообщений от оконной системы в цикл обработки событий Qt.  Android является одной из таких платформ, но во многом отличается от других платформ, поддерживаемых Qt, так как он по своей сути является Java-платформой. Android приложения это Java приложений, работающие под виртуальной машине «Dalvik». Это создает дополнительные проблемы при интеграции с C++  фреймворком  Qt.

Что приводит нас к «Java Native Interface» (или JNI для краткости). Это слой коммуникации между Java и C, и используется для передачи данных туда и обратно между операционной системой и платформой плагинов Qt. В Qt, мы работаем над некоторыми удобным API, вокруг JNI API, чтобы помочь вам объединить Qt код с кодом JNI, если вы хотите, чтобы ваш код взаимодействовал с Java.

В разрезе

На самом верхнем уровне, Qt для Android состоит из двух частей:

  • Qt приложение: Это кросс-платформенный код и ресурсы, которые вы, как разработчик приложения, управляете сами, и которые приведены в вашем qmake.pro файле
  • Пусковое приложение под Android, которое генерирует Qt Creator для вас, в при первом подключению к проекту.

Последнее состоит из:

  • Подкласса android.app.Application, что поддерживает привязку к Qt средствами Java’s Reflection API;
  • Подкласс android.app.Activity : Это точка входа в приложение.  Android, приложение может состоять из нескольких activitys, отвечая на различные intents. Однако по умолчанию приложений Qt будет состоять только из одного activity, которое может быть запущено из сетки приложений Android.  Несколько событий системы распространяется на activity вашего приложения, и распространяются по подклассам Qt, QtActivity класс также проводит загрузку родные файлы на основе выбранного метода развертывания и запуска приложения вызывая функцию main ();
  • Интерфейс, для подключения к службе Ministro: Ministro является механизмом развертывания, где Qt библиотеки загружаются и поддерживается внешней службой на целевом устройстве, и служит для уменьшения объема используемого пространства на каждое приложение Qt. Интерфейс используются для связи со службой в случае, если был выбран этот механизм развертывания
  • AndroidManifest.xml: Это jоснова мета-данных в Android приложениях. В какой-то момент вам придется изменить его, чтобы установить такие вещи, как имя вашего приложения, имя пакета, версию кода, значок, разрешений и т.д.
  • Другие мета-данные: Есть набор дополнительных файлов, используемых для хранения дополнительной информации о приложении. Это, например, информация о выбранном механизме развертывания в Qt Creator, макет для отображения заставки, перевод текста интерфейса Ministro и т.д.

Когда Qt Creator настраивает проект на работу с Qt под Android, он будет копировать эти файлы из каталога $QT/src/Android/Java . Он будет вносить изменения в файлы на основе параметров настройки развертывания вашей,  версии Android и т.д.  При разработке обычного приложения Qt, вам не придется изменять все это самостоятельно, за исключением AndroidManifest.xml и даже то, что может подождать, пока вы на самом деле не захотите развернуть приложения пользователям или в маркете. В этот момент вы, вероятно, захотите установить некоторые специализированные данные, такие как имя и значок.

Последняя часть пазла состоящая из:

  • QtActivityDelegate.java и других java файлов. Это позволит создать пользовательский интерфейс для приложений (только один SurfaceView для отрисовки  Qt), и заботиться о взаимодействия Android и QPA. Когда приложения получает activity события от операционной системы, они будут вызывать функции QtActivityDelegate и они будут передаваться  в Qt.
  • Платформа плагинов. Есть две платформы плагинов в Qt для Android, которые рассчитаны на двух различных вариантах использования. Первым из них является растровой плагин, который используется для QtWidget -приложений, которые не зависят от OpenGL.  Это имитирует некоторые поведения в традиционной оконной системы рабочего стола.  Другой на GL основе, и использоваться, например, для Qt Quick 2 приложения, которые зависят от OpenGL ES 2.

Запуск

Когда запускается Qt приложение, приложение будет просто обычным Java приложение. Точка входа будет в QtActivity.java которую можно найти в  android/src/ … в каталоге проекта. Этот код сначала проверит ваш проект мета-данных, который содержится в android/AndroidManifest.xml  и android/res/values/libs.xm , чтобы увидеть, какой механизм развертывания был выбран. Qt Creator будет обновлять мета-данных в соответствии с настройками проекта.  Для более глубокое представление о различных значениях, вы можете попробовать выбрать различные механизмы развертывания в Qt Creator и запустить приложение, затем просмотреть мета-данные, чтобы увидеть, что изменилось.

Существуют три различных механизмов развертывания, каждый из которых имеет несколько иной код запуска

  • Связка Qt библиотек в APK. При запуске, приложение скопирует некоторые из включенных в набор файлов в кэш;
  • Использовать сервис Ministro;
  • Развертывание  локальных Qt библиотек  во временный каталог.

Как только подготовка закончена, приложение будет первыми явно загружать Qt (и друге) библиотек, перечисленных в android/RES/values/libs.xml в указанном порядке. Когда это будет сделано, он загрузит платформу плагинов, который служит как плагин QPA и прослойку между Qt и Java. Этот плагин зарегистрирует набор собственных обратных вызовов, которые вызываются из Java в качестве реакции на Android событий, и это будет регистрацией интерфейсов QPA. Как только это будет сделано, то приложение будет загружать последнюю библиотеку: это ваше приложение.