Android – calling Java method from jni (C/C++) even Qt

From C++ to java

Let us first create a method in Java which we will call from C++:


public class MyJavaClass

{

  private final Activity m_MainActivity;

  public MyJavaClass(final Activity MainActivity)

  {

    // Save the main activity pointer

    m_MainActivity = MainActivity;

  }

  public void TestCallMe(int param1, String param2)
  {

  // Do something here

  }

}

Now we want to call TestCallMe which has two params, one integer and one string. Using Qt class is possible to call this method from C++ side as follow:

int Param1;

QString Param2;

MyJavaClass.callMethod<void>("TestCallMe",

"(ILjava/lang/String;)V",

Param1,

QAndroidJniObject::fromString(Param2).object<jstring>()

);

This here is

“(ILjava/lang/String;)V”,

basically method definition.

So in addition to method name, the params accepted using the JNI format specification.

Also you need to convert the data type in specific way before passing to Java method.

Native variable like integer doesn’t need specific conversion but some more complex element, like QString as in example, require a conversion to the JNI native format jstring. Anyway reading the JNI types map it will be possible to exchange the majority of common format available.

Advertisements

flask

Flask

Very easy to start

sudo pip install flask
from flask import Flask, url_for

app = Flask(__name__)

@app.route('/')

def api_root():

  return 'Welcome'

@app.route('/articles')

def api_articles():

  return 'List of ' + url_for('api_articles')

@app.route('/articles/<articleid>')

def api_article(articleid):

  return 'You are reading ' + articleid

if __name__ == '__main__':

  app.run()

You can use curl to make the requests using:

curl http://127.0.0.1:5000/

$ export FLASK_APP=hello.py

$ flask run

* Running on http://127.0.0.1:5000/

Visible externally

flask run --host=0.0.0.0

$ export FLASK_DEBUG=1

$ flask run

flask session

Android app with NDK

Follow guidelines to setup your Android Studio. You need to download Android NDK with  “Tools” > “Android” > “SDK Manager”, then select the tab “SDK Tools” and check “Android NDK” if it is not checked.

Now while creating new project notice the checkbox for cpp/JNI compatibility in the project.

For me Android Studio was creating native-lib.cpp cpp file and also adding it to CMakeLists.txt. On compilation you will get an .so file named libnative-lib.so.

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myservice_MainService_getString( JNIEnv* env,
                                                            jobject thiz )
{
    return env->NewStringUTF(" Hello from JNI !  This Worked");
}

 

I forgot extern “C” that is why I highlighted it here. In c file things are little different.

My MainService.java file:

 

package com.example.myservice;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class MainService extends Service {

    private static final String TAG = "My_Service";
    private boolean isRunning  = false;

    public MainService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "Service onBind");
        return null;
    }
    @Override
    public void onCreate() {
        Log.i(TAG, "Service onCreate");

        isRunning = true;
    }

    public native String  getString();

    static {
        System.loadLibrary("native-lib");
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        Log.i(TAG, "Service onStartCommand");

        //Creating new thread for my service
        //Always write your long running tasks in a separate thread, to avoid ANR
        new Thread(new Runnable() {
            @Override
            public void run() {

                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {
                    }

                    if(isRunning){
                        Log.i(TAG, "Service running");
                        Log.i(TAG, getString());
                    }
                }

                //Stop service once it finishes its task
                stopSelf();
            }
        }).start();

        return Service.START_STICKY;
    }

    @Override
    public void onDestroy() {

        isRunning = false;

        Log.i(TAG, "Service onDestroy");
    }

}

 

Highlighted parts are JNI related.

 

MainActivity.java file:

package com.example.myservice;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.content.Context;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "My_Service";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent startServiceIntent = new Intent(this, MainService.class);
        Log.i(TAG, "Starting service from activity");

        startService(startServiceIntent);
        finish();
    }
}

 

MyBroadcastReceiver.java:

package com.example.myservice;


import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent startServiceIntent = new Intent(context, MainService.class);
        context.startService(startServiceIntent);
    }
}

 

AndroidManifest.xml:

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myservice">

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <service
            android:name=".MainService"
            android:enabled="true"
            android:exported="true" />

        <receiver android:name=".MyBroadcastReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

        <activity
            android:name=".MainActivity"
            android:theme="@android:style/Theme.Translucent.NoTitleBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

 

My problem statement was to make a service which should be started at bootup and I also didn’t want any UI.

For first timer it is not suggested to use underscore (_) anywhere in package name or class name of java. You need to use _1 in cpp file for naming the function but if it doesn’t work out you will be confused. So keep it simple for the first time.

My experience was I already had underscore(_) in Java class name and also in package name. I did name the cpp function name correctly but forgot to use extern “C” banged my head for almost an hour and then created a separate project without underscore(_) only to find out the problem was extern “C” 

 

qt on ubuntu core

Building Qt for a given device requires a toolchain and a sysroot. Additionally, some devices require vendor-specific adaptation code for EGL and OpenGL ES 2.0 support. This is not relevant for non-accelerated platforms, for example the ones using the LinuxFB plugin, which is meant for software-based rendering only. This means Qt Quick 2 is not functional in such a setup as it depends on OpenGL for rendering.

The directory qtbase/mkspecs/devices contains configuration and graphics adaptation code for a number of devices. For example, the linux-rasp-pi2-g++ mkspec contains build settings such as the optimal compiler and linker flags for the Raspberry Pi 2 device. The mkspec also contains information about either an implementation of the eglfs hooks (vendor-specific adaptation code), or a reference to the suitable eglfs device integration plugin. The device is selected through the configure tool’s -device parameter. The name that follows after this argument must, at least partially, match one of the subdirectories under devices.

There are currently some limitations to note with Qt 5 on the Raspberry Pi 2. Both the xcb and eglfs back ends for Qt are available.

The xcb back end supports widget applications and supports the X11 window manager running on the desktop so Qt-based applications will run in a window, which can be moved or resized. However, the OpenGL implementation on the Raspberry Pi is not compatible with xcb or X11, so applications that use OpenGL will not work with the xcb back end.

The eglfs back end supports widget-based and QML applications, and uses OpenGL, but will run in full screen mode and not be aware of any window manager.

The linuxfb back end, which directly uses the Linux video frame buffer, should also be available and work, although you will probably need to set some environment variables in order for the keyboard and mouse devices to work correctly.

You can select which back end to use at run time using command line options, i.e. “-platform xcb” or “-platform eglfs”. Normally, the default is to use the eglfs back end.

source package list

o summarize, you need to open the /etc/apt/sources.list file with a text editor and then add arch= between deb and the URL for each line. This is the example provided on the page:

Running qt app with fbv or egl

 


root@myDesk:/usr/local/bin# ./application -platform egl

Available platform plugins are: eglfs, kms, linuxfb, minimal, minimalegl, offscreen, xcb.

 

QT_QPA_GENERIC_PLUGINS=evdevmouse
QT_QPA_EVDEV_MOUSE_PARAMETERS=/dev/input/mouse1
QT_QPA_FB_HIDECURSOR=0

 

/dev/input/
event3 is mouse
event4 is kbd or read via by-id/usb

 

mice is also mouse and mouse1 is also mouse

 

/dev/input/mice

check where the mouse is

 

keyboard is working as expected no mouse though

 
./application -platform linuxfb:fb=/dev/fb0 -plugin evdevkeyboard:/dev/input/event4 -plugin evdevmouse:/dev/input/event3@

 
./application -platform linuxfb:fb=/dev/fb0 -plugin evdevkeyboard:/dev/input/event4 -plugin evdevmouse:/dev/input/event3@


./application -platform linuxfb -plugin EvdevMouse -plugin EvdevKeyboard > ~/application.txt

./application -platform linuxfb -plugin EvdevMouse -plugin EvdevKeyboard > ~/application.txt

fb=/dev/fb0

 

 

drawing is very slow on /dev/fb0
each time a new frame is drawn We can see from top to bottom it is being drawn

 

Qt for embedded linux

http://www.360doc.com/content/14/1215/10/18578054_433033534.shtml

 

 


./configure -v -debug -opensource -confirm-license -no-use-gold-linker -nomake examples -nomake tests -nomake tools -no-cups -no-pch -no-linuxfb -skip qtquick1 -skip declarative -skip multimedia -opengl es2 -no-eglfs -system-xcb -sysroot $SUNXI_SYSROOT -device linux-sunxi-g++ -device-option CROSS_COMPILE=/home/name/gcc-linaro-arm-linux-gnueabihf-4.8-2014.04_linux/bin/arm-linux-gnueabihf- -prefix /opt/qt/sunxi