1、JNI概述

JNI是Java Native Interface的缩写,中文为JAVA本地调用。从Java1.1开始,Java Native Interface(JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。

2、使用方法

android JNI是连接android Java部分和C/C++部分的纽带,完整使用JNI需要Java代码和C/C++代码。其中C/C++代码用于生成库文件,Java代码用于引用C /C++库文件以及调用C/C++方法。

3、具体例子及说明

 

(1)Java部分代码

jnitest.java

package com.hello.jnitest;     
                  
import android.app.Activity;     
import android.os.Bundle;     
         
          
public class jnitest extends Activity {
	
	/** Called when the activity is first created. */     
        @Override    
          
        public void onCreate(Bundle savedInstanceState) {     
          
            super.onCreate(savedInstanceState);     
          
            setContentView(R.layout.main);     
          
            Nadd test = new Nadd();     
          
            setTitle("The Native Add Result is "+String.valueOf(test.nadd(10, 20)));     
          
        }      
          
    }

 

Nadd.java

package com.hello.jnitest;                
          
    public class Nadd {     
          
           static {     
          
           System.loadLibrary("hello_jni");     
          
           }                
	
    public native int nadd(int a, int b);     
          
    }

 

Java代码说明

1)jnitest.java是一个activity的类对象,在该类对象中生成调用JNI函数的类对象,同时调用JNI方法,最后将JNI方法的结果显示到标题栏上;

2)Nadd.java是一个引用和声明JNI库和函数的类,其中System.loadLibrary()函数用来引用JNI库,默认JNI库放在 android系统的/system/lib/目录下;

3)public native int nadd(int a, int b);为声明需要在java程序中使用的JNI库中的函数。

JNI中java部分的代码到此就结束了,总结一下在java代码中需要做两件事:

1)使用System.loadLibrary()函数来引用JNI库;

2)声明调用JNI库的函数且前面添加native关键字。

 

(2)C/C++部分代码

#define LOG_TAG "hello-JNI"     
    #include <stdio.h>     
    #include <stdlib.h>     
    #include <unistd.h>     
    #include <sys/types.h>     
    #include <sys/stat.h>     
    #include <fcntl.h>     
    #include <assert.h>     
    #include "jni.h"     
    #include "JNIHelp.h"     
    #include "android_runtime/AndroidRuntime.h"     
    static jint com_hello_jnitest_jnitest_nadd(JNIEnv *env, jobject obj, jint a, jint b)     
    {     
    return (a * b);     
    }     
    static JNINativeMethod gMethods[] = {     
    {"nadd", "(II)I", (void *)com_hello_jnitest_jnitest_nadd},     
    };     
    static int register_android_test_hello(JNIEnv *env)     
    {     
    return android::AndroidRuntime::registerNativeMethods(env, "com/hello/jnitest/Nadd", gMethods, NELEM(gMethods));     
    }     
    jint JNI_OnLoad(JavaVM *vm, void *reserved)     
    {     
    JNIEnv *env = NULL;     
    if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {     
    printf("Error GetEnv\n");     
    return -1;     
    }     
    assert(env != NULL);     
    if (register_android_test_hello(env) < 0) {     
    printf("register_android_test_hello error.\n");     
    return -1;     
    }     
    return JNI_VERSION_1_4;     
    }

 

C/C++代码说明

上面的代码主要做了三件事:

1)JNI_OnLoad()函数:该函数在Java程序调用System.loadLibrary()时被调用执行,用于向JavaVM注册JNI函数等。在本例中首先通过参数JavaVM(Java虚拟机指针)获取当前应用程序所在的线程即JNIEnv,再通过调用 android::AndroidRuntime::registerNativeMethods() 注册native实现的函数指针;

2)JNI函数和Java调用函数的映射关系:使用JNINativeMethod将java调用的函数名与JNI实现的函数名联系在一起;

3)JNI函数实现;

 

(3)Android.mk代码

LOCAL_PATH := $(call my-dir)     
    include $(CLEAR_VARS)     
    LOCAL_PRELINK_MODULE := false    
    LOCAL_SRC_FILES := \     
    com_hello_jnitest.cpp     
    LOCAL_SHARED_LIBRARIES := \     
    libandroid_runtime     
    LOCAL_MODULE := libhello_jni     
    include $(BUILD_SHARED_LIBRARY)

 

(4)注意事项

1)JNI C/C++部分的代码需要在android源代码树上进行编译,编译完成后一般是直接将生成的.so通过adb push方法上传到android虚拟机的/system/lib/目录下;

2)java代码可以在IDE(AS或Eclipse)工具下直接编译且在虚拟机上执行;

附上操作流程:

//编译JNI C/C++部分代码(在android内核源代码根目录下)

#make libhello_jni

//之后在out/target/product/generic/system/lib/目录下生成libhello_jni.so

//上传libhello_jni.so到android虚拟机

#adb push out/target/product/generic/system/lib/libhello_jni.so /system/lib

注意:此时有可能出现Out of Memory的错误提示,当出现如上错误提示时,需要使用#adb remount重新加载一下就可以了。