- Android Native Development Kit Cookbook
- Feipeng Liu
- 1159字
- 2021-07-27 18:07:25
Loading native libraries and registering native methods
Native code is usually compiled into a shared library and loaded before the native methods can be called. This recipe covers how to load native libraries and register native methods.
Getting ready
Please read the recipes in Chapter 1, Hello NDK, to set up the Android NDK development environment if you haven't done so already.
How to do it…
The following steps will show you how to build an Android application that demonstrates loading native libraries and registering native methods:
- Start Eclipse, select File | New | Android Project. Enter the value for Project Name as
NativeMethodsRegister
. Select Create new project in workspace. Then, click on Next. - In the next window, select the latest version of Android SDK, then click on Next to go to the next window.
- Specify the package name as
cookbook.chapter2
. Select the Create Activity checkbox, and specify the name asNativeMethodsRegisterActivity
. Set the value for Minimum SDK as 5 (Android 2.0). Then, click on Finish. - In Eclipse Package Explorer, right-click on the
NativeMethodsRegister
project, then select New | Folder. Enter the namejni
in the pop-up window, then click on Finish. - Right-click on the newly created
jni
folder under theNativeMethodsRegister
project, then select New | File. Enternativetest.c
as the value for File name, then click on Finish. - Add the following code to
nativetest.c
:#include <android/log.h> #include <stdio.h> jint NativeAddition(JNIEnv *pEnv, jobject pObj, jint pa, jint pb) { return pa+pb; } jint NativeMultiplication(JNIEnv *pEnv, jobject pObj, jint pa, jint pb) { return pa*pb; } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* pVm, void* reserved) { JNIEnv* env; if ((*pVm)->GetEnv(pVm, (void **)&env, JNI_VERSION_1_6)) { return -1; } JNINativeMethod nm[2]; nm[0].name = "NativeAddition"; nm[0].signature = "(II)I"; nm[0].fnPtr = NativeAddition; nm[1].name = "NativeMultiplication"; nm[1].signature = "(II)I"; nm[1].fnPtr = NativeMultiplication; jclass cls = (*env)->FindClass(env, "cookbook/chapter2/NativeMethodRegisterActivity"); // Register methods with env->RegisterNatives. (*env)->RegisterNatives(env, cls, nm, 2); return JNI_VERSION_1_6; }
- Add the following code to load the native shared library and define native methods to
NativeMethodRegisterActivity.java
:public class NativeMethodRegisterActivity extends Activity { … … private void callNativeMethods() { int a = 10, b = 100; int c = NativeAddition(a, b); tv.setText(a + "+" + b + "=" + c); c = NativeMultiplication(a, b); tv.append("\n" + a + "x" + b + "=" + c); } private native int NativeAddition(int a, int b); private native int NativeMultiplication(int a, int b); static { //use either of the two methods below //System.loadLibrary("NativeRegister"); System.load("/data/data/cookbook.chapter2/lib/libNativeRegister.so"); } }
- Change
TextView
in theres/layout/activity_native_method_register.xml
file as follows:<TextView android:id="@+id/display_res" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:padding="@dimen/padding_medium" android:text="@string/hello_world" tools:context=".NativeMethodRegisterActivity" />
- Create a file named
Android.mk
under thejni
folder with the following content:LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := NativeRegister LOCAL_SRC_FILES := nativetest.c LOCAL_LDLIBS := -llog include $(BUILD_SHARED_LIBRARY)
- Start a terminal, go to the
jni
folder under our project, and typendk-build
to build the native library. - Run the project on an Android device or emulator. You should see something similar to the following screenshot:
How it works…
This recipe describes how to load a native library and register native methods:
- Loading Native Library: The
java.lang.System
class provides two methods to load native libraries, namelyloadLibrary
andload
.loadLibrary
accepts a library name without the prefix and file extension. For example, if we want to load the Android native library compiled aslibNativeRegister.so
in our sample project, we useSystem.loadLibrary("NativeRegister")
. TheSystem.load
method is different. It requires the full path of the native library. In our sample project, we can useSystem.load("/data/data/cookbook.chapter2/lib/libNativeRegister.so")
to load the native library. TheSystem.load
method can be handy when we want to switch between different versions of a native library, since it allows us to specify the full library path.We demonstrated the usage of both the methods in the static initializer of the
NativeMethodRegisterActivity.java
class. Note that only one method should be enabled when we build and run the sample application. - JNIEnv Interface Pointer: Every native method defined in native code at JNI must accept two input parameters, the first one being a pointer to
JNIEnv
. TheJNIEnv
interface pointer is pointing to thread-local data, which in turn points to a JNI function table shared by all threads. This can be illustrated using the following diagram:The
JNIEnv
interface pointer is the gateway to access all pre-defined JNI functions, including the functions that enable the native code to process Java objects, access Java fields, invoke Java methods, and so on. TheRegisterNatives
native function we're going to discuss next is also one of them.Tip
The
JNIEnv
interface pointer points to thread-local data, so it cannot be shared between threads. In addition,JNIEnv
is only accessible by a Java thread. A native thread must call the JNI functionAttachCurrentThread
to attach itself to the VM, to obtain theJNIEnv
interface pointer. We will see an example of this in the Manipulating classes in JNI recipe in this chapter. - Registering Native Methods: JNI can automatically discover the native method implementation if its function name follows a specific naming convention as mentioned in Chapter 1, Hello NDK. This is not the only way. In our sample project, we explicitly called the
RegisterNatives
JNI function to register the native methods. TheRegisterNatives
function has the following prototype:jint RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);
The
clazz
argument is a reference to the class in which the native method is to be registered. Themethods
argument is an array of theJNINativeMethod
data structure.JNINativeMethod
is defined as follows:typedef struct { char *name; char *signature; void *fnPtr; } JNINativeMethod;
name
indicates the native method name,signature
is the descriptor of the method's input argument data type and return value data type, andfnPtr
is the function pointer pointing to the native method. The last argument,nMethods
ofRegisterNatives
, indicates the number of methods to register. The function returns zero to indicate success, and a negative value otherwise.RegisterNatives
is handy to register a native method implementation for different classes. In addition, it can simplify the native method name to avoid careless mistakes.The typical way of using
RegisterNatives
is in theJNI_OnLoad
method as shown in the following template.JNI_OnLoad
is called when the native library is loaded, so we can guarantee that the native methods are registered before they're invoked:JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* pVm, void* reserved) { JNIEnv* env; if ((*pVm)->GetEnv(pVm, (void **)&env, JNI_VERSION_1_6)) { return -1; } // Get jclass with env->FindClass. // Register methods with env->RegisterNatives. return JNI_VERSION_1_6; }
We demonstrated the usage of the preceding template in the JNI_OnLoad
method of our sample code, where we registered two native methods to add and multiply two input integers respectively. The execution result shown earlier proves that the Java code can invoke the two registered native methods successfully.
Note that this example uses some JNI features that we're going to cover in later recipes, including the FindClass
function and field descriptors. It is alright if don't fully understand the code at this stage. You can always go back to it after learning more about those topics.