Android JNI使用方法,JNI机制详解

JNI的出现使得开发者既可以利用Java语言跨平台、类库丰 富、开发便捷等特点,又可以利用Native语言的高效。

JNI是JVM实现中的一部分,因此Native语言和Java代码都运行在JVM的宿主环境。

JNI是一个双向的接口:开发者不仅可以通过JNI在Java代码中访问Native模块,还可以在 Native代码中嵌入一个JVM,并通过JNI访问运行于其中的Java模块。可见,JNI担任了一个桥梁的角色,它将JVM与Native模块联系起 来,从而实现了Java代码与Native代码的互访。在OPhone上使用Java虚拟机是为嵌入式设备特别优化的Dalvik虚拟机。每启动一个应 用,系统会建立一个新的进程运行一个Dalvik虚拟机,因此各应用实际上是运行在各自的VM中的。Dalvik VM对JNI的规范支持的较全面,对于从JDK 1.2到JDK 1.6补充的增强功能也基本都能支持。

缺点:由于Native模块的使用,Java代码会丧失其原有的跨平台性和类型安全等特性。此外,在JNI应用中,Java代码与Native代 码运行于同一个进程空间内;对于跨进程甚至跨宿主环境的Java与Native间通信的需求,可以考虑采用socket、Web Service等IPC通信机制来实现。

互的类型可以分为在Java代码中调用Native模块和在Native代码中调用Java模块两种。

Java调用Native模块

HelloJni.java

[java] view plain copy
  1. /* A native method that is implemented by the
  2. * 'hello-jni' native library, which is packaged
  3. * with this application.
  4. */
  5. public native String  stringFromJNI();

这个stringFromJNI()函数就是要在Java代码中调用的Native函数。

hello-jni.c

[java] view plain copy
  1. #include <string.h>
  2. #include <jni.h>
  3. jstring
  4. Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
  5. jobject thiz ) {
  6. return (*env)->NewStringUTF(env, "Hello from JNI !");
  7. }

这个Native函数对应的正是我们在com.example.hellojni.HelloJni这个中声明的Native函数String stringFromJNI()的具体实现。

JNI函数的命名规则: Java代码中的函数声明需要添加native 关键 字;Native的对应函数名要以“Java_”开头,后面依次跟上Java的“package名”、“class名”、“函数名”,中间以下划线“_” 分割,在package名中的“.”也要改为“_”。此外,关于函数的参数和返回值也有相应的规则。对于Java中的基本类型如int 、double 、char 等,在Native端都有相对应的类型来表示,如jint 、jdouble 、jchar 等;其他的对象类型则统统由jobject 来表示(String 是个例外,由于其使用广泛,故在Native代码中有jstring 这个类型来表示,正如在上例中返回值String 对应到Native代码中的返回值jstring )。而对于Java中的数组,在Native中由jarray 对应,具体到基本类型和一般对象类型的数组则有jintArray 等和jobjectArray 分别对应(String 数组在这里没有例外,同样用jobjectArray 表示)。还有一点需要注意的是,在JNI的Native函数中,其前两个参数JNIEnv *和jobject 是必需的——前者是一个JNIEnv 结构体的指针,这个结构体中定义了很多JNI的接口函数指针,使开发者可以使用JNI所定义的接口功能;后者指代的是调用这个JNI函数的Java对象,有点类似于C++中的this 指针。在上述两个参数之后,还需要根据Java端的函数声明依次对应添加参数。在上例中,Java中声明的JNI函数没有参数,则Native的对应函数只有类型为JNIEnv *和jobject 的两个参数。

,要使用JNI函数,还需要先加载Native代码编译出来的动态库文件(在Windows上是.dll,在Linux上则为.so)。这个动作是通过如下语句完成的:

[java] view plain copy
  1. static {
  2. System.loadLibrary("hello-jni");
  3. }
JNI函数的使用方法和普通Java函数一样。在本例中,调用代码如下:
[java] view plain copy
  1. TextView tv = new TextView(this);
  2. tv.setText( stringFromJNI() );
  3. setContentView(tv);

Native调用Java模块

[java] view plain copy
  1. package com.example.hellojni;
  2. public class SayHello {
  3. public String sayHelloFromJava(String nativeMsg) {
  4. String str = nativeMsg + " But shown in Java!";
  5. return str;
  6. }
  7. }

从OPhone的系统架构来看,JVM和Native系统库位于内核之上,构成OPhone Runtime;更多的系统功能则是通过在其上的Application Framework以Java API的形式提供的。因此,如果希望在Native库中调用某些系统功能,就需要通过JNI来访问Application Framework提供的API。

一般来说,要在Native代码中访问Java对象,有如下几个步骤:
1.         得到该Java对象的类定义。JNI定义了jclass 这个类型来表示Java的类的定义,并提供了FindClass接口,根据类的完整的包路径即可得到其jclass 。
2.         根据jclass 创建相应的对象实体,即jobject 。在Java中,创建一个新对象只需要使用new 关键字即可,但在Native代码中创建一个对象则需要两步:首先通过JNI接口GetMethodID得到该类的构造函数,然后利用NewObject接口构造出该类的一个实例对象。
3.         访问jobject 中的成员变量或方法。访问对象的方法是先得到方法的Method ID,然后使用Call<Type>Method 接口调用,这里Type对应相应方法的返回值——返回值为基本类型的都有相对应的接口,如CallIntMethod;其他的返回值(包括String) 则为CallObjectMethod。可以看出,创建对象实质上是调用对象的一个特殊方法,即构造函数。访问成员变量的步骤一样:首先 GetFieldID得到成员变量的ID,然后Get/Set<Type>Field读/写变量值。
[java] view plain copy
  1. jstring helloFromJava( JNIEnv* env ) {
  2. jstring str = NULL;
  3. jclass clz = (*env)->FindClass(env, "com/example/hellojni/SayHello");
  4. jmethodID ctor = (*env)->GetMethodID(env, clz, "<init>", "()V");
  5. jobject obj = (*env)->NewObject(env, clz, ctor);
  6. jmethodID mid = (*env)->GetMethodID(env, clz, "sayHelloFromJava", "(Ljava/lang/String;)Ljava/lang/String;");
  7. if (mid) {
  8. jstring jmsg = (*env)->NewStringUTF(env, "I'm born in native.");
  9. str = (*env)->CallObjectMethod(env, obj, mid, jmsg);
  10. }
  11. return str;
  12. }

提一下编程时要注意的要点:1、FindClass要写明Java类的完整包路径,并将 “.”以“/”替换;2、GetMethodID的第三个参数是方法名(对于构造函数一律用“<init>”表示),第四个参数是方法的“签 名”,需要用一个字符串序列表示方法的参数(依声明顺序)和返回值信息。

如上这种使用NewObject创建的对象实例被称为“Local Reference”,它仅在创建它的Native代码作用域内有效,因此应避免在作用域外使用该实例及任何指向它的指针。如果希望创建的对象实例在作用 域外也能使用,则需要使用NewGlobalRef接口将其提升为“Global Reference”——需要注意的是,当Global Reference不再使用后,需要显式的释放,以便通知JVM进行垃圾收集。

Native模块的编译与发布

NDK的全称是Native Development Toolkit,即原生应用开发包。,为广大开发者提供了编译用于Android应用的Native模块的能力,以及将Native模块随Java应用打包为APK文件。

引用地址:http://mysuperbaby.iteye.com/blog/915425

二、

[java] view plain copy
  1. <span style="font-family: Arial, Helvetica, sans-serif;">原文地址:http://www.open-open.com/lib/view/open1324909652374.html</span>

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

[java] view plain copy
  1. jnitest.java
  2. package com.hello.jnitest;
  3. import android.app.Activity;
  4. import android.os.Bundle;
  5. public class jnitest extends Activity {
  6. /** Called when the activity is first created. */
  7. @Override
  8. public void onCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.main);
  11. Nadd test = new Nadd();
  12. setTitle("The Native Add Result is "+String.valueOf(test.nadd(10, 20)));
  13. }
  14. }
  15. Nadd.java
  16. package com.hello.jnitest;
  17. public class Nadd {
  18. static {
  19. System.loadLibrary("hello_jni");
  20. }
  21. public native int nadd(int a, int b);
  22. }
Java代码说明:
1)jnitest.java是一个activity的类对象,在该类对象中生成调用JNI函数的类对象,同时调用JNI方法,最后将JNI方法的结果显示到标题栏上;
2)Nadd.java是一个引用和声明JNI库和函数的类,其中System.loadLibrary();函数用来引用JNI库,默认JNI库放在 android系统的/system/lib/目录下;public nadd int nadd(int a, int b);为声明需要在java程序中使用的JNI库中的函数;
JNI中java部分的代码到此就结束了,总结一下在java代码中需要做两件事:
1)使用System.loadLibrary()函数来引用JNI库;
2)声明调用JNI库的函数且前面添加native关键字;

android C/C++部分代码:

[cpp] view plain copy
  1. #define LOG_TAG "hello-JNI"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <fcntl.h>
  8. #include <assert.h>
  9. #include "jni.h"
  10. #include "JNIHelp.h"
  11. #include "android_runtime/AndroidRuntime.h"
  12. static jint com_hello_jnitest_jnitest_nadd(JNIEnv *env, jobject obj, jint a, jint b)
  13. {
  14. return (a * b);
  15. }
  16. static JNINativeMethod gMethods[] = {
  17. {"nadd", "(II)I", (void *)com_hello_jnitest_jnitest_nadd},};
  18. static int register_android_test_hello(JNIEnv *env)
  19. {
  20. return android::AndroidRuntime::registerNativeMethods(env, "com/hello/jnitest/Nadd", gMethods, NELEM(gMethods));
  21. }
  22. jint JNI_OnLoad(JavaVM *vm, void *reserved)
  23. {
  24. JNIEnv *env = NULL;
  25. if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {
  26. printf("Error GetEnv\n");
  27. return -1;
  28. }
  29. assert(env != NULL);
  30. if (register_android_test_hello(env) < 0) {
  31. printf("register_android_test_hello error.\n");
  32. return -1;
  33. }
  34. return JNI_VERSION_1_4;
  35. }
JNI 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函数实现;

Android.mk代码:

[html] view plain copy
  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_PRELINK_MODULE := false
  4. LOCAL_SRC_FILES := \
  5. com_hello_jnitest.cpp
  6. LOCAL_SHARED_LIBRARIES := \
  7. libandroid_runtime
  8. LOCAL_MODULE := libhello_jni
  9. include $(BUILD_SHARED_LIBRARY)
需要注意的是:
1)JNI C/C++部分的代码需要在android源代码树上进行编译,编译完成后我的做法是直接将生成的.so通过adb push方法上传到android虚拟机的/system/lib/目录下;
2)java代码可以在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重新加载一下就可以了。
另外,也有可能直接使用eclipse启动android虚拟机时出现上述错误且使用#adb remount也出现的情况,此时需要手动启动android虚拟机,如:#emulator -avd xxx -partition-size 128,之后在使用#adb push就可以了。

Android JNI使用方法,JNI机制详解相关推荐

  1. Android开发之触摸事件处理机制详解

     android触碰消息传递机制 用户的每次触碰(onClick,onLongClick,onScroll,etc.)都是由一个ACTION_DOWN+n个ACTION_MOVE+1个ACTION ...

  2. Android设备扫描机制详解

    Android设备扫描机制详解 本文基于Android pie,对Android的设备扫描机制做一个全面的解析,由于本人掌握的知识有限,如有讲错的地方还请大家指出来. Android提供了一套扫描机制 ...

  3. Android 系统(199)---Android事件分发机制详解

    Android事件分发机制详解 前言 Android事件分发机制是Android开发者必须了解的基础 网上有大量关于Android事件分发机制的文章,但存在一些问题:内容不全.思路不清晰.无源码分析. ...

  4. android系统(63)---Jobscheduler运行机制详解

    android之Jobscheduler运行机制详解 如果想在将来达到一定条件下执行某项任务时,可以在一个实现了JobService的子类的onStartJob方法中执行这项任务,使用JobInfo的 ...

  5. Android 进阶——Framework 核心之Android Storage Access Framework(SAF)存储访问框架机制详解(一)

    文章大纲 引言 一.Android Storage Access Framework 二.Storage Access Framework 的主要角色成员 1.Document Provider 文件 ...

  6. Android 进阶——Framework 核心之Android Storage Access Framework(SAF)存储访问框架机制详解(二)

    文章大纲 引言 一.DirectFragment 1.当选中DirectoryFragment中RecyclerView的Item时 2.选中DirectoryFragment中RecyclerVie ...

  7. Android学习笔记——Android 签名机制详解

    Android 签名机制详解 近期由于工作需要在学习 Android 的签名机制,因为没有现成资料,只能通过开发者文档和阅读博客的方式对 Android 签名机制进行大致了解.过程中查阅到的资料相对零 ...

  8. View的事件体系之三 android事件分发机制详解(下)

    接着上一篇来分析事件分发机制,在看了各位大牛的关于事件分发机制的分析后茅塞顿开,之前看过好几遍郭霖,弘扬以及玉刚大神关于事件体系的讲解,一直看不懂,比较模糊,最近复习时,看到一篇博文,写的相当精彩,看 ...

  9. java有返回值的方法回调_java调用回调机制详解

    调用和回调机制 在一个应用系统中, 无论使用何种语言开发, 必然存在模块之间的调用, 调用的方式分为几种: 1.同步调用 同步调用是最基本并且最简单的一种调用方式, 类A的方法a()调用类B的方法b( ...

最新文章

  1. Hyperledger Fabric 私有数据(2)操作流程
  2. VTK:地标变换用法实战
  3. android Google Advertising ID 如何重置
  4. 关于微信小程序web开发者工具模拟器出现空白问题
  5. DDD | 领域驱动设计初探
  6. 【开发】微信公共帐号机器人WeBot
  7. 洛谷-P1957 口算练习题
  8. 麻省理工学院公开课:计算机科学及编程导论习题3下
  9. python之sys模块
  10. 计算机蓝屏了 怎么维修,电脑蓝屏怎么解决
  11. java生成迷宫_java怎么生成迷宫地图
  12. VBA禁止更改工作表名称
  13. vue表单项目解决安卓键盘遮挡输入框问题
  14. 利用pot播放器将视频的音频文件抽离。
  15. 阿里云ECS上Linux系统实例搭建FTP站点
  16. 吴忌寒有算力,我有的是钱,我打算用钱摧毁比特大陆|专访澳洲中本聪
  17. 吕雉到底是一个怎样的人?
  18. jupyter notebook如何导入使用pygal模块
  19. 数据库中触发器的作用,规则和限制
  20. 前端 - Underscore.js

热门文章

  1. CentOS配置snmp
  2. 复数矩阵Cholesky分解算法的C++实现
  3. 杭电 2016 计算机组成原理,杭电计算机组成原理多功能ALU设计实验
  4. 《零基础入门深度学习》解读
  5. jquery 对象 和 js 对象 为什么要互相转换?_JQuery对象与dom对象两者之间的相互转换...
  6. xss挖掘思路分享_视频分享:XSS的利用与挖掘
  7. python打不开text_python-无法启动并使用TextBlob运行
  8. bin文件查看器app_腾讯文件:腾讯官方出品的微信 / QQ 文件管理和清理利器
  9. Android 换肤功能的实现(Apk插件方式)
  10. 【NOIP模拟】矩阵