1、JNI简介
JNI全称为Java Native Interface(JAVA本地调用)。从Java1.1开始,JNI成为java平台的一部分,它允许Java代码和其他语言写的代码(如C&C++)进行交互。并非从Android发布才引入JNI的概念的。
2、JNI与NDK
简单来说,Android的NDK提供了一些交叉编译工具链和Android自带的库,这些Android的库可以让开发者在编写本地语言的程序时调用。而NDK提供的交叉编译工具链就对已经编写好的C&C++代码进行编译,生成库。
当然了,你也可以自己搭建交叉编译环境,而不用NDK的工具和库。然后生成库,只要规范操作,一样可以生成能让JAVA层成功调用的库文件的。
利用NDK进行编译本地语言可以参考这篇博文:http://blog.csdn.net/conowen/article/details/7522667
3、JNI 调用流程
众所周知,Android的应用层的类都是以Java写的,这些Java类编译为Dex文件之后,必须靠Dalvik虚拟机( Virtual Machine)来执行。假如在执行java程序时,需要载入C&C++函数时,Dalvik虚拟机就会去加载C&C++的库,(System.loadLibrary("libName");)让java层能顺利地调用这些本地函数。需要清楚一点,这些C&C++的函数并不是在Dalvik虚拟机中运行的,所以效率和速度要比在Dalvik虚拟机中运行得快很多。
Dalvik虚拟机成功加载库之后,就会自动地寻找库里面的JNI_OnLoad函数,这个函数用途如下:
(1)告诉Dalvik虚拟机此C库使用哪一个JNI版本。如果你的库里面没有写明JNI_OnLoad()函数,VM会默认该库使用最老的JNI 1.1版本。但是新版的JNI做了很多的扩充,也优化了一些内容,如果需要使用JNI的新版功能,就必须在JNI_OnLoad()函数声明JNI的版本。如
result = JNI_VERSION_1_4;
当没有JNI_OnLoad()函数时,Android调试信息会做出如下提示(No JNI_OnLoad found)
04-29 13:53:12.184: D/dalvikvm(361): Trying to load lib /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea98
04-29 13:53:12.204: D/dalvikvm(361): Added shared lib /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea98
04-29 13:53:12.204: D/dalvikvm(361): No JNI_OnLoad found in /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea98, skipping init
(2)因为Dalvik虚拟机加载C库时,第一件事是调用JNI_OnLoad()函数,所以我们可以在JNI_OnLoad()里面进行一些初始化工作,如注册JNI函数等等。注册本地函数,可以加快java层调用本地函数的效率。
利用Android NDK编写一个简单的HelloWorld
1、Android NDK简介
NDK全称为native development kit本地语言(C&C++)开发包。而对应的是经常接触的Android-SDK,(software development kit)软件开发包(只支持java语言开发)。
简单来说利用NDK,可以开发纯C&C++的代码,然后编译成库,让利用Android-SDK开发的Java程序调用。NDK开发的可以称之为底层开发或者jni(java native interface)层开发,SDK开发可以称为上层开发。
2、为何要用NDK
2.1、众所周知,利用SDK编写的代码,生成的APK,很容易就可以反编译了,安全性极为不高,而利用NDK开发的库,不容易被反编译,保密性,安全性都提高了。
2.2、很多开源工程和大型工程都是C&C++代码,把它们转换为纯java语言显然是不可能的。
2.3、C&C++的代码运行速度和效率都比java快很多。
3、NDK环境的搭建
在http://developer.android.com/sdk/ndk/index.html下载最新版的NDK,然后解压到你喜欢的位置,然后把NDK的所在的目录,配置到环境变量里面就行。
如我在windows下面用cygwin开发。配置环境变量可以参考http://blog.csdn.net/conowen/article/details/7518870
ndk=/home/conowen/android-ndk-r7b
export ndk
4、新建一个NDK工程
新建一个目录,命名为HelloWorld,然后在里面新建一个名为jni的目录(名称一定要是jni,因为ndk-build的时候会在HelloWorld目录下寻找jni的目录,然后进行build),在jni目录下新建如下文件HelloWorld.c和Android.mk。
HelloWorld.c文件代码如下(此JNI是没有jni_onLoad函数的)
#include <string.h>
#include <jni.h>
jstring
Java_com_conowen_helloworld_HelloWorldActivity_helloWorldFromJNI( JNIEnv* env,
jobject thiz )
{
return (*env)->NewStringUTF(env, "HelloWorld! I am from JNI !");
}
/*注意,这里
jstring 表示返回值
Java_com_conowen_helloworld_HelloWorldActivity_helloWorldFromJNI
写法是Java+Android工程的包名+Android工程的Activity名+方法名,点号用下划线表示,这个写法很严格。
包名:com_conowen_helloworld
Activity名:HelloWorldActivity
方法名:helloWorldFromJNI
JNIEnv* env, jobject thiz是Native方法自带的参数,可以用来转换一个数据类型。也就是说其实这个helloWorldFromJNI是没有形参的。
*/
Android.mk代码如下
关于Android.mk的编写格式,下一篇博文补全
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- #LOCAL_MODULE表示生成的库的名字,前面的lib和后缀名不用写
- LOCAL_MODULE := HelloWorld
- LOCAL_SRC_FILES := HelloWorld.c
- include $(BUILD_SHARED_LIBRARY)
然后在终端里面,转到HelloWorld目录下,执行
- $ndk/ndk-build
5、新建一个Android工程
在eclipse里面新建一个Android工程,java代码如下,然后把刚刚生成的libs文件夹拖到Android工程目录下。
- package com.conowen.helloworld;
- import android.app.Activity;
- import android.os.Bundle;
- import android.widget.TextView;
- public class HelloWorldActivity extends Activity {
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- TextView tv = new TextView(this);
- tv.setText(helloWorldFromJNI());
- setContentView(tv);
- }
- public native String helloWorldFromJNI();// native声明,表示这个方法来自Native层。实现过程已经在native层实现了
- static {
- System.loadLibrary("HelloWorld");// 加载库,前面的lib和,后缀名不用写
- }
- }
生活不易,全靠努力,谢谢支持了