Better_Software_Header_MobileBetter_Software_Header_Web

Find what you need - explore our website and developer resources

Qt on Android Episode 7

How to access and use Android Java API using JNI in a safe way.


// src/com/kdab/training/MyActivity.java
package 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));
    }
}

<activity ...
        android:name="org.qtproject.qt5.android.bindings.QtActivity"
        ... >

<activity ...
        android:name="com.kdab.training.MyActivity"
        ... >

// src/com/kdab/training/RegisterReceiverRunnable.java
package 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);
    }
}

// src/com/kdab/training/SDCardReceiver.java
package 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 notification
        if (intent.getAction().equals(Intent.ACTION_MEDIA_MOUNTED))
            NativeFunctions.onReceiveNativeMounted();
        else if (intent.getAction().equals(Intent.ACTION_MEDIA_UNMOUNTED))
            NativeFunctions.onReceiveNativeUnmounted();
    }
}

// src/com/kdab/training/NativeFunctions.java
package 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();
}

// main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QtAndroid>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // call registerBroadcastReceiver to register the broadcast receiver
    QtAndroid::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 thread
static 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 loaded
JNIEXPORT 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 methods
    if (env->RegisterNatives(javaClass, methods,
                          sizeof(methods) / sizeof(methods[0])) < 0) {
      return JNI_ERR;
    }

    return JNI_VERSION_1_6;
}

// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    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.h"

MainWindow::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 thread
void MainWindow::onReceiveMounted()
{
    ui->plainTextEdit->appendPlainText(QLatin1String("MEDIA_MOUNTED"));
}

void MainWindow::onReceiveUnmounted()
{
    ui->plainTextEdit->appendPlainText(QLatin1String("MEDIA_UNMOUNTED"));
}

36 Comments

2 - Jun - 2015

Stanislav

3 - Jun - 2015

BogDan Vatra

3 - Jun - 2015

Stanislav

3 - Jun - 2015

BogDan Vatra

3 - Jun - 2015

Stanislav

3 - Jun - 2015

BogDan Vatra

18 - Jun - 2015

Federico

3 - Jun - 2015

foruok

3 - Jun - 2015

BogDan Vatra

3 - Jun - 2015

jiangcaiyang

3 - Jun - 2015

BogDan Vatra

14 - Jun - 2015

jiangcaiyang

16 - Jun - 2015

BogDan Vatra

16 - Jun - 2015

jiangcaiyang

16 - Jun - 2015

BogDan Vatra

15 - Jul - 2015

daniel

23 - Oct - 2015

BogDan Vatra

23 - Oct - 2015

Tilly Pavone

@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

...
...

23 - Oct - 2015

BogDan Vatra

23 - Oct - 2015

Tilly Pavone

26 - Oct - 2016

Sudha

12 - Nov - 2016

seniorivn

14 - Nov - 2016

BogDan Vatra

2 - Dec - 2016

Valentin OFFNER

static  void notifyXXXX(JNIEnv *env, jobject, jlong id)
{   
   {
     JData *const mp = mDataObjects[id];        jData*const mp = mDataObjects[id];
    if (mp) {     
           Q_EMIT mp->modelChanged(mp->model());
    }
}
static  void notifyXXXX(JNIEnv *env, jobject, jlong id)
{   
   {
     JData *const mp = mDataObjects[id];        jData*const mp = mDataObjects[id];
    if (mp) {     
           QMetaObject::invokeMethod(mp, "handleDataChanged");
    }
}

void JData::handleDataChanged(){
    Q_EMIT modelChanged(model());
}

19 - Dec - 2016

BogDan Vatra

16 - Dec - 2016

Fabrice Mousset

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)

19 - Dec - 2016

BogDan Vatra

19 - Dec - 2016

Fabrice Mousset

19 - Dec - 2016

BogDan Vatra

19 - Dec - 2016

Fabrice Mousset

20 - Dec - 2016

Fabrice Mousset

20 - Dec - 2016

BogDan Vatra

18 - Dec - 2016

Ahmed Arif

19 - Dec - 2016

BogDan Vatra

20 - Oct - 2020

Wenderson Costa

19 - Jan - 2021

BogDan Vatra