Using JNI in Openframeworks Android (Part III)

Using JNI in Openframeworks Android (Part III)

As mentioned in previous posts ( part I, part II), in this third tutorial we’ll explain how to call Java Android’s code from Openframeworks’ methods.

Eventually, we’ll want to call some of the functions provided by the Android SDK. For example, if we want to access, the keyboard, show a form or call in-app billing methods. When we find ourselves facing one of these situations, we will need to call Java code from our Openframeworks context.

This strategy might be very useful in order to generate applications which contain multiple “activities” and, consequently, multiple graphic interfaces or functionalities which are exclusive of the native Android context. Calling native code from Openframeworks can also be very useful for conceptually dividing our application between the OF’s graphically poweful side and more ‘classic’ functionalities such as log-ins, sign-ups, connections with google-play, maps, databases, … In the end, it allows us to access a bunch of very useful and easy to code functionalities from Java Android.

In the following example, we will use the ofxGui addon in order to create a button. In its click event, we’ll call a Java method which will open a new Java Android “Activity” with a simple login/e-mail form.

Let’s begin by editing the Java side. Let’s edit the file “OFActivity.java”, creating and adding the code which we will be calling in the C++ side.


</pre>
<pre>	public void openActivity(){

		 Log.v("OFActiviy", "openActivity method in java native class access via OF");

		Intent nextScreen = new Intent(getApplicationContext(), SecondScreenActivity.class);

        //Sending data to another Activity
        nextScreen.putExtra("name", "Default Name");
        nextScreen.putExtra("email", "Default eMail");

        startActivity(nextScreen);

	}

Expand code

In this way, we have already prepared the method ‘openActivity’ and we’ll be able to call it from the button created in OF.

In order to do so, we need to create, set and subsequently call the following instructions in OF. Let’s begin with the file “ofApp.h”. We’ll create:

<pre>		ofxPanel gui;
		ofxButton changeActivity;
		void changeActivityPressed();

		// variable needed for import value from java
		jclass javaClass;
		jobject javaObject;

		void setupJNI();

Expand code

Here we can see how we create the necessary objects to build the button and the button’s method with the ofxGUI addon. On the other hand, we create two “JNI” objects: jclass, where we save the class java, and jobject, which will be the Java object of the class.

On the other side, in the “ofApp.cpp”, and in order to encapsulate some code, we’ll create the ‘setupJNI()’ method. Here we’ll setup the “JNI” objects:

<pre>void ofApp::setupJNI(){

	JNIEnv *env = ofGetJNIEnv();

	//get OFActivity class from java
	jclass localClass = env->FindClass("cc/openframeworks/activitiesOF/OFActivity");
	javaClass = (jclass) env->NewGlobalRef(localClass);
	if (!javaClass) {
		ofLog() << "javaClass NOT found!" << endl;
	}else if(javaClass){
		ofLog() << "javaClass found!" << endl;
	}

	//get OFActiviy object from java
	javaObject = ofGetOFActivityObject();
	javaObject = (jobject) env->NewGlobalRef(javaObject);
	if (!javaObject) {
		ofLog() << "javaObject NOT found!" << endl;
	}else if(javaObject) {
		ofLog() << "javaObject found!" << endl;
	}
}

Expand code

In the previous piece of code, we must state the “path” where our Java class is located (OFAcvitity) in order to be able to call its object and run the methods afterwards.

We are now going to the OF’s button event where we’ll add the code below in order to call the method created in the Java side:

void ofApp::changeActivityPressed(){

	JNIEnv *env = ofGetJNIEnv();

	/**
	 * This calls the "openActivity" java-method, it's a "void" function without parameters.
	 *
	 */
	jmethodID javaReturnMethod = env->GetMethodID(javaClass,"openActivity","()V");
	if(!javaReturnMethod){
		ofLog() << "Method NOT found!" << endl;
	}else if(javaReturnMethod){
		ofLog() << "Method found!" << endl;
	}

	//This code execute Java method located in OFActivity.java
	env->CallVoidMethod(javaObject, javaReturnMethod);

	//Code that close actual OFActivity and give feedback to the activity change (coded in OFActivity.java)
	endApp();

}

Expand code

After introducing these lines, when we run the program we’ll see that, after pressing the button, our program will close the Openframeworks context, leaving it ready to reopening later. Meanwhile, over that context, we’ll see an Android “Activity” opening where we can introduce user and email.

We must pay attention to the ‘jmethodID‘ object, where we’ll assign the method named ‘openActivity‘. Its name must match with the name in the Java Android side. The third recieved parameter is extremely important as it indicates the kind of method which will be created and the parameters which will be sent to it (again, they must match!). In our case, where the method is ‘void’ and doesn’t have any parameters, the third parameter is ‘()V‘.

In order to know and manage this kind of codification for the method’s headers, we can look up the JNI documentation in the oracle’s website.

http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html

We attach an example project to the post in order to make testing and developing easier and to serve as a guide for your projects. The project has been tested in OF 0.8.3 and should be paced in the path:. …/examples/Android

Link to the project