Java code can pass parameters to native methods and receive the processing results returned. This recipe walks through how to pass parameters and receive returns in primitive types.
You should have built at least one Android application with native code before reading this recipe. If you haven't done so, please read the Writing a Hello NDK program recipe in Chapter 1, Hello NDK first.
The following steps create a sample Android application with native methods receiving input parameters from the Java code and returning the processing result back:
PassingPrimitive
. Set the package name as cookbook.chapter2
. Create an activity named PassingPrimitiveActivity
. Under this project, create a folder named jni
. Please refer to the Loading native libraries and registering native methods recipe in this chapter if you want more detailed instructions.primitive.c
under the jni
folder and implement the native methods. In our sample project, we implemented one native method for each of the eight primitive data types. Following is the code for jboolean
, jint
, and jdouble
. Please refer to the downloaded code for the complete list of methods:#include <jni.h> #include <android/log.h> JNIEXPORT jboolean JNICALL Java_cookbook_chapter2_PassingPrimitiveActivity_passBooleanReturnBoolean(JNIEnv *pEnv, jobject pObj, jboolean pBooleanP){ __android_log_print(ANDROID_LOG_INFO, "native", "%d in %d bytes", pBooleanP, sizeof(jboolean)); return (!pBooleanP); } JNIEXPORT jint JNICALL Java_cookbook_chapter2_PassingPrimitiveActivity_passIntReturnInt(JNIEnv *pEnv, jobject pObj, jint pIntP) { __android_log_print(ANDROID_LOG_INFO, "native", "%d in %d bytes", pIntP, sizeof(jint)); return pIntP + 1; } JNIEXPORT jdouble JNICALL Java_cookbook_chapter2_PassingPrimitiveActivity_passDoubleReturnDouble(JNIEnv *pEnv, jobject pObj, jdouble pDoubleP) { __android_log_print(ANDROID_LOG_INFO, "native", "%f in %d bytes", pDoubleP, sizeof(jdouble)); return pDoubleP + 0.5; }
PassingPrimitiveActivity.java
Java code, we add code to load the native library, declare the native methods, and call the native methods. Following is that part of the code. The "…
" indicates the part that is not shown. Please refer to the source file downloaded from the website for the complete code:@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_passing_primitive); StringBuilder strBuilder = new StringBuilder(); strBuilder.append("boolean: ").append(passBooleanReturnBoolean(false)).append(System.getProperty("line.separator")) ...... .append("double: ").append(passDoubleReturnDouble(11.11)).append(System.getProperty("line.separator")); TextView tv = (TextView) findViewById(R.id.display_res); tv.setText(strBuilder.toString()); } private native boolean passBooleanReturnBoolean(boolean p); private native byte passByteReturnByte(byte p); private native char passCharReturnChar(char p); private native short passShortReturnShort(short p); ...... static { System.loadLibrary("PassingPrimitive"); }
res/layout/activity_passing_primitive.xml
file according to step 8 of the Loading native libraries and registering native methods recipe of this chapter or the downloaded project code.Android.mk
under the jni
folder, and add the following content to it:LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := PassingPrimitive LOCAL_SRC_FILES := primitive.c LOCAL_LDLIBS := -llog include $(BUILD_SHARED_LIBRARY)
jni
folder, and type ndk-build
to build the native library PassingPrimitive
.logcat
output on it:$adb logcat -v time
The logcat output is as follows:
The code illustrates how to pass parameters and receive returns in primitive types from the native method. We created one method for each primitive type. In the native code, we printed the received value to logcat
, modified the value, and returned it back.
Java Type |
JNI Type |
Number of bytes |
Sign |
---|---|---|---|
|
1 |
unsigned | |
|
1 |
signed | |
|
2 |
unsigned | |
|
2 |
signed | |
|
4 |
signed | |
|
8 |
signed | |
|
4 |
- | |
|
8 |
- |
Note that both Java char
and JNI jchar
are two bytes, while the C/C++ char
type is only one byte long. In fact, C/C++ char
are interchangeable with jbyte
instead of jchar
in JNI programming.
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__);
ANDROID_LOG_INFO
is an enum
value defined in android/log.h
, which indicates that we're using the info-level logging. LOG_TAG
can be any strings, and __VA_ARGS__
is replaced by the parameters passed to the API, in a format similar to the
printf
method in C.
We must include the android/log.h
header in the native code to use the log functions:
#include <android/log.h>
Besides this, we'll need to include the NDK log library in the Android.mk
file in order to use the API:
LOCAL_LDLIBS := -llog
We will cover more details about Android logging API in Chapter 3, Build and Debug NDK Applications, while utilizing logging API for debugging purposes.