📜 ⬆️ ⬇️

Fresh impressions of the BlackBerry 10 NDK

image

For the last two weeks, I’ve been digging into BlackBerry 10 NDK again, as one of my clients asked for help. I offered to adapt my “introduction to Qt” course to the BlackBerry platform, and also recommended following the tips from my series of BB10 and Cascades training videos published on YouTube earlier this year. 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 Jans Weller’s note . The translation was made to create a general picture of the current state of the world [BB10 + Qt]. Enjoy reading.

Applications and C ++

Before you begin, a brief introduction about applications and C ++. People migrating from other languages, like Java or .NET, often do not understand the need to write applications in C ++. Especially moving from languages ​​forcibly linked to OOP and Garbage Collector - it’s not easy for them to understand all the concepts used in C ++. In my opinion, in fact, there are many reasons to use C ++ for developing 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 power. Not to mention the fact that there is a ceiling on the growth of device performance in the future, mentioned this . Gert Sutter in his note “free rations ended” The Qt Framework is now available for both Android and iOS, so C ++ and Qt / QML have become a really powerful combination for building mobile apps.
')
NDK and Cascades

Therefore, when you develop applications for BlackBerry 10, you need to start with BlackBerry NDK . I didn’t have enough time to play with the freshly hung version 1.2, but at first glance IDE became noticeably better and more stable. But 1.2 has not yet received the “golden” status (fixing the API, it has already received - approx. Translator ), so I recommend writing under 1.1, unless you need something from 1.2. The BlackBerry NDK comes with the Cascades Framework, the API of which you will use when developing an application for BB10. Cascades is made on top of Qt and uses QML, while Qt5 has QtQuick 2, because BlackBerry has its own implementation of QML running in a UI stream. 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 in Qt and QML, MeeGo used to be busy, 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 the 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 basic 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_PROPERTY to access their QML. Then most of the work happens on the QML side. QML has some verification methods and error checks, for example console.assert, but since QML is translated in JavaScript and does not have a strong typing, therefore, it cannot verify the type conformity. A typo in the variable name will lead to the fact that QML may not notice this error, and calculate this variable as declared new. A simple example:

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

This element is simple to display in the ListView container. Through ListItemData we get access to the data to be displayed. I made a small mistake here, in fact, the element is called the title , and titel is in German. Moreover, the German did not immediately notice. And QML will not notice the more. You can insert any word here, QML does not check for this kind of error, not at compile time or at run time. Nothing will be displayed, perhaps you are lucky to find a warning in the console. By the way, if you configure the IDE correctly, the message should appear exactly, even when debugging on the device.

How to deal with it? Cascades hides a C ++ framework built on Qt, so at least in C ++ we have a chance to detect this and record the error. Unfortunately, this cannot be detected at compile time, but I am working in this direction. So Q_ASSERT does the checking while the application is running. For all elements of Cascades involved in QML, there is a class in C ++ that instantiates dynamically for each element during the execution of the application. The Cascades API allows you to search for objects of these classes and provides a way to control certain things. ListView also has a class that supplies items for a ListView from 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); 

Its implementation of these virtual methods allows you to create elements for display in the ListView and also fill them with actual values. BlackBerry provides examples of implementation. Unfortunately, these examples do not use QML, but are written entirely in C ++. But I personally like to use QML for UI. And still such OOP-style, as in the example from BlackBerry, implies inheritance from ListItemProvider for each individual ListView , and I would like to solve this problem once and for all, so I have my own generic 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 go to crateItem, a brief install on addType, an auxiliary 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 defined in QML using QString objects, so QMap<QString, QmlDocument*> will be sufficient to store the supported types. This method converts a qml-resource (for example, file:///partial.qml - approx. Translator ) into a QmlDocument using QmlDocument::create . By the way, QmlDocument::create does not actually return a QmlDocument* , as written in the example above. It returns a reference to the utility class Builder to create a QmlDocument 's, then it looks like it produces an implicit conversion to QmlDocument* . In principle, there is nothing interesting here, let's go further, the createItem method:

 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<bb::cascades::Container>(); 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 an object through the QmlDocument::createRootObject , which 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 the Container as the root object. Perhaps the VisualNode* type, which is the return value of the createItem method, is also suitable here. The most interesting thing here is error handling, how should we do this? Here Q_ASSERT_X comes to the Q_ASSERT_X and notifies you of an error. But in the case of assembly without debugging, it will not tell anything, and nevertheless the method should return some value. About 0 does not say anything in the documentation, so there is no reason to think that this is the correct return value for this method. But the documentation clearly states that the returned pointer will belong to the ListView Even if we return 0 (fortunately, the BB10 developers provided for it), this will hide the error from the tester. So I decided to return a small Container with a Label with an error text. Maybe I could formulate the message better, but in this version the tester will notice this error more likely. Alternatively, an exception could be thrown, 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 updateItem method. And again it does not work out easier, because we are writing generic code. In the end, the loaded QML file must be loaded with the correct values, which was also one of the reasons why I decided to write a bicycle to implement a generalized approach. But there is an option to borrow the implementation for this directly from our class - we registered callbacks and create objects of the necessary types, now it remains to simply call the corresponding callback in the updateItem method:

 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 } 

Up to this point, I could not use / hide the USE_BOOST 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 here it turns out that it is not so easy, at least my toolbar falls with an error in boost/type_traits/detail/cv_trait_impl.hpp . I know that boost is being used by many, so this is most likely an error due to the settings of my system or the Linux distribution. The error seems to come from the preprocessor, on the GCC version 4.6.3, the error text says about the mismatch of the brackets. I preferred to patch my version of boost on the local machine and reported this to the boost community and to the BlackBerry. If you use boost under BB10, then you better use the boost version from GitHub's BlackBerry . Since not everyone needs a boost, I also made a version without it, just in case the boost breaks again.

And last but not least, the implementation of callback'a:

 void ApplicationUI::callbackMyListItem(bb::cascades::VisualNode* node,const QVariantList& indexPath, const QVariant& data) { bb::cascades::ImageView* image = node->findChild<bb::cascades::ImageView*>("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. A pointer to a VisualNode inherited from a QObject , so the child objects can be obtained via findChild , which returns 0 if nothing is found. Therefore it is appropriate to use Q_ASSERT here to test this case. Then there is a search for data in QVariantMap . Since, in any case, we need some image, we check the presence of an element in the QVariantMap container. If not, then Q_ASSERT 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 the BB10 does not support regular models from Qt, instead BlackBerry implemented its model classes. In general, this is good, but I personally prefer models from Qt, the more it would allow to use them when porting Qt applications for 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 IDE. As I said earlier, 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 does not drop the entire IDE when it is dropped. An alternative could be Qt Creator, in which QML support has also improved. At the moment, I think Momentics IDE (based on Elipse - approx. Translator ) from BlackBerry is better than QtCreator, if we are talking about the development under Cascades. First of all, there is no integration with Cascades at all in QtCreator, therefore QML autocompletion will not work, because the NDK does not have the necessary file for this. For the same reason, the visual QML editor will not work. Qt, of course, is slightly better supported in QtCreator, but in version 1.2 of the NDK there are indeed many improvements in this direction. The 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 in main.cpp . Momentics and QtCreator can create a working application for my DevAlpha device, so development under BB10 in QtCreator is possible, but there is room to grow.

I downloaded the ListViewItemProvider source codes if you need ...

Popular comments (1 pcs)

- Who wants to visit the morgue?

Source: https://habr.com/ru/post/192064/


All Articles