Qt on Android Episode 7)

Source: Internet
Author: User

Qt on Android Episode 7)

 

In the last two articles on Qt on Android, I learned how to use the basic JNI and how to use external IDE to manage the Java part of the Qt application. In this chapter, we will continue to focus on how to expand the Java part of our Qt on Android Application and how to securely use JNI for interaction.

In this part, we are going to implement an SD card listener. This is a useful example for applications that want to use SD cards to store data, because if the application does not close the opened file immediately after receiving the notification, it will be killed by the Android system.

As we can see in Episode 5, calling Java methods in C/C ++ code or Calling C/C ++ methods in Java code is quite simple, but not all cases work. Why?

To understand why it doesn't work, first we need to understand the Qt on Android architecture.

Qt on Android Architecture

A few words about the architecture diagram:

The blue rectangle on the left represents the green rectangle on the right of the Android UI thread represents the main thread of Qt (where the Qt main event runs cyclically ). For more information about Android UI and Qt threads, see Episode 1. The black rectangle on the top is the Java section in your application. As you can see, most of them run in the Android UI thread. The only situation where Java runs in the Qt thread is that we call it from the C/C ++ code in the Qt thread (this is where most JNI calls occur ). The black rectangle at the bottom is the C/C ++ (Qt) part of your application. As you can see, most of it runs in the Qt thread. The only situation where the C/C ++ part runs on the Android UI thread is that the Java part of the Android UI thread calls it (most Java callbacks occur here ).

Okay ...... So what is the problem? Well, the problem is,Some Android APIs must be called in the Android UI thread.When we call the Java method in C/C ++ codeQt thread. That is to say, we need a way to run the code.In the Android UI thread rather than the Qt thread. To implement such a call from the C/C ++ Qt thread to the Java Android UI thread, we need three steps:

From C/C ++ Qt threadTo call a Java method. This method will Qt threadSo we need a way to access the Android API in the Android UI thread. Our Java method uses Activity. runOnUiThread to deliver a Runnable Android UI thread. Android event Loops Android UI threadRun this Runnable. The Runnable object accesses the Android API in the Android UI thread.

Similar problems exist when Java code calls C/C ++ functions, Because Java willAndroid UI threadSo we need a method inQt threadTo pass those notifications. There are also three steps:

Call a C/C ++ function in the Android UI thread. Use QMetaObject: invokeMethod Qt event LoopShip a method call. Qt time loops in Qt thread. Java Extension

Before you start, you 'd better read Episode 6, because you need it to easily manage Java files.

The first step is to create a custom Activity, inherit from QtActivity, and define a method for shipping our Runnable.

// src/com/kdab/training/MyActivity.javapackage com.kdab.training;import org.qtproject.qt5.android.bindings.QtActivity;public class MyActivity extends QtActivity{    // this method is called by C++ to register the BroadcastReceiver.    public void registerBroadcastReceiver() {        // Qt is running on a different thread than Android.        // In order to register the receiver we need to execute it in the Android UI thread        runOnUiThread(new RegisterReceiverRunnable(this));    }}

Next, we need to change the default Activity of AndroidManifest. xml from:

Changed to this:

This is done to ensure that the application will be instantiated at startup.Custom Activity.

Step 3: define ourRegisterReceiverRunnableClass: The run method of this class willAndroid UI thread. InRunIn the method, we register ourSDCardReceiverListener.

// src/com/kdab/training/RegisterReceiverRunnable.javapackage com.kdab.training;import android.app.Activity;import android.content.Intent;import android.content.IntentFilter;public class RegisterReceiverRunnable implements Runnable{    private Activity m_activity;    public RegisterReceiverRunnable(Activity activity) {        m_activity = activity;    }    // this method is called on Android Ui Thread    @Override    public void run() {        IntentFilter filter = new IntentFilter();        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(new SDCardReceiver(), filter);    }}

Let's take a look at the class:

// src/com/kdab/training/SDCardReceiver.javapackage com.kdab.training;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;public class SDCardReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        // call the native method when it receives a new notificatio**SDCardReceiver**n        if (intent.getAction().equals(Intent.ACTION_MEDIA_MOUNTED))            NativeFunctions.onReceiveNativeMounted();        else if (intent.getAction().equals(Intent.ACTION_MEDIA_UNMOUNTED))            NativeFunctions.onReceiveNativeUnmounted();    }}

SDCardReceiverThe onReceive method is rewritten, And then it uses the declared native method to send notifications to C/C ++ code.

The last step is to declareSDCardReceiverNative functions used in:

// src/com/kdab/training/NativeFunctions.javapackage com.kdab.training;public class NativeFunctions {    // define the native function    // these functions are called by the BroadcastReceiver object    // when it receives a new notification    public static native void onReceiveNativeMounted();    public static native void onReceiveNativeUnmounted();}
Java Architecture

Let's take a look at the call summary in the Java section by combining the structure diagram:

C/C ++ Extension

Now let's take a look at how to expand the C/C ++ section. To demonstrate how to do this, I use a simple QWidget-based application.

The first thing we need to do is callRegisterBroadcastReceiverMethod.

// main.cpp#include mainwindow.h#include 
  
   #include 
   
    int main(int argc, char *argv[]){    QApplication a(argc, argv);    // call registerBroadcastReceiver to register the broadcast receiver    QtAndroid::androidActivity().callMethod
    
     (registerBroadcastReceiver, ()V);    MainWindow::instance().show();    return a.exec();}
    
   
  

!

// native.cpp#include 
  
   #include 
   
    #include mainwindow.h// define our native static functions// these are the functions that Java part will call directly from Android UI threadstatic void onReceiveNativeMounted(JNIEnv * /*env*/, jobject /*obj*/){    // call MainWindow::onReceiveMounted from Qt thread    QMetaObject::invokeMethod(&MainWindow::instance(), onReceiveMounted                              , Qt::QueuedConnection);}static void onReceiveNativeUnmounted(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 killed    QMetaObject::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
    
     (&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 methods    if (env->RegisterNatives(javaClass, methods,                          sizeof(methods) / sizeof(methods[0])) < 0) {      return JNI_ERR;    }    return JNI_VERSION_1_6;}
    
   
  

InNative. cppWe have registered Native functions. In our static native functions, we use QMetaObject: invokeMethod to deliver a slot call to the Qt thread.

// mainwindow.h#ifndef MAINWINDOW_H#define MAINWINDOW_H#include 
  
   namespace Ui {class MainWindow;}class MainWindow : public QMainWindow{    Q_OBJECTpublic:    static MainWindow &instance(QWidget *parent = 0);public slots:    void onReceiveMounted();    void onReceiveUnmounted();private:    explicit MainWindow(QWidget *parent = 0);    ~MainWindow();private:    Ui::MainWindow *ui;};#endif // MAINWINDOW_H// mainwindow.cpp#include mainwindow.h#include ui_mainwindow.hMainWindow::MainWindow(QWidget *parent) :    QMainWindow(parent),    ui(new Ui::MainWindow){    ui->setupUi(this);}MainWindow::~MainWindow(){    delete ui;}MainWindow &MainWindow::instance(QWidget *parent){    static MainWindow mainWindow(parent);    return mainWindow;}// Step 6// Callback in Qt threadvoid MainWindow::onReceiveMounted(){    ui->plainTextEdit->appendPlainText(QLatin1String(MEDIA_MOUNTED));}void MainWindow::onReceiveUnmounted(){    ui->plainTextEdit->appendPlainText(QLatin1String(MEDIA_UNMOUNTED));}
  

MainWindowClass only adds some text to the plainText control when receiving the notification. Calling these functions in the Android thread can greatly damage the robustness of our application-it may cause crashes or unpredictable behavior, so they must be called in the Qt thread.

C/C ++ Architecture

In our architecture diagram, the calling of C/C ++ is as follows:

Structure of mutual calls between Java & C/C ++

The following figure shows the architecture of all calls between C/C ++ and Java:

Download the sample source code: Click Here.

Thank you for taking the time to read this article.

(Note: BogDan Vatra is really super nice. It provides so many diagrams that it is too clear to call each other between Java <-> C ++ .)

My articles on Qt on Android Episode:

Qt on Android Episode 1 Qt on Android Episode 2 Qt on Android Episode 3 Qt on Android Episode 4 Qt on Android Episode 5 Qt on Android Episode 6

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.