viernes, 22 de febrero de 2019

Hemos solucionado un error crìtico en Unik Qml Engine

Desde hace unas semanas, hemos publicado nuevas actualizaciones de Unik. Las versiones anteriores, si bien siempre fueron compatibles con Windows, GNU/Linux y Macos, dichas versiones menores a la versiòn 3.x, no eran compatibles con Android 7 o superior. Las actualizaciones superiores a la versiòn 3.x, cuentan con la compatibilidad y funcionalidad plena sobre los sistemas operativos mencionados pero ademàs tambien lo son para los OS Android 5/6/7/8/9.

Todo o casi todo funcionaba segùn lo esperado, excepto la funciòn log(QByteArray) de unik. Èsta funciòn, no estaba funcionando en Unik para sistemas Android con arquitectura armeabi-v7a. Èsta falla estaba provocando un cierre inesperado de la aplicaciòn. Un error terriblemente crìtico que nos ha obligado a anular dicha funciòn para dicha arquitectura, por lo menos hasta que encontremos una soluciòn.

El gran problema, era que dicho error estaba presente y los que formamos parte del la parte dura del desarrollo de bajo nivel en el equipo de unikode.org, tenìamos pasaje para vacacionar. Lamentablemente no ha quedado otro camino que intentar una soluciòn al regreso.

Regresamos el 19/02/2019. El calor era insoportable. Con una sensaciòn tèrmica de 41 grados en el exterior, por razones de fuerza mayor nuestros estudios estaban algo condicionados y no podìamos lograr la climatizaciòn dentro del ambiente de programaciòn. Fuè asì que luego de 4 dìas intensos de programar, probar, depurar, instalar, desinstalar, etc, hemos dado con la soluciòn.

Por momentos la situaciòn se tornò realmente agotadora. Hay que admitir que el desànimo por momentos nos empujò a creer que todo estaba perdido, a pensar negativamente sobre todo lo relacionado al proyecto unik, sus funcionalidades, propòsitos, estrategias, mòdulos, etc, fueron momentos durìsimos.

La paciencia es lo que un programador debe conservar, mucho màs aùn en momentos crìticos. La falta de resultados y el agotamiento nos llevò a estados de ànimo no muy adecuados, esos que nos impulsan a intentar mètodos o maneras descabelladas. Hemos dudado mucho, hemos cambiado y probado de todos los modos posibles, compilando con todos los NDK, el r10e, el r16b, el r17b y el r18b. Pasando de compilaciones con el API 26 y volviendo al API 28, probando còdigo una y otra vez. Tantas variaciones casi nos hace perder el rumbo de tanto desformar el còdigo.

Por fortuna, existen ciertas herramientas que nos salvan para esta situaciones. Por ejemplo Git. Hemos ido retrocediendo en el tiempo, buscando la versiòn del còdigo que màs estabilidad y respuesta nos provea, una vez que hemos vuelto a un punto de partida por vez nùmero 10 en la busqueda de esta soluciòn, como estrategia salvadora hemos recurrido a la creaciòn de una clase C++ llamada UnikLogObject, tal como lo dice su nombre es un objeto que administra o mejor dicho sirve de puente o nexo para conectar la funcion estàtica C++ encargada del manejo de los mensajes de salida estandar en android dependientes de las cabeceras log.h de android.

Expliquemos mejor esto ùltimo. En Qt, cuando programamos en C++, en la funciòn principal o en otras partes, pero generalmente en la funciòn main, en el caso que sea necesario o ùtil, allì podemos especificarle a nuestra aplicaciòn, cuàl serà la funciòn encargada de gestionar los mensajes de la salida de la aplicaciòn relacionados con mensajes crìticos, fatales, de depuraciòn, de informaciòn etc. Esto Qt lo hace con la previa declaraciòn de una funciòn del tipo static llamada por ejemplo myMessageHandler(), la cuàl se utiliza en nuestra aplicaciòn llamando al mètodo qInstallMessageHandler().

Ejemplo

Archivo main.cpp

static void myMessageHandler(QtMsgType type,
                                    const QMessageLogContext &context,
                                    const QString &message)
{
    android_LogPriority priority = ANDROID_LOG_DEBUG;
    switch (type) {
    case QtDebugMsg: priority = ANDROID_LOG_DEBUG; break;
    case QtWarningMsg: priority = ANDROID_LOG_WARN; break;
    case QtCriticalMsg: priority = ANDROID_LOG_ERROR; break;
    case QtFatalMsg: priority = ANDROID_LOG_FATAL; break;
    };
    __android_log_print(priority, "Qt", "%s", qPrintable(message));
}
int main(int argc, char *argv[])
{
...
qInstallMessageHandler(myMessageHandler);
...
}

El problema es que dicha funciòn estàtica no puede llamar funciones de objetos previamente declarados a la funciòn main. Esto en un principio se solucionò creando variables del objeto de unik que se encarga de mostrar la salida en QML, dicha variable se crea al principio de todo el còdigo, es decir antes que la funcion main y antes que la funcion estàtica mencionada. Esa soluciòn funcionò en las versiones anteriores, funcionaba para todos los sistemas en cualquier arquitectura, pero lamentablemente cuando escalamos APIs de Android arriba llegando a la versiòn 28, esta estrategia comenzò a provocar un apagado inesperado de unik, un error fatal.
¿Como lo hemos solucionado? Lo solucionamos con la creaciòn del objeto UnikLogObject que hemos mencionado. Gracias a los benditos SIGNAL Y SLOT de Qt, hemos conectado nuestro nuevo objeto desde dentro del main hasta la funciòn estàtica. Lo hemos hecho del siguiente modo.

Archivo main.cpp

UnikLogObject ulo;
static void myMessageHandler(QtMsgType type,
                                    const QMessageLogContext &context,
                                    const QString &message)
{
    android_LogPriority priority = ANDROID_LOG_DEBUG;
    switch (type) {
    case QtDebugMsg: priority = ANDROID_LOG_DEBUG; break;
    case QtWarningMsg: priority = ANDROID_LOG_WARN; break;
case QtCriticalMsg: priority = ANDROID_LOG_ERROR; break;
    case QtFatalMsg: priority = ANDROID_LOG_FATAL; break;
    };
    __android_log_print(priority, "Qt", "%s", qPrintable(message));
    ulo.setLog(message.toUtf8());
}
int main(int argc, char *argv[])
{
    ...
    qInstallMessageHandler(myMessageHandler);
    UK u;
    QObject::connect(&ulo, SIGNAL(logReceived(QByteArray)),
                                &u, SLOT(log(QByteArray)));
...
}

Hemos conectado mediante el mètodo connect() de la clase QObject a el objeto de la clase UnikLogObject mediante su señal logReceived() hacia el objeto principal de unik de la clase UK llamada "u" hacia su mètodo o slot log(). De este modo lo hemos resuelto. Ahora ya hemos publicado en el sitio oficial de descargas de unik y en la Google Play Store, el archivo instalador de Unik Qml Engina para Android 5/6/7/8/9 para la arquitectura armeabi-v7a. Se trata del APK de Unik versiòn 3.7.1 que nos permite ver la salida estandar en nuestros proyectos QML. El fallo està solucionado, ahora la aplicaciòn no se cierra inesperadamente, cada vez que se reinicia para cargar algùn mòdulo especìfico o se solicita el cierre con el mètod Qt.quit() o qApp->quit(), en ningùno de los casos se apaga inadecuadamente.




No hay comentarios:

Publicar un comentario

Entrada destacada

QtQuick para Android - Unik Qml Examples - #4 - Conexiòn Sqlite desde Android

En el dìa de la fecha hemos publicado el cuarto video sobre QtQuick en YouTube.com. Este vìdeo corresponde a una serie de videos que est...