
Passing parameters and receiving returns in primitive types
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.
Getting ready
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.
How to do it…
The following steps create a sample Android application with native methods receiving input parameters from the Java code and returning the processing result back:
- Create a project named
PassingPrimitive
. Set the package name ascookbook.chapter2
. Create an activity namedPassingPrimitiveActivity
. Under this project, create a folder namedjni
. Please refer to the Loading native libraries and registering native methods recipe in this chapter if you want more detailed instructions. - Add a file named
primitive.c
under thejni
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 forjboolean
,jint
, andjdouble
. 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; }
- In the
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"); }
- Modify the
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. - Create a file named
Android.mk
under thejni
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)
- Start a terminal, go to the
jni
folder, and typendk-build
to build the native libraryPassingPrimitive
. - In Eclipse, select Window | Show View | LogCat to show the logcat console. Alternatively, start a terminal and enter the following command in your terminal to show
logcat
output on it:$adb logcat -v time
- Run the project on an Android device or emulator. You should see something similar to the following screenshot:
The logcat output is as follows:
How it works…
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.
- JNI primitive type and Java primitive type mapping: The primitive types in JNI and Java have the following mapping:
Note that both Java
char
and JNIjchar
are two bytes, while the C/C++char
type is only one byte long. In fact, C/C++char
are interchangeable withjbyte
instead ofjchar
in JNI programming. - Android log library: We output the received values to the Android logging system at a native method, by using the following code:
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__);
ANDROID_LOG_INFO
is anenum
value defined inandroid/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 theprintf
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.