Fresh impressions of the BlackBerry 10 NDK

Original author: Jens Weller
  • Transfer
image

For the past two weeks, I have delved into the BlackBerry 10 NDK again, as one of my clients asked me to help him. I suggested adapting my Introduction to Qt course to the BlackBerry platform, and also recommended following the tips in my tutorial series on BB10 and Cascades , published earlier this year on YouTube. Now I want to share with you my fresh impressions of the BlackBerry 10 NDK. By the way, I already wrote about my first experiments with BB10 NDK this spring.

Attention. This is a free translation of a note by Jens Weller. The translation was made to compile a general picture of the current state of the world [BB10 + Qt]. Enjoy reading.

Applications and C ++

Before you begin, a short introduction about applications and C ++. People transitioning from other languages, such as Java or .NET, often do not understand the need to write applications specifically in C ++. Especially moving from languages ​​forcibly tied to OOP and Garbage Collector - it is not easy for them to understand all the concepts used in C ++. In my opinion, in fact, there are many reasons to use C ++ to develop applications, especially in conjunction with such a powerful Qt framework. One of the reasons is performance, since C ++ is really closer to hardware, your application will eat a minimum of battery. Not to mention the fact that there is a ceiling for the growth of device performance in the future, Gerb Sutter mentioned this in his note “free rations are over” (there is a translation on the hub from webmascon- approx. translator ). The Qt Framework is now available for both Android and iOS, so C ++ and Qt / QML have become a really powerful combination for creating mobile applications.

NDK and Cascades

Therefore, when you develop applications for BlackBerry 10, you need to start with the BlackBerry NDK . I did not have enough time to play with the freshly released 1.2 version, but the IDE at first glance became noticeably better and more stable. But 1.2 has not yet received a "golden" status (fixing an API, has already received - translator's comment), so I recommend writing under 1.1, unless you need something from 1.2. BlackBerry NDK comes with the Cascades Framework, the API of which you will use when developing an application for BB10. Cascades is built on top of Qt and uses QML, while Qt5 has QtQuick 2 because BlackBerry has its own QML implementation that runs in a UI thread. So QML created under QtQuick1 or 2 will not work under Cascades. And Qt5 is not fully supported by Cascades - the current version is based on Qt 4.8.

Since 2010, I have been interested in mobile development on Qt and QML, I used to be busy with MeeGo, and now I'm with BlackBerry. QML in BB10 is slightly different, it uses special elements, for example: Container, Pages and Controls, while in QtQuick1 / 2 it offers quite basic elements like Item or Rectangle. So for QML and its API, BlackBerry has its own little world. In the meantime, Qt5 applications can be built and run on BB10, although this will not be accompanied by the degree of integration that Cascades offers.

QML and C ++

Judging by the documentation and discussions, the main approach comes down to using QML for most tasks and bridges in C ++ only when necessary. For example, when writing models in C ++ and declaring class methods using Q_PROPERTYQML to access them. Then most of the work happens on the QML side. QML has few ways to verify and check for errors, for example console.assert, but since QML is translated in JavaScript and does not have strong typing, it therefore cannot verify type matching. A typo in the variable name will lead to the fact that QML may not notice this error, but consider this variable declared new. A simple example:

Container {
    layout: DockLayout {
    }
    Label{
        text:ListItemData.titel
    }
}

This simple element for display in the container ListView, through ListItemDatawe get access to data for display. I made a small mistake here, in fact the element is called title, but titel- this is in German. Moreover, the German will not immediately notice it. And QML will not notice even more so. You can insert any word here, QML does not check for such errors not at compile time, nor at runtime. Nothing will be displayed, maybe you are lucky enough to find a warning in the console. By the way, if you correctly configure the IDE, then the message should definitely appear, even when debugging on the device.

How to deal with it? Behind Cascades lies the C ++ framework built on Qt, so at least in C ++ we have a chance to detect this and report the error. Unfortunately, this cannot be detected at compile time, but I am working in that direction. So Q_ASSERTdoes a check while the application is running. For all elements from Cascades involved in QML, there is a class in C ++ that is instantiated dynamically for each element at runtime. The Cascades API allows you to search for objects of these classes and provides a way to control some things. For ListViewas there is a class, which supplies components for ListViewfrom ++ the C: ListItemProvider. This class has its own interface:

virtual bb::cascades::VisualNode* createItem(bb::cascades::ListView* listview,const QString& type);
virtual void updateItem(bb::cascades::ListView* listview,bb::cascades::VisualNode* node,const QString& type,const QVariantList& indexPath, const QVariant& data);

Own implementation of these virtual methods allows you to create items for display in the ListView and also fill them with actual values. BlackBerry provides implementation examples. Unfortunately, these examples do not use QML, but are written entirely in C ++. But I personally like to use QML for the UI. And also such an OOP style, as in the example from BlackBerry, implies inheritance from ListItemProvidereach individual ListView, and I would like to solve this problem once and for all, so I have my own generalized one ListItemProvider. Since all checks occur while the application is running, C ++ templates are not an option, let's take a look at the following implementation. Before I move on to crateItem, a brief snippet on addType, a helper method for declaring handlers for each type:

void ListViewItemProvider::addType(const QString& type, const QString& qmlasset, const listitem_callback& callback)
{
    bb::cascades::QmlDocument* doc = bb::cascades::QmlDocument::create(qmlasset);
    if(!doc->hasErrors())
    {
        doc->setParent(this);
        type_map.insert(type,doc);
        callback_map.insert(type,callback);
    }//TODO add additional error checking & handling
}

This method adds different handlers for different types. Types are described in QML using objects QString, so it will be enough to store supported types. This method converts a qml resource (for example, a comment of a translator ) to using . Incidentally, it does not actually return , as written in the example above. It returns a reference to the utility class to create 's, then it seems to produce an implicit conversion to . In principle, there is nothing interesting here, we go further, the method :QMapfile:///partial.qmlQmlDocumentQmlDocument::createQmlDocument::createQmlDocument*BuilderQmlDocumentQmlDocument*createItem

bb::cascades::VisualNode* ListViewItemProvider::createItem(bb::cascades::ListView* listview,const QString& type)
{
     if(type_map.find(type)!=type_map.end())
     {
          bb::cascades::Container* node = type_map[type]->createRootObject();
          return node;
     }
     Q_ASSERT_X(false,__FUNCTION__,type +" TYPE not handled");
     bb::cascades::Container* con = new bb::cascades::Container(0);
     bb::cascades::Label* label = new bb::cascades::Label(con);
     label->setText("ERROR");
     return con;
}

This code first checks the type registration, and then creates the object through a method QmlDocument::createRootObjectthat returns a pointer to the created object. This is a template, so we need to know in advance the type of object to create. So far I have decided for myself that for all UI elements I will use Containeras the root object. Perhaps a type VisualNode*that is the return value of the method is also suitable here createItem. The most interesting part here is error handling, how should we do this? Here comes to the rescueQ_ASSERT_Xand notifies of an error. But in the case of assembly without debugging, it will not report anything, and nevertheless, the method should return some value. Nothing is said about 0 in the documentation, so there is no reason to think that this is a valid return value for this method. But on the other hand, the documentation clearly states that the returned pointer will belong ListVieweven if we return 0 (fortunately, the BB10 developers provided this), this will conceal the error from the tester. So I decided to return small ContainerwithLabelwith error text. Maybe I could have formulated the message better, but in this version the tester is more likely to notice this error. Alternatively, you could throw an exception, but after that control is transferred back to the Cascades API and Qt, and this is not the best option, since Qt and Cascades do not use exceptions, although they are supported in BB10.

The last thing to implement is the method updateItem. And again it doesn’t work out easier, because we write generalized code. In the end, the downloadable QML file must be loaded with the correct values, which was also one of the reasons why I decided to write a bikeimplement a generalized approach. But there is an option with borrowing the implementation for this directly from our class - we registered callbacks to create objects of the required types, now it remains only to call the corresponding callback in the method updateItem:

if(callback_map.find(type)!=callback_map.end())
{
#ifndef USE_BOOST
        (*callback_map[type])(node,indexPath,data);
#else
        callback_map[type](node,indexPath,data);
#endif
}

Until this moment, I could not use / hide the define USE_BOOST, but for such a callback, the C ++ programmer must first consider boost::function. And since BlackBerry claims that boost is supported, I certainly use it. And then it turns out that it is not so simple, at least my toolchain crashes with an error in boost/type_traits/detail/cv_trait_impl.hpp. I know that boost is used by many, so this is most likely an error due to the settings of my toolchain or Linux distribution. The error seems to come from the preprocessor, on GCC version 4.6.3, the error text says about parentheses mismatch. I preferred to patch my version of boost on a local machine and reported this to the boost community and to BlackBerry. If you use boost under BB10, then you better use the boost version from GitHub BlackBerry. Since not everyone needs boost, I also made a version without it, in case boost breaks down again.

And last but not least, callback implementation:

void ApplicationUI::callbackMyListItem(bb::cascades::VisualNode* node,const QVariantList& indexPath, const QVariant& data)
{
    bb::cascades::ImageView* image = node->findChild("imageview");
    Q_ASSERT(image);
    if(image)
    {
        QString name_image = "image"; //this must be correct!
        QVariantMap map = data.toMap();
        bool hasdata = map.contains(name_image);
        Q_ASSERT(hasdata);
        if(hasdata)
            image->setImageSource(map[name_image].toUrl());
    }
}

In this case, the path to the image is set. The pointer to is VisualNodeinherited from QObject, so that child objects can be obtained through findChild, which will return 0 if nothing is found. Therefore, it is appropriate to use here Q_ASSERTto verify this case. Then the data is searched in QVariantMap. Since in any case we need some kind of image, the element is checked in the QVariantMap container. If he is not, then Q_ASSERThe comes to the rescue again. This callback is simply registered with boost::bind.

The search for values ​​can also be implemented through the model, but BB10 does not support the usual models from Qt; instead, BlackBerry implemented their model classes. In general, this is good, but I personally prefer the models from Qt, especially since it would allow using them when porting a Qt application to Android, iOS, PC / Mac, or even Jolla. KDAB - one of our "gold" sponsors - has published a solution that eliminates this misunderstanding and makes Qt models suitable for use in Cascades .

IDE

Now, a few words about the IDE. As I said earlier, the IDE has improved with the release of version 1.2. The environment is improving, but in some cases it still remains far from perfect. The QML editor is still not good enough, but now it doesn’t drop the entire IDE when it crashes. An alternative could be Qt Creator, in which support for QML has also improved. At this point, it seems to me Momentics IDE (based on Eslipse - approx interpreter.) BlackBerry is better than QtCreator when it comes to Cascades development. Firstly, in QtCreator there is no integration with Cascades at all, therefore QML auto-completion will not work, because the required file for this is not available in the NDK. For the same reason, the visual QML editor will not work. Qt, of course, is slightly better supported in QtCreator, but there are really a lot of improvements in this direction in version 1.2 of the NDK. Project templates offered by QtCreator are not as good as in Momentics, for example, they lack integration of the localization code. I like the fact that templates in Momentics include QTranslator's main.cpp. Momentics and QtCreator Obm can create a working application for my DevAlpha device, so development for BB10 in QtCreator is possible, but there is room to grow.

I downloaded the source codeListViewItemProvider if you need ...

Popular comments (1 pc)

“Who wants to visit the morgue?”

Also popular now: