一直以来,都觉得Jni很神奇的一个东西,今天我就学习学习这个神奇的东西。
在给定的平台上采用java通过jni调用本地方法,而本地方法是以库文件的形式存放的,在windows平台上是DLL文件形式,在unix机器上是so文件形式。通过调用本地库的内部方法,使java可以实现和本地机器的紧密联系,调用系统级的各接口方法。
第一步:安装windows系统上安装与使用Android NDK
http://www.cnblogs.com/luxiaofeng54/archive/2011/02/12/1952391.html
第二步:
新建一个工程,可以建立一个普通的Java工程或者一个Android工程都可以。类似下面这样:
package com.example.ajnitest1;
import android.os.Bundle;
import android.app.Activity;
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public static native void Show();
}
你必须要写一个标示符为native的方法,并且没有方法体的。类似上面那样。
1. 生成.java文件:注意进入到src下MainActivity这个类所在的目录下:
javac MainActivity.java
然后在bin/classes目录下会生成一个MainActivity.java的类文件;
2. 生成.h文件:注意要进入到bin目录下的classes这个目录下,因为你有native方法的类生成的类文件在这个目录下:
javah com.example.ajnitest1.MainActivity
2013.5.6 补充:javah -classpath bin/classes -d jni com.example.jnitest1.MainActivity,这样的好处是可以直接在项目中生成jni目录,要不还要自己新建一个。
然后在bin/classes目录下会生成com_example_ajnitest1_MainActivity.h这个文件,内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_ajnitest1_MainActivity */
#ifndef _Included_com_example_ajnitest1_MainActivity
#define _Included_com_example_ajnitest1_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_ajnitest1_MainActivity
* Method: Show
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_example_ajnitest1_MainActivity_Show
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
说实话,我看不太懂。看着似乎这个名称是:Java+包名+类名+方法名 -> Java_com_example_ajnitest1_MainActivity_Show
我从新编译了一个:这回native方法返回值是String,如下:
public native String pop();
编译后的.h文件如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_a2_MainActivity */
#ifndef _Included_com_example_a2_MainActivity
#define _Included_com_example_a2_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_a2_MainActivity
* Method: pop
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_a2_MainActivity_pop
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
对比一下注释中的内容,标明了方法的返回值的不同。
编写C文件,名称为:com_example_a2_MainActivity.c
#include <string.h>
#include <jni.h>
JNIEXPORT jstring JNICALL Java_com_example_a2_MainActivity_pop (JNIEnv * env, jobject obj){
return (*env)->NewStringUTF(env, "songshichao");
}
编写Android.mk文件,名称就是Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := A2
LOCAL_SRC_FILES := com_example_a2_MainActivity.c
include $(BUILD_SHARED_LIBRARY)
OK,继续。。。
打开cygwin,不会安装的看这个帖子:http://www.cnblogs.com/luxiaofeng54/archive/2011/02/12/1952391.html
了解到它就是模拟个unix环境,以为命令都一样呢,想切换到d盘,怎么也进不去,啥命令都不好使,后来才知道,要这样用才行:cd /cygdrive/d,然后就可以自由的使用linux的命令了。
Administrator@PC-201204141932 ~
$ cd /cygdrive/d
然后进入到你的项目目录的jni目录下,去编译:
前几次总是出错,如下:
$ ndk-build
make: *** 没有规则可以创建“obj/local/armeabi/objs/A2/com.example_a2_MainActivity.o”需要的目标“jni/com.example_a2_MainActivity.c”。 停止。
后来仔细检查了下,确实我的Android.mk文件写错了,太大意了。
修改后从新build,正确了,结果如下:
$ ndk-build
Cygwin : Generating dependency file converter script
Compile thumb : A2 <= com_example_a2_MainActivity.c
SharedLibrary : libA2.so
Install : libA2.so => libs/armeabi/libA2.so
刷新libs文件夹后,发现,确实多了一个armeabi文件夹,里面有一个libA2.so文件,就不截图了。
继续,既然写了这个东西,就得用啊,怎么用呢,像这样:
package com.example.a2;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
public class MainActivity extends Activity {
// 库的扩展名字不用写出来,究竟是DLL还是SO,由系统自己判断。
static {
System.loadLibrary("A2");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("tag", pop());
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
public native String pop();
}
去Logcat上看,哈哈, 已经成功输出内容了,真不错,我纠结了好久了,终于基本的知道如何使用JNI了。
注意:
Jni的弊端:
1.使用了jni,那么这个java app 将不能跨平台,如果要移植到别的平台上,那么native代码就需要进行重新编写。
2.Java是强类型的语言,而C/C++不是,因此你写jni时要更小心。
参考链接:
1.下面这篇讲的和上面差不多,唯一的区别,我用的环境是cygwin,就是在windows环境下开发jni需要c/c++编译器的支持。他使用GNUStep,下载地址http://www.gnustep.org/experience/Windows.html。具体看链接。
android 之JNI开发步骤总结
http://www.eoeandroid.com/forum.php?mod=viewthread&tid=207213&fromuid=511991
2.这篇也喝上面的差不多,不过做了很多理论解释
cygwin+ndk+eclipse下JNI技术入门
http://www.eoeandroid.com/forum.php?mod=viewthread&tid=189315&fromuid=511991
3.这个是装一个插件可以不用命令行build,不过我没看太懂。
http://permadi.com/blog/2011/09/creating-your-first-android-jnindk-project-in-eclipse-with-sequoyah/
4.仍然是一篇基础的博客
http://www.cnblogs.com/skyseraph/archive/2012/03/20/2407593.html