Update: Here you have also the Chinese version, thanks goes to Foruok.
In the last two Qt on Android episodes we learned how to use basic JNI on Android and how to use an external IDE to easily manage the Java part. In this episode, it is time to move forward and focus on extending our Qt on Android Java part and also how to interact with it using JNI in a "safe way".
In this part we are going to implement an SD-Card listener. This is quite a useful example for applications that are using SD-Cards to store their data, because if the application doesn't close all the opened files immediately when it gets the notification, it will be killed by the Android O.S.
As we've seen in Episode 5 it's quite easy to call a Java method from C/C++ and a C/C++ function from Java, but it doesn't work on all cases. But why not?
To understand why not, we need first to understand the Qt on Android architecture.
Architecture diagram:
A few words about the architecture diagram.
the left blue rectangle represents the Android UI thread
the right green rectangle represents the main Qt thread (where the main QEventLoop is running). Read Episode 1 if you want to learn more about Android UI & Qt threads)
the top (black) rectangle is the Java part of your application. As you can see the biggest part of it runs on the Android UI thread. The only case when the Java part runs on the Qt thread is when we call it from C/C++ from Qt thread (as most of the JNI calls will come from there).
the bottom (black) rectangle is the C/C++ (Qt) part of your application. As you can see the biggest part of it runs on the Qt thread. The only case when the C/C++ part runs on the Android UI thread is when it's called from the Java part from Android UI (as most of the Java callbacks will be from there).
Ok ... so what's the problem? Well, the problem is that there are SOME Android APIs that MUST be called from Android UI thread, and when we call a Java method from C/C++ we do it from Qt thread. It means that we need a way to run that code on Android UI not on Qt thread. To do such a call, from C/C++ Qt thread to Java Android UI thread, we need to do 3 steps:
call a Java method from C/C++ Qt thread. The Java method will be executed in Qt thread, so we we need a way to access Android APIs in Android UI thread.
our Java method uses Activity.runOnUiThread to post a runnable on Android UI thread. This runnable will be executed by the Android event loop on Android UI thread.
the runnable accesses the Android APIs from Android UI thread.
The same problem occurs when Java calls a C/C++ function, because Java will call our C/C++ functions from Android UI and we need a way to pass that notification on Qt thread. Again there are 3 steps involved:
Qt event loop will execute that function on Qt thread.
Extending the Java part:
Before you start, make sure you read Episode 6 one more time because you'll need it to easily manage the Java files. First step is to create a custom Activity by extending QtActivity and defining a method which will post our Runnable.
// src/com/kdab/training/MyActivity.javapackagecom.kdab.training;importorg.qtproject.qt5.android.bindings.QtActivity;publicclassMyActivityextendsQtActivity{// this method is called by C++ to register the BroadcastReceiver.publicvoidregisterBroadcastReceiver(){// Qt is running on a different thread than Android.// In order to register the receiver we need to execute it in the Android UI threadrunOnUiThread(newRegisterReceiverRunnable(this));}}
We need to do this to make sure that our custom Activity will be instantiated when the application starts.
Next step is to define our RegisterReceiverRunnable class: The run method of this class will be called on Android UI thread. In run method we register our SDCardReceiver listener.
// src/com/kdab/training/RegisterReceiverRunnable.javapackagecom.kdab.training;importandroid.app.Activity;importandroid.content.Intent;importandroid.content.IntentFilter;publicclassRegisterReceiverRunnableimplementsRunnable{privateActivity m_activity;publicRegisterReceiverRunnable(Activity activity){ m_activity = activity;}// this method is called on Android Ui Thread@Overridepublicvoidrun(){IntentFilter filter =newIntentFilter(); filter.addAction(Intent.ACTION_MEDIA_MOUNTED); filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); filter.addDataScheme("file");// this method must be called on Android Ui Thread m_activity.registerReceiver(newSDCardReceiver(), filter);}}
Let's check what SDCardReceiver class looks like:
// src/com/kdab/training/SDCardReceiver.javapackagecom.kdab.training;importandroid.content.BroadcastReceiver;importandroid.content.Context;importandroid.content.Intent;publicclassSDCardReceiverextendsBroadcastReceiver{@OverridepublicvoidonReceive(Context context,Intent intent){// call the native method when it receives a new notificationif(intent.getAction().equals(Intent.ACTION_MEDIA_MOUNTED))NativeFunctions.onReceiveNativeMounted();elseif(intent.getAction().equals(Intent.ACTION_MEDIA_UNMOUNTED))NativeFunctions.onReceiveNativeUnmounted();}}
SDCardReceiver overrides onReceive method, then it uses the declared native functions to send the notification to C/C++.
Last step is to declare our native functions that we used in SDCardReceiver:
// src/com/kdab/training/NativeFunctions.javapackagecom.kdab.training;publicclassNativeFunctions{// define the native function// these functions are called by the BroadcastReceiver object// when it receives a new notificationpublicstaticnativevoidonReceiveNativeMounted();publicstaticnativevoidonReceiveNativeUnmounted();}
Architecture diagram Java:
Let's see the summary of the Java part calls on our architecture diagram:
Extending C/C++ part:
Now let's see how we extend the C/C++ part. To illustrate how to do it, I'm using a simple widget application.
First thing we need to do, is to call the registerBroadcastReceiver method.
// main.cpp#include"mainwindow.h"#include<QApplication>#include<QtAndroid>intmain(int argc,char*argv[]){ QApplication a(argc, argv);// call registerBroadcastReceiver to register the broadcast receiverQtAndroid::androidActivity().callMethod<void>("registerBroadcastReceiver","()V");MainWindow::instance().show();return a.exec();}
// native.cpp#include<jni.h>#include<QMetaObject>#include"mainwindow.h"// define our native static functions// these are the functions that Java part will call directly from Android UI threadstaticvoidonReceiveNativeMounted(JNIEnv */*env*/, jobject /*obj*/){// call MainWindow::onReceiveMounted from Qt threadQMetaObject::invokeMethod(&MainWindow::instance(),"onReceiveMounted", Qt::QueuedConnection);}staticvoidonReceiveNativeUnmounted(JNIEnv */*env*/, jobject /*obj*/){// call MainWindow::onReceiveUnmounted from Qt thread, we wait until the called function finishes// in this function the application should close all its opened files, otherwise it will be killedQMetaObject::invokeMethod(&MainWindow::instance(),"onReceiveUnmounted", Qt::BlockingQueuedConnection);}//create a vector with all our JNINativeMethod(s)static JNINativeMethod methods[]={{"onReceiveNativeMounted","()V",(void*)onReceiveNativeMounted},{"onReceiveNativeUnmounted","()V",(void*)onReceiveNativeUnmounted},};// this method is called automatically by Java after the .so file is loadedJNIEXPORT jint JNI_OnLoad(JavaVM* vm,void*/*reserved*/){ JNIEnv* env;// get the JNIEnv pointer.if(vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)!= JNI_OK)return JNI_ERR;// search for Java class which declares the native methods jclass javaClass = env->FindClass("com/kdab/training/NativeFunctions");if(!javaClass)return JNI_ERR;// register our native methodsif(env->RegisterNatives(javaClass, methods,sizeof(methods)/sizeof(methods[0]))<0){return JNI_ERR;}return JNI_VERSION_1_6;}
In native.cpp we are registering the native functions. From our static native functions we are using QMetaObject::invokeMethod to post the slots call to Qt thread.
MainWindow class is used just to add some text to our plainText control when it gets a notification. Calling these functions from Android thread might be very harmful to our application health - it might lead to crashes or unexpected behavior, so they MUST be called from Qt thread.
Architecture diagram C/C++:
This is the summary of C/C++ calls on our architecture diagram:
Architecture diagram Java & C/C++:
This is the summary of all the calls that we've done in C/C++ and in Java.
This example is very good, but still the show stopper for me is the possibility to handle intents. Maybe you have some working example for that? I've described in one of the emails on interest mailing list what I've achieved so far, but haven't got it working:
http://lists.qt-project.org/pipermail/interest/2015-April/016320.html
And of course, please join this and other Qt+Android conversations on the lists!
3 - Jun - 2015
BogDan Vatra
disclaimer: I read your mail very briefly.
So, you want to create an application (viewer) that handles some file extensions? I'm pretty sure it is already possible, you just need to tweak a little bit your android manifest file, set APPLICATION_PARAMETERS [0] correctly in your custom activity, make sure your let the application to quit and that it. If you'll control the intent then you can pass the params via applicationArguments stringExtras key [1].
Invoking it for the first time is not a problem, only minor QtActivity code modification is needed. But then when you switch to file manager and try to open another file - android will not quit the first instance of viewer, it will just restore it, with the first opened file. I was not able to open second file when the first one is already opened.
3 - Jun - 2015
BogDan Vatra
First and foremost, as I said many times, you should NOT change QtActivity, instead you should extend it.
If you want to make sure that your application quits you can listen for QGuiApplication::applicationStateChanged [0] and quit the application when state is Qt::ApplicationSuspended.
Sorry, I meant QtNative, not QtActivity. I would contribute the fix upstream if I will be able to make it work as I wanted.
I see your point about quitting, but it will affect the startup time on every new intent.
3 - Jun - 2015
BogDan Vatra
If your fix doesn't break anything else than you can upstream it.
If you want to keep your application running than you need to send the params using JNI.
18 - Jun - 2015
Federico
I've solved this problem in another way. Instead of restarting the application I have set the activity with property android:launchMode="singleTask"; this way the same, opened, instance is called when an intent is launched. I've also extended the QtActivity with the method "onNewIntent(Intent i)" in which I call "setIntent(i): this way the subsequent call to the extras() method will return the parameters contained in the new Intent, instead of the original intent which started the app. I've only ONE last issue: notify Qt of the new Intent. Guess this episode is what I'm looking for. Thanks BogDan! :)
Hi, BogDan Vatra. In my practice, I tend to register natives by using my custom functions and let it run before QQmlApplicationEngine gets started rather than "JNI_OnLoad( )" function, and it also works.
In fact I'm going to encapsulate some open authentication platforms into my plugin and expose some informations to QML environments, registering methods within "QQmlExtensionPlugin::registerTypes( )" function seems a good practice.
3 - Jun - 2015
BogDan Vatra
It doesn't matter where you register them as long as you'll register them before the java part calls them.
JNI_OnLoad( ) gives us everything we need to register them (JavaVM pointer), that's why I'm using it and that is the reason why I'm telling people to use it.
14 - Jun - 2015
jiangcaiyang
How is using AsyncTask on Java side in replace of Runnable?
16 - Jun - 2015
BogDan Vatra
HUH ?!?! The idea is to run some code on Android UI thread.
AsyncTask is used for something else. It's usefull when you want to do some heavy work in background and publish the results on Android UI thread. I don't see how AsyncTask is better than the humble Runnable and Activity.runOnUiThread for what we need...
Of course AsyncTask might be useful for other examples, but for the example in the article is useless.
16 - Jun - 2015
jiangcaiyang
What about the solution on creating instance in C++ by JNI whereas it affiances to Android UI thread. subclass Activity and write a new clause is a solution, but I don't want to disturb QtActivity.
16 - Jun - 2015
BogDan Vatra
I think I fail to follow you :) .
You want to create a new Activity from JNI? If the activity is not declared in your manifest file, you can't create a new one at runtime.
Also the main Activity and Qt needs separate threads because they have their own event loops and they can't leave togheter into a single thread (check http://www.kdab.com/qt-on-android-episode-1 for more info on this matter).
15 - Jul - 2015
daniel
Hello ,I have a "already working " Qt application ,an instant messaging one ,it uses QXmpp and I am porting it to android.
The problem I am facing is that when the application is inactive for some time it is killed by the system .I tried to remedy on this by writting an android service using (asmack) and handling received messages and opening my application again and pass in the received messages .
This is starting to look like a bad design as I have to log off from the server and let the service take control every time the user minimises the app or when the screen goes black.
I was wondering if there is a better way of hanling "long running operations on android (Qt) or should I just offload the Instant messaging details to the background service.
Thank you.
23 - Oct - 2015
BogDan Vatra
Using a service is the Android way to do long term background operations.
So, IMHO you should create a service which processes the data, post notifications and start the Qt UI using an Intent if needed.
23 - Oct - 2015
Tilly Pavone
Hello Bodgan, thank you for your job (yes, I'm really impressed!).
I have a question on how the Android UI thread and the Qt thread relate to the process running the Android app, in particular when it comes to acquire a WakeLock.
While developing a GPS app, I ended up writing an example that shows how, if holding a PARTIAL_WAKE_LOCK, the QtActivity keeps working also in the background, as expected, while the Qt thread goes to sleep.
How is that possible? Do Android WakeLocks, as it seems, get effect at a thread level?
Here is an excerpt from my sample:
*** java ***
public class UtyActivity extends QtActivity
{
private PowerManager.WakeLock m_wakelock;
Timer m_timer;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// starts Android UI task
m_timer = new Timer();
DebugTask task = new DebugTask();
m_timer.schedule(task, 1, 10000);
// acquire a partial WakeLock here (not working using JNI from Qt)
// NB: We are in the Android UI thread here, aren't we?
// (anyway I also tryed using runOnUiThread)
Context context = getApplicationContext();
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
m_wakelock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "com.utillyty.android.wakelock");
m_wakelock.acquire();
}
// ---> THIS KEEPS WORKING ALSO IN BACKGROUND
class DebugTask extends TimerTask
{
public void run()
{
// http get on test server: the request is always delivered
...
========== C++ ==========
// ---> THIS BLOCKS WHILE APP IN THE BACKGROUND
void MainPanel::timerEvent(QTimerEvent *)
{
// same http get as java: no request is delivered while in the background
...
23 - Oct - 2015
BogDan Vatra
By default the main QEventLoop is freezed when it goes in background. We need to do it because the surface that Qt needs to paint to is not available anymore and if you try to paint something your application might crash.
There are two ways to keep going on Qt world after it goes in background:
- create a new thread , call QThread::exec() then create all the timers and other QObjects that you need the in that thread.
- don't freeze the Qt main loop. You need to edit AndroidManifest.xml and set "android.app.background_running" to "true", but you must make sure you don't draw anything when the application is backgrounded.
23 - Oct - 2015
Tilly Pavone
It works great!
If I need to draw anything when the app may be running in the background, I just check if "isVisible()" is "false" and if so, store the data in memory and do the job in the "showEvent()" function.
Thanks a lot!
26 - Oct - 2016
Sudha
Hello, I need to control device orientation, both iOS and Android from qt5.6 and c++. What's the best way
12 - Nov - 2016
seniorivn
Hi, it is the best articles about qt on android that i've found. But it's didn't answer on my questions :(
Here is my problem:
1) i'm trying to write soft keyboard using qt.
2) android use android.view.View to draw keyboard
3) so i need to use View to draw qt interface, but standard way to draw qt on android is QtActivity.
4) I've found a way to do that, by extendinng View, with custom onDraw() that will get bitmap of qt interface by grabWindow() and put it into view.
5) Do you think it's normal solution? Does anyone have any better ideas?
Thanks anyway, you did a great job.
14 - Nov - 2016
BogDan Vatra
I think you're wrong, "standard" way to draw qt is by using a [Qt]SurfaceView not the QtActivity.
QtActivity is used just to forward all the events to Qt world.
I never tried to write an android keyboard so, I don't know all the details, but using grabWindow() doesn't seem the best solution...
If you can add a single SurfaceView to android.view.View that (apparently) is needed to draw the keyboard, then Qt can use it to draw its stuff too.
2 - Dec - 2016
Valentin OFFNER
Hi,
I have a little question about this article and signals.
Shoud I always use the QMetaObject::InvokeMethod or can I call the Q_EMIT directly?
thanks very much to sharing your knowledge about Qt/Android with us.
I have read your Android/Qt article and watched some of your presentation at Qt Con.
I have developed a quit simple Qt/Android application, which is using the Camera and some other features of my Smartphone.
The application is working well when I use "Bundle Qt libraries in APK" but when I try "Use Ministro service to install Qt". Nothing works!
My Application uses some JNI functions, so I have create JNI_OnLoad() function as your show in your presentation at Qt Con 2016. But when I start it I've got following error:
E/art ( 8451): No implementation found for void com.geocept.android.NativeFunctions.onWifiStateChanged(boolean) (tried Java_com_geocept_android_NativeFunctions_onWifiStateChanged and Java_com_geocept_android_NativeFunctions_onWifiStateChanged__Z)
This only happens when I generate the APK with "Use Ministro service to install Qt".
Why?
Can you help me?
19 - Dec - 2016
BogDan Vatra
I'll need more info on this matter: e.g. what Qt version are you using?
19 - Dec - 2016
Fabrice Mousset
Here my configuration:
- Qt 5.7.1 / Android armeabi-v7
- Android NDK r13b
- Qt Creator v4.2.0
- Workstation: Windows 7 pro/64 bit
- Android Lollipop
- Android API level used: 21
19 - Dec - 2016
BogDan Vatra
Qt 5.7.x wasn't yet uploaded to Ministro repos, I'm still testing it ...
I hope I'll upload it in a few days.
19 - Dec - 2016
Fabrice Mousset
Thanks for your answer,
Do you have a deadline for this update?
Before end of this week (before Xmas) or next year?
I have to inform customer about software availability.
20 - Dec - 2016
Fabrice Mousset
Hi BogDan,
in the meantime, I found a quick&dirty solution which "WorksForMe(tm)".
To save data space on APK transfer to the device, I have create a full APK.
On this APK I have extract all Qt specific stuff.
Those files are transferred into device internal storage (backup copy).
For every next APK release, I remove Qt stuff from APK.
Send this small APK (less than 2 MB) to the device, on device side a insert remove Qt stuff to complete the APK then start a "normal" APK install on Android.
It is very dirty but it works for my needs.
Perhaps, you can provide an "extended" version of Ministro, with a kind of "Off-Line" or "Static" mode in which it is possible to setup a device with specified Qt version, for example downloaded from Qt repository. The transfer this version on device.
So all APK generated for this device do not need any Qt dll/qml/whatever. This can be useful for a business solution.
I don't think this will be so much changes on Ministro side.
What do you think about this?
Regards
Fabrice
20 - Dec - 2016
BogDan Vatra
I just uploaded Qt 5.7 a few hours ago, it should already be visible.
18 - Dec - 2016
Ahmed Arif
just wandering isn't possible to make the "static void onReceiveNativeMounted" inside Mainwindow's class so it will be "static void Mainwindow::onReceiveNativeMounted" and then call ui->plainTextEdit.... directly from it ? this could be more elegant i guess ?...
imagine if we have many many functions to pass with it data from java to C++, making statics functions along side with the same number of slots will rise the ability of maintaining the code ...
19 - Dec - 2016
BogDan Vatra
Calling a static method (from your class) or a static function it doesn't change the thread from it's called.
So "Mainwindow::onReceiveNativeMounted" is called from Android UI thread no matter what and trying to call "ui->plainTextEdit" directly (though you can't use it directly because "ui" is not accessible from a static method) will not automatically switch the threads and it will lead to a crash (sooner or later).
20 - Oct - 2020
Wenderson Costa
Good explanation, but you would a actualization of this.
19 - Jan - 2021
BogDan Vatra
+90% of this material should still apply today.
I don't know when I'll have time to update it.
Bogdan Vatra
Senior Software Engineer
Bogdan Vatra is a Senior Software Engineer at KDAB
36 Comments
2 - Jun - 2015
Stanislav
Bogdan,
This example is very good, but still the show stopper for me is the possibility to handle intents. Maybe you have some working example for that? I've described in one of the emails on interest mailing list what I've achieved so far, but haven't got it working: http://lists.qt-project.org/pipermail/interest/2015-April/016320.html
And of course, please join this and other Qt+Android conversations on the lists!
3 - Jun - 2015
BogDan Vatra
disclaimer: I read your mail very briefly.
So, you want to create an application (viewer) that handles some file extensions? I'm pretty sure it is already possible, you just need to tweak a little bit your android manifest file, set APPLICATION_PARAMETERS [0] correctly in your custom activity, make sure your let the application to quit and that it. If you'll control the intent then you can pass the params via applicationArguments stringExtras key [1].
[0] http://code.qt.io/cgit/qt/qtbase.git/tree/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java#n137 [1] http://code.qt.io/cgit/qt/qtbase.git/tree/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java#n652
3 - Jun - 2015
Stanislav
Invoking it for the first time is not a problem, only minor QtActivity code modification is needed. But then when you switch to file manager and try to open another file - android will not quit the first instance of viewer, it will just restore it, with the first opened file. I was not able to open second file when the first one is already opened.
3 - Jun - 2015
BogDan Vatra
First and foremost, as I said many times, you should NOT change QtActivity, instead you should extend it.
If you want to make sure that your application quits you can listen for QGuiApplication::applicationStateChanged [0] and quit the application when state is Qt::ApplicationSuspended.
[0] https://doc-snapshots.qt.io/qt5-5.4/qguiapplication.html#applicationStateChanged
3 - Jun - 2015
Stanislav
Sorry, I meant QtNative, not QtActivity. I would contribute the fix upstream if I will be able to make it work as I wanted.
I see your point about quitting, but it will affect the startup time on every new intent.
3 - Jun - 2015
BogDan Vatra
If your fix doesn't break anything else than you can upstream it.
If you want to keep your application running than you need to send the params using JNI.
18 - Jun - 2015
Federico
I've solved this problem in another way. Instead of restarting the application I have set the activity with property android:launchMode="singleTask"; this way the same, opened, instance is called when an intent is launched. I've also extended the QtActivity with the method "onNewIntent(Intent i)" in which I call "setIntent(i): this way the subsequent call to the extras() method will return the parameters contained in the new Intent, instead of the original intent which started the app. I've only ONE last issue: notify Qt of the new Intent. Guess this episode is what I'm looking for. Thanks BogDan! :)
3 - Jun - 2015
foruok
Hi,BogDan,here is the Chinese version:http://blog.csdn.net/foruok/article/details/46323129
3 - Jun - 2015
BogDan Vatra
Thanks.
3 - Jun - 2015
jiangcaiyang
Hi, BogDan Vatra. In my practice, I tend to register natives by using my custom functions and let it run before QQmlApplicationEngine gets started rather than "JNI_OnLoad( )" function, and it also works. In fact I'm going to encapsulate some open authentication platforms into my plugin and expose some informations to QML environments, registering methods within "QQmlExtensionPlugin::registerTypes( )" function seems a good practice.
3 - Jun - 2015
BogDan Vatra
It doesn't matter where you register them as long as you'll register them before the java part calls them.
JNI_OnLoad( ) gives us everything we need to register them (JavaVM pointer), that's why I'm using it and that is the reason why I'm telling people to use it.
14 - Jun - 2015
jiangcaiyang
How is using AsyncTask on Java side in replace of Runnable?
16 - Jun - 2015
BogDan Vatra
HUH ?!?! The idea is to run some code on Android UI thread. AsyncTask is used for something else. It's usefull when you want to do some heavy work in background and publish the results on Android UI thread. I don't see how AsyncTask is better than the humble Runnable and Activity.runOnUiThread for what we need... Of course AsyncTask might be useful for other examples, but for the example in the article is useless.
16 - Jun - 2015
jiangcaiyang
What about the solution on creating instance in C++ by JNI whereas it affiances to Android UI thread. subclass Activity and write a new clause is a solution, but I don't want to disturb QtActivity.
16 - Jun - 2015
BogDan Vatra
I think I fail to follow you :) .
You want to create a new Activity from JNI? If the activity is not declared in your manifest file, you can't create a new one at runtime.
Also the main Activity and Qt needs separate threads because they have their own event loops and they can't leave togheter into a single thread (check http://www.kdab.com/qt-on-android-episode-1 for more info on this matter).
15 - Jul - 2015
daniel
Hello ,I have a "already working " Qt application ,an instant messaging one ,it uses QXmpp and I am porting it to android.
The problem I am facing is that when the application is inactive for some time it is killed by the system .I tried to remedy on this by writting an android service using (asmack) and handling received messages and opening my application again and pass in the received messages .
This is starting to look like a bad design as I have to log off from the server and let the service take control every time the user minimises the app or when the screen goes black.
I was wondering if there is a better way of hanling "long running operations on android (Qt) or should I just offload the Instant messaging details to the background service.
Thank you.
23 - Oct - 2015
BogDan Vatra
Using a service is the Android way to do long term background operations. So, IMHO you should create a service which processes the data, post notifications and start the Qt UI using an Intent if needed.
23 - Oct - 2015
Tilly Pavone
Hello Bodgan, thank you for your job (yes, I'm really impressed!).
I have a question on how the Android UI thread and the Qt thread relate to the process running the Android app, in particular when it comes to acquire a WakeLock.
While developing a GPS app, I ended up writing an example that shows how, if holding a PARTIAL_WAKE_LOCK, the QtActivity keeps working also in the background, as expected, while the Qt thread goes to sleep.
How is that possible? Do Android WakeLocks, as it seems, get effect at a thread level?
Here is an excerpt from my sample:
*** java ***
public class UtyActivity extends QtActivity { private PowerManager.WakeLock m_wakelock; Timer m_timer;
========== C++ ==========
// ---> THIS BLOCKS WHILE APP IN THE BACKGROUND void MainPanel::timerEvent(QTimerEvent *) { // same http get as java: no request is delivered while in the background
23 - Oct - 2015
BogDan Vatra
By default the main QEventLoop is freezed when it goes in background. We need to do it because the surface that Qt needs to paint to is not available anymore and if you try to paint something your application might crash.
There are two ways to keep going on Qt world after it goes in background: - create a new thread , call QThread::exec() then create all the timers and other QObjects that you need the in that thread. - don't freeze the Qt main loop. You need to edit AndroidManifest.xml and set "android.app.background_running" to "true", but you must make sure you don't draw anything when the application is backgrounded.
23 - Oct - 2015
Tilly Pavone
It works great! If I need to draw anything when the app may be running in the background, I just check if "isVisible()" is "false" and if so, store the data in memory and do the job in the "showEvent()" function. Thanks a lot!
26 - Oct - 2016
Sudha
Hello, I need to control device orientation, both iOS and Android from qt5.6 and c++. What's the best way
12 - Nov - 2016
seniorivn
Hi, it is the best articles about qt on android that i've found. But it's didn't answer on my questions :( Here is my problem: 1) i'm trying to write soft keyboard using qt. 2) android use android.view.View to draw keyboard 3) so i need to use View to draw qt interface, but standard way to draw qt on android is QtActivity. 4) I've found a way to do that, by extendinng View, with custom onDraw() that will get bitmap of qt interface by grabWindow() and put it into view.
5) Do you think it's normal solution? Does anyone have any better ideas?
Thanks anyway, you did a great job.
14 - Nov - 2016
BogDan Vatra
I think you're wrong, "standard" way to draw qt is by using a [Qt]SurfaceView not the QtActivity. QtActivity is used just to forward all the events to Qt world. I never tried to write an android keyboard so, I don't know all the details, but using grabWindow() doesn't seem the best solution... If you can add a single SurfaceView to android.view.View that (apparently) is needed to draw the keyboard, then Qt can use it to draw its stuff too.
2 - Dec - 2016
Valentin OFFNER
Hi,
I have a little question about this article and signals. Shoud I always use the QMetaObject::InvokeMethod or can I call the Q_EMIT directly?
OR
19 - Dec - 2016
BogDan Vatra
I don't think Q_EMIT (which is the same thing as emit) will delegate the call to callee thread.
static void notifyXXXX(JNIEnv env, jobject, jlong id) { JData const mp = mDataObjects[id]; jData*const mp = mDataObjects[id]; if (mp) QMetaObject::invokeMethod(mp, “callTheSignalMethodDirectly”); }
16 - Dec - 2016
Fabrice Mousset
Hi BogDan,
thanks very much to sharing your knowledge about Qt/Android with us. I have read your Android/Qt article and watched some of your presentation at Qt Con. I have developed a quit simple Qt/Android application, which is using the Camera and some other features of my Smartphone. The application is working well when I use "Bundle Qt libraries in APK" but when I try "Use Ministro service to install Qt". Nothing works!
My Application uses some JNI functions, so I have create JNI_OnLoad() function as your show in your presentation at Qt Con 2016. But when I start it I've got following error:
This only happens when I generate the APK with "Use Ministro service to install Qt". Why?
Can you help me?
19 - Dec - 2016
BogDan Vatra
I'll need more info on this matter: e.g. what Qt version are you using?
19 - Dec - 2016
Fabrice Mousset
Here my configuration: - Qt 5.7.1 / Android armeabi-v7 - Android NDK r13b - Qt Creator v4.2.0 - Workstation: Windows 7 pro/64 bit - Android Lollipop - Android API level used: 21
19 - Dec - 2016
BogDan Vatra
Qt 5.7.x wasn't yet uploaded to Ministro repos, I'm still testing it ... I hope I'll upload it in a few days.
19 - Dec - 2016
Fabrice Mousset
Thanks for your answer, Do you have a deadline for this update? Before end of this week (before Xmas) or next year?
I have to inform customer about software availability.
20 - Dec - 2016
Fabrice Mousset
Hi BogDan,
in the meantime, I found a quick&dirty solution which "WorksForMe(tm)". To save data space on APK transfer to the device, I have create a full APK. On this APK I have extract all Qt specific stuff. Those files are transferred into device internal storage (backup copy). For every next APK release, I remove Qt stuff from APK. Send this small APK (less than 2 MB) to the device, on device side a insert remove Qt stuff to complete the APK then start a "normal" APK install on Android.
It is very dirty but it works for my needs.
Perhaps, you can provide an "extended" version of Ministro, with a kind of "Off-Line" or "Static" mode in which it is possible to setup a device with specified Qt version, for example downloaded from Qt repository. The transfer this version on device. So all APK generated for this device do not need any Qt dll/qml/whatever. This can be useful for a business solution. I don't think this will be so much changes on Ministro side. What do you think about this?
Regards
Fabrice
20 - Dec - 2016
BogDan Vatra
I just uploaded Qt 5.7 a few hours ago, it should already be visible.
18 - Dec - 2016
Ahmed Arif
just wandering isn't possible to make the "static void onReceiveNativeMounted" inside Mainwindow's class so it will be "static void Mainwindow::onReceiveNativeMounted" and then call ui->plainTextEdit.... directly from it ? this could be more elegant i guess ?... imagine if we have many many functions to pass with it data from java to C++, making statics functions along side with the same number of slots will rise the ability of maintaining the code ...
19 - Dec - 2016
BogDan Vatra
Calling a static method (from your class) or a static function it doesn't change the thread from it's called. So "Mainwindow::onReceiveNativeMounted" is called from Android UI thread no matter what and trying to call "ui->plainTextEdit" directly (though you can't use it directly because "ui" is not accessible from a static method) will not automatically switch the threads and it will lead to a crash (sooner or later).
20 - Oct - 2020
Wenderson Costa
Good explanation, but you would a actualization of this.
19 - Jan - 2021
BogDan Vatra
+90% of this material should still apply today. I don't know when I'll have time to update it.