-
This section outlines the necessary steps to write a simple native "Hello World!" JNI-enabled Android application.
-
This section assumes you have read and followed the steps in Compiling C/C++ code directly from Android Studio
-
Create a new Android Studio project. To do so, select
File -> New -> New Project
.-
Give the application a name. For example:
HelloWorldJni
. -
Set the "Company Domain" entry to be
example.com
, so that the application package name becomescom.example.helloworldjni
. -
Select
Blank Activity
in the "Add an activity to Mobile" step. This activity will become your application's "main" activity, and is appropriately namedMainActivity.java
with layout fileactivity_main.xml
.
-
-
In
activity_main.xml
, change the defaultTextView
to have anandroid:id
with no defaultandroid:text
. The layout file should look like the following:<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <TextView android:id="@+id/text_view" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout>
-
In
MainActivity.java
, for the sake of simplicity, remove any override methods except foronCreate(Bundle)
. In astatic
block inside the class (usually at the top, by convention), add the lineSystem.loadLibrary("helloWorldJni")
. This will link the native module with namehelloWorldJni
with theMainActivity
class at runtime, which we will write later. YourMainActivity
class should look like the following:public class MainActivity extends AppCompatActivity { static { System.loadLibrary("helloWorldJni"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
-
Now, in the
onCreate(Bundle)
method, initialize theTextView
from the layout file. We will then set aString
onto it, which will be returned by a native JNI method. To do this, declare anative
method (usually at the bottom, also by convention) calledgetNativeString()
. This is needed since the compiler still needs to know that such method exists. Then, on theTextView
, call its member methodsetText(String)
, and pass in the return value ofgetNativeString()
. YourMainActivity
class should look like the following:public class MainActivity extends AppCompatActivity { static { System.loadLibrary("helloWorldJni"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView textView = (TextView) findViewById(R.id.text_view); textView.setText(getNativeString()); } private native String getNativeString(); }
-
It's time to write the native method
getNativeString()
you called above. To do this, create a C++ source file namedHelloWorldJni.cpp
undersrc/main/java/jni
with the following contents.#include <jni.h> extern "C" JNIEXPORT jstring JNICALL Java_com_example_helloworldjni_MainActivity_getNativeString( JNIEnv *env, jobject obj) { return env->NewStringUTF("Hello World! From native code!"); }
There are several points to note about this file:
-
extern "C"
: Statement to make C++ function names have C linkage. To support function overloading, C++ compilers mangle function names, which means C++ function names are not the same as in C. Withoutextern "C"
, your native functions' signatures will not match their declarations in Java (at runtime). Long story short, you need this statement for every method if you are writing native C++ and not C. -
Method signature: JNI functions need to be named in the following manner:
JNIEXPORT <RETURN_TYPE> JNICALL Java_<PACKAGE_NAME>_<JAVA_CLASS>_<METHOD_NAME>( JNIEnv *env, jobject obj, <METHOD_PARAMETERS>...) { ... }
-
JNIEXPORT
: Contains compiler directives required to ensure the function is exported properly. -
<RETURN_TYPE>
: Return type of the JNI method, usually a native version of a Java type. For example, in the method you just wrote above, you are returning ajstring
, which is the native equivalent ofString
in Java. -
JNICALL
: Contains compiler directives required to ensure the function is treated with the proper JNI calling convention. -
<JAVA_CLASS>
: The connecting Java class this function is tied to. In our example, this would beMainActivity
, since that's the Java class that will use this function. -
<PACKAGE_NAME>
: The package name where the previously defined<JAVA_CLASS>
resides in. Replace dots (.
) with underscores (_
). -
<METHOD_NAME>
: This name should be the same as the one you declare inside the connecting<JAVA_CLASS>
. In our example, we declared thenative
methodgetNativeString()
. In this case,<METHOD_NAME>
should also begetNativeString()
. -
JNIEnv *env
: Pointer to a structure (a function table, to be exact) storing all JNI helper function pointers, including the one we call in our example,NewStringUTF(string)
. To be able to use these functions, you will need to#include <jni.h>
. -
jobject obj
: Java object corresponding to the connecting<JAVA_CLASS>
. -
<METHOD_PARAMETERS>...
: Comma delimited list of input arguments the native method is supposed to take. In our example, we do not have any input arguments for ourgetNativeString()
function, and is hence left blank.
-
-
-
Ensure that your
Android.mk
file has correct entries forLOCAL_MODULE
andLOCAL_SRC_FILES
:LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := helloWorldJni LOCAL_SRC_FILES := HelloWorldJni.cpp include $(BUILD_SHARED_LIBRARY)
And that's it! You should be able to run your app onto a device/emulator as usual.
do you know how to arrange my native methods to group all together inside my java classes?
Im already tried regions, section rule, but android studio arrangement dont understand native keyword