JNI是Java Native Interface的缩写,译为Java本地接口。它允许Java代码和其他语言编写的代码进行交互。在android中提供JNI的方式,让Java程 序可以调用C语言程序。android中很多Java类都具有native接口,这些接口由本地实现,然后注册到系统中。



     主要的JNI代码放在以下的路径中:frameworks/base/core/jni/,这个路径中的内容被编译成库 libandroid_runtime.so,这是个普通的动态库,被放置在目标系统的/system/lib目录下。此外,android还有其他的 JNI库。JNI中的各个文件,实际上就是普通的C++源文件;在android中实现的JNI库,需要连接动态库 libnativehelper.so。

1,JNI的实现方式

    实现JNI需要在Java源代码中声明,在C++代码中实现JNI的各种方法,并把这些方法注册到系统中。实现JNI的核心是 JNINativeMethod结构体。

typedef struct {
      const char* name;
      const char* signature;
      void* fnPtr;
} JNINativeMethod;

第一个变量name是Java中JNI函数的名字,第二个变量signature用字符串描述函数参数和返回值,第三个变量fnPtr是JNI函 数C指针。  

     在Java代码中,定义的函数由JNI实现时,需要指定函数为native。

2,在应用程 序中使用JNI,可以通过代码中/development/samples/SimpleJNI来分析:

A,分析顶层 Android.mk文件

   LOCAL_PACKAGE_NAME := SimpleJNI    //生成PACKAGE的名字,在out\target\product\smdk6410\obj\APPS

   LOCAL_JNI_SHARED_LIBRARIES := libsimplejni //生成JNI共享库的名字,在....smdk6410\obj\SHARED_LIBRARIES

   include $(BUILD_PACKAGE)                   //以生成APK的方式编译

   include $(call all-makefiles-under,$(LOCAL_PATH))   //调用下层makefile

B,分析 JNI目录下Android.mk文件

   LOCAL_SRC_FILES:= \                           //JNI的C++源文件
         native.cpp

   include $(BUILD_SHARED_LIBRARY)       //以共享库方式编译

3,JNI的代码实现和调用

A,native.cpp内容

    static jint add(JNIEnv *env, jobject thiz, jint a, jint b){} //定义JAVA方法add

    static const char *classPathName = "com/example/android/simplejni/Native";   //目标JAVA类路径

    static JNINativeMethod methods[] = {                   //本地实现方法列表
            {"add", "(II)I", (void*)add },
    };

   static int registerNativeMethods(JNIEnv* env, const char* className,
   JNINativeMethod* gMethods, int numMethods){}   //为调用的某个JAVA类注册本地JNI函数

   static int registerNatives(JNIEnv* env){}                //为当前平台注册所有类及JNI函数

   jint JNI_OnLoad(JavaVM* vm, void* reserved)        //为当前虚拟机平台注册本地JNI

以上三个从下到上依次调用

B,SimpleJNI.java 内容

   package com.example.android.simplejni;  //JAVA包,跟文件路径对应

   import android.app.Activity;
   import android.os.Bundle;
   import android.widget.TextView;            //需要包含的类,以便调用函数

public class SimpleJNI extends Activity {
   /** Called when the activity is first created. */
   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       TextView tv = new TextView(this);
       int sum = Native.add(2, 3);                     //调用Native类的函数add,该add就是JNI函数,由CPP实现
       tv.setText("2 + 3 = " + Integer.toString(sum));
       setContentView(tv);                                //在屏幕上显示
   }
}

class Native {
   static {
    // The runtime will add "lib" on the front and ".o" on the end of
    // the name supplied to loadLibrary.
       System.loadLibrary("simplejni");              //载入由native.cpp生成的动态库,全名是lib+simplejni+.o
   }

   static native int add(int a, int b);                  //声明动态库中实现的JNI函数add,供JAVA调用
}

   编译生成PACKAGE后,安装到MID上,运行即是2+3=5。



Android JNI 使用的数据结构JNINativeMethod详解

Andoird 中使用了一种不同传统Java JNI的方式来定义其native的函数。其中很重要的区别是Andorid使用了一种Java 和 C 函数的映射表数组,并在其中描述了函数的参数和返回值。这个数组的类型是JNINativeMethod,定义如下: typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
第一个变量name是Java中函数的名字。 第二个变量signature,用字符串是描述了函数的参数和返回值 第三个变量fnPtr是函数指针,指向C函数。 其中比较难以理解的是第二个参数,例如 "()V" "(II)V" "(Ljava/lang/String;Ljava/lang/String;)V" 实际上这些字符是与函数的参数类型一一对应的。 "()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func(); "(II)V" 表示 void Func(int, int); 具体的每一个字符的对应关系如下 字符 Java类型 C类型 V      void            void
Z       jboolean     boolean
I        jint              int
J       jlong            long
D      jdouble       double
F      jfloat            float
B      jbyte            byte
C      jchar           char
S      jshort          short
数组则以"["开始,用两个字符表示 [I       jintArray      int[]
[F     jfloatArray    float[]
[B     jbyteArray    byte[]
[C    jcharArray    char[]
[S    jshortArray   short[]
[D    jdoubleArray double[]
[J     jlongArray     long[]
[Z    jbooleanArray boolean[]
上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾中间是用"/" 隔开的包及类名。而其对应的C函数名的参数则为jobject. 一个例外是String类,其对应的类为jstring Ljava/lang/String; String jstring
Ljava/net/Socket; Socket jobject
如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。 例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"


更多相关文章

  1. Android(安卓)Parcelable接口
  2. WebRTC 的 Android(安卓)2 Android(安卓)实现
  3. Android横竖屏切换总结
  4. [android] Serializable 和 Parcelable 区别
  5. Android弹幕实现:基于B站弹幕开源系统(5)-抽象和复用
  6. Qt on Android:将Qt调试信息输出到logcat中
  7. 浅谈Java中Collections.sort对List排序的两种方法
  8. 箭头函数的基础使用
  9. Python技巧匿名函数、回调函数和高阶函数

随机推荐

  1. Android(安卓)7.0 给开发者带来了什么
  2. Android开发的未来发展方向
  3. Android(安卓)AsyncTask完全解析,带你从源
  4. android 登陆、提交数据或加载数据时提示
  5. Android知识体系总结之Android部分之通知
  6. 深入理解Android消息处理系统——Looper
  7. Android(安卓)网络框架 Retrofit 源码解
  8. Android编译详解之lunch命令
  9. Android截屏截图方法所有方法汇总(包括Act
  10. Sqlite3常用简单指令