Necessitas/JNI: Difference between revisions
Bog dan ro (talk | contribs) (Created page with "== Using JNI == The Java Native Interface (JNI) is a programming framework that enables Java code running in a Java Virtual Machine (JVM) to call, and to be called by, nati...") |
m (Fix style. It is not code.) |
||
(One intermediate revision by one other user not shown) | |||
Line 1: | Line 1: | ||
== Using JNI == | == Using JNI == | ||
The Java Native Interface (JNI) is a programming framework that enables Java code running in a Java Virtual Machine (JVM) to call, and to be called by, native applications (programs specific to a hardware and operating system platform) and libraries written in other languages such as C, C++ and assembly ([http://en.wikipedia.org/wiki/Java_Native_Interface quote from wikipedia]) . | |||
In this page we are not going to teach you JNI, Java or C/C++, instead we'll try to explain how to use JNI from a Qt application. If you want to lean JNI, then you should start with by reading the following documentations [http://developer.android.com/guide/practices/jni.html Google's JNI page] [http://android-developers.blogspot.ro/2011/11/jni-local-reference-changes-in-ics.html Google's blogs]. | |||
First this you need to know is that you don't need to load your application manually (e.g. don't use '''System.loadLibrary'''), the second thing you also need to know is that your Qt application is running on a different thread than main Android Activity, so if you want to use Activity you should use [http://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable) runOnUiThread]. | |||
In this example we'll create a simple Audio player using Android's [http://developer.android.com/reference/android/media/MediaPlayer.html MediaPlayer]. [http://dl.dropbox.com/u/67435935/untitled7.tar.bz2 Here] you can download the full example. | |||
=== The .pro file === | === The .pro file === |
Latest revision as of 14:26, 26 January 2013
Using JNI
The Java Native Interface (JNI) is a programming framework that enables Java code running in a Java Virtual Machine (JVM) to call, and to be called by, native applications (programs specific to a hardware and operating system platform) and libraries written in other languages such as C, C++ and assembly (quote from wikipedia) .
In this page we are not going to teach you JNI, Java or C/C++, instead we'll try to explain how to use JNI from a Qt application. If you want to lean JNI, then you should start with by reading the following documentations Google's JNI page Google's blogs.
First this you need to know is that you don't need to load your application manually (e.g. don't use System.loadLibrary), the second thing you also need to know is that your Qt application is running on a different thread than main Android Activity, so if you want to use Activity you should use runOnUiThread.
In this example we'll create a simple Audio player using Android's MediaPlayer. Here you can download the full example.
The .pro file
Edit your profile and add the following lines for Android platform.
android {
SOURCES += androidmediaplayer.cpp
HEADERS += androidmediaplayer.h
}
The QSimpleAudioPlayer.java file
Create QSimpleAudioPlayer.java to your project_root/android/src/org/kde/necessitas/origo/
package org.kde.necessitas.origo;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.util.Log;
public class QSimpleAudioPlayer {
MediaPlayer m_mediaPlayer;
public QSimpleAudioPlayer() {
m_mediaPlayer = new MediaPlayer();
}
boolean setUrl(String path)
{
m_mediaPlayer.reset();
try {
m_mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
m_mediaPlayer.setDataSource(path);
m_mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
return false;
}
Log.i("Qt", "addTwoNumbers returned "+addTwoNumbers(1,2));
return true;
}
boolean play()
{
try {
m_mediaPlayer.start();
}
catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
boolean pause()
{
try {
m_mediaPlayer.pause();
}
catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
boolean stop()
{
try {
m_mediaPlayer.stop();
}
catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
boolean release()
{
try {
m_mediaPlayer.release();
}
catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
public static native int addTwoNumbers(int a, int b);
}
the androidmediaplayer.cpp file
#include <QDebug>
#include "androidmediaplayer.h"
static JavaVM* s_javaVM = 0;
static jclass s_audioPlayerClassID = 0;
static jmethodID s_audioPlayerConstructorMethodID=0;
static jmethodID s_audioPlayerSetUrlMethodID=0;
static jmethodID s_audioPlayerPlayMethodID=0;
static jmethodID s_audioPlayerPauseMethodID=0;
static jmethodID s_audioPlayerStopMethodID=0;
static jmethodID s_audioPlayerReleaseMethodID=0;
SimpleAndroidMediaPlayer::SimpleAndroidMediaPlayer()
{
JNIEnv* env;
// Qt is running in a different thread than Java UI, so you always Java VM *MUST* be attached to current thread
if (s_javaVM->AttachCurrentThread(&env, NULL)<0)
{
qCritical()<<"AttachCurrentThread failed";
return;
}
// Create a new instance of QSimpleAudioPlayer
m_audioPlayerObject = env->NewGlobalRef(env->NewObject(s_audioPlayerClassID, s_audioPlayerConstructorMethodID));
if (!m_audioPlayerObject)
{
qCritical()<<"Can't create the player";
return;
}
// Don't forget to detach from current thread
s_javaVM->DetachCurrentThread();
}
SimpleAndroidMediaPlayer::~SimpleAndroidMediaPlayer()
{
if (!m_audioPlayerObject)
return;
JNIEnv* env;
if (s_javaVM->AttachCurrentThread(&env, NULL)<0)
{
qCritical()<<"AttachCurrentThread failed";
return;
}
if (!env->CallBooleanMethod(m_audioPlayerObject, s_audioPlayerReleaseMethodID))
qCritical()<<"Releasing media player object failed";
s_javaVM->DetachCurrentThread();
}
bool SimpleAndroidMediaPlayer::setUrl(const QString &url)
{
if (!m_audioPlayerObject)
return false;
JNIEnv* env;
if (s_javaVM->AttachCurrentThread(&env, NULL)<0)
{
qCritical()<<"AttachCurrentThread failed";
return false;
}
jstring str = env->NewString(reinterpret_cast<const jchar*>(url.constData()), url.length());
jboolean res = env->CallBooleanMethod(m_audioPlayerObject, s_audioPlayerSetUrlMethodID, str);
env->DeleteLocalRef(str);
s_javaVM->DetachCurrentThread();
return res;
}
bool SimpleAndroidMediaPlayer::play()
{
if (!m_audioPlayerObject)
return false;
JNIEnv* env;
if (s_javaVM->AttachCurrentThread(&env, NULL)<0)
{
qCritical()<<"AttachCurrentThread failed";
return false;
}
jboolean res = env->CallBooleanMethod(m_audioPlayerObject, s_audioPlayerPlayMethodID);
s_javaVM->DetachCurrentThread();
return res;
}
bool SimpleAndroidMediaPlayer::pause()
{
if (!m_audioPlayerObject)
return false;
JNIEnv* env;
if (s_javaVM->AttachCurrentThread(&env, NULL)<0)
{
qCritical()<<"AttachCurrentThread failed";
return false;
}
jboolean res = env->CallBooleanMethod(m_audioPlayerObject, s_audioPlayerPauseMethodID);
s_javaVM->DetachCurrentThread();
return res;
}
bool SimpleAndroidMediaPlayer::stop()
{
if (!m_audioPlayerObject)
return false;
JNIEnv* env;
if (s_javaVM->AttachCurrentThread(&env, NULL)<0)
{
qCritical()<<"AttachCurrentThread failed";
return false;
}
jboolean res = env->CallBooleanMethod(m_audioPlayerObject, s_audioPlayerStopMethodID);
s_javaVM->DetachCurrentThread();
return res;
}
// our native method, it is called by the java code above
static int addTwoNumbers(JNIEnv * /*env*/, jobject /*thiz*/,int a, int b)
{
return a+b;
}
static JNINativeMethod methods[] = {
{"addTwoNumbers", "(II)I", (void *)addTwoNumbers}
};
// this method is called immediately after the module is load
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
{
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
qCritical()<<"Can't get the enviroument";
return -1;
}
s_javaVM = vm;
// search for our class
jclass clazz=env->FindClass("org/kde/necessitas/origo/QSimpleAudioPlayer");
if (!clazz)
{
qCritical()<<"Can't find QSimpleAudioPlayer class";
return -1;
}
// keep a global reference to it
s_audioPlayerClassID = (jclass)env->NewGlobalRef(clazz);
// search for its contructor
s_audioPlayerConstructorMethodID = env->GetMethodID(s_audioPlayerClassID, "<init>", "()V");
if (!s_audioPlayerConstructorMethodID)
{
qCritical()<<"Can't find QSimpleAudioPlayer class contructor";
return -1;
}
// search for setUrl method
s_audioPlayerSetUrlMethodID = env->GetMethodID(s_audioPlayerClassID, "setUrl", "(Ljava/lang/String;)Z");
if (!s_audioPlayerSetUrlMethodID)
{
qCritical()<<"Can't find setUrl method";
return -1;
}
// search for play method
s_audioPlayerPlayMethodID = env->GetMethodID(s_audioPlayerClassID, "play", "()Z");
if (!s_audioPlayerPlayMethodID)
{
qCritical()<<"Can't find start method";
return -1;
}
// search for play method
s_audioPlayerPauseMethodID = env->GetMethodID(s_audioPlayerClassID, "pause", "()Z");
if (!s_audioPlayerPauseMethodID)
{
qCritical()<<"Can't find start method";
return -1;
}
// search for stop method
s_audioPlayerStopMethodID = env->GetMethodID(s_audioPlayerClassID, "stop", "()Z");
if (!s_audioPlayerStopMethodID)
{
qCritical()<<"Can't find stop method";
return -1;
}
// search for release method
s_audioPlayerReleaseMethodID = env->GetMethodID(s_audioPlayerClassID, "release", "()Z");
if (!s_audioPlayerReleaseMethodID)
{
qCritical()<<"Can't find release method";
return -1;
}
// register our native methods
if (env->RegisterNatives(s_audioPlayerClassID, methods, sizeof(methods) / sizeof(methods[0])) < 0)
{
qCritical()<<"RegisterNatives failed";
return -1;
}
qDebug()<<"Yahooo !";
return JNI_VERSION_1_6;
}
the androidmediaplayer.h file
#ifndef ANDROIDMEDIAPLAYER_H
#define ANDROIDMEDIAPLAYER_H
#include <jni.h>
class SimpleAndroidMediaPlayer
{
public:
SimpleAndroidMediaPlayer();
~SimpleAndroidMediaPlayer();
bool setUrl(const QString &url);
bool play();
bool pause();
bool stop();
private:
jobject m_audioPlayerObject;
};
#endif // ANDROIDMEDIAPLAYER_H