学习JNI--Android下使用JNI调用C
一、什么是JNI:
JNI是Java Native Interface的缩写,中文为JAVA本地调用。从Java1.1开始,Java Native Interface(JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。
1、使用JNI的好处:
a、可以使用JNI来实现“本地方法”(native methods),并在JAVA程序中调用它们,一般是在java中调用C的函数;相反的也可以用C来调用Java中的方法,这样可以复用很多以前写过的代码。
b、JNI支持一个“调用接口”(invocation interface),它允许你把一个JVM嵌入到本地程序中。本地程序可以链接一个实现了JVM的本地库,然后使用“调用接口”执行JAVA语言编写的软件模块。
2、使用JNI的副作用:
a、众所周知的Java的可移植性在使用JNI之后可能会被破坏,程序不再跨平台,原因很简单,一个操作系统上的本地方法很可能不能在另一个平台上正常运行,那么导致使用了这些本地方法的Java代码也同样无法在别的平台上运行。
b、程序不再是绝对安全的,本地代码的不当使用可能导致整个程序崩溃。
一个比较好的做法是,让调用的本地方法只集中在你写的工程的少数几个类中,这样可以降低Java和C代码的耦合性,也提高了代码的可维护性。
二、Java调用C来实现简单的HelloWorld:(以Android+eclipse为例)
好吧,又是HelloWorld!
这里我们以Android工程为例。
在真正开始做之前,还是先来了解一下基本流程:
1、先创建一个Java文件:HelloWorld.java,并且声明一个本地方法,注意要加上一个关键字native,在这里我们不写他的实现,具体实现交给C:
public native void helloworld();
2、然后我们可以使用javah指令来生成对应java文件的.h头文件(C来使用):
这里我默认已经搭好了环境,如果没有可以参看:http://www.cnblogs.com/baronzhao/archive/2012/07/10/2585181.html
打开cygwin,我们首先进入到对应工程的src目录下,在我这里是使用的cygwin打开的目 录:/cygdrive/d/Android_Workspace/JNI_day19/Exercise/src
然后执行:javah -jni com.example.exercise.MainActivity,其中com.example.exercise.MainActivity为MainActivity.java的全类名
正常的话就会在src目录下生成一个.h的头文件:com_example_exercise_MainActivity.h,其内容为:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_exercise_MainActivity */ #ifndef _Included_com_example_exercise_MainActivity #define _Included_com_example_exercise_MainActivity #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_exercise_MainActivity * Method: getStr * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_exercise_MainActivity_getStr( JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
实际上上面那些endif之类的宏定义都不是必须的,关键的部分只有一个include<jni.h>和JNIEXPORT jstring JNICALL Java_com_example_exercise_MainActivity_getStr(JNIEnv *, jobject);
其中,jni.h是jni定义的对应java中的类型和方法的c中的对应类型和函数的头文件,这个是必须有的。
其次,JNIEXPORT jstring JNICALL Java_com_example_exercise_MainActivity_getStr(JNIEnv *, jobject); 这一句除了JNIEXPORT可以去掉之外,其他的必须一致保留,大概写法就是:返回类型(这里是void) java_全类名(.换成_)_方法名(JNIEnv* env,jobject ojb);
3、在工程中新建一个名为jni的文件夹,并将刚才生成的.h文件剪切到jni文件夹中,并写好相应的.c/.cpp代码。
这里有一个小细节就是,有时候eclipse会在#include <jni.h>这里报一个黄色的问号的警告,找不到这个对应的jni.h文件,我们可以右键点击工程,然后点击AndroidTools->AddNativeSupport,如下图:
点击完之后会弹出一个对话框:
这里我们可以直接写入我们之后想生成的so文件名,这个文件名跟.c文件关联,这里我们选择hello,点击Finish之后,我们发现eclipse自动在jni文件夹中生成了两个新的文件:Android.mk和hello.cpp文件
其中Android.mk文件为:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) #对应的是打包成函数库的名字 LOCAL_MODULE := hello #src目录,对应的是c代码的文件 LOCAL_SRC_FILES := hello.cpp include $(BUILD_SHARED_LIBRARY)
这个文件的作用是指定生成.so文件的规则。
而hello.cpp文件则是我们需要实际写c代码的文件,在这里面,我们将实现刚才头文件中的jstring JNICALL Java_com_example_exercise_MainActivity_getStr(JNIEnv *, jobject);这个函数。
实际上这里要做的也很简单,我们的目的就是想在这个函数里返回一个字符串:"hello from C!",实际上这里就需要用到jni.h中定义的一个方法了:jstring (*NewStringUTF)(JNIEnv*, const char*);它的作用就是返回一个jstring字符串
#include <stdio.h> #include "com_example_err_MainActivity.h" //jstring (*NewStringUTF)(JNIEnv*, const char*); JNIEXPORT jstring JNICALL Java_com_example_err_MainActivity_helloWorld(JNIEnv * env, jobject obj) { return (*env).NewStringUTF("hello from c!"); }
4、之后我们需要做的就是生成.so库文件,在系统环境变量配置好了的情况下,直接打开cmd,进入到对应的工程目录下,执行ndk-build即可生成对应的库文件:libhello.so。
实际上这个文件会出现在工程文件夹的:obj/local/armeabi/文件夹下。
至此,函数库就已经生成好了,接下来的工作就是调用这个函数
5、在MainActiviy.java中调用这个.so文件。
我们可以再MainActiviy.java文件下,定义一个Button,对应点击事件cilck,每当点击的时候,就调用这个方法,将其返回的字符串"hello from c!"以Toast的形式打印出来,在这之前需要注意的是,我们需要先加载刚才生成的.so库文件,这里使用一个static块来加载,System.loadLibrary("hello");,注意这里我们不需要写libhello.so,而只需要写hello即可:
public class MainActivity extends Activity { public native String helloWorld(); static { System.loadLibrary("hello"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click(View view) { String helloWorld = helloWorld(); System.out.println(helloWorld); Toast.makeText(getApplicationContext(), helloWorld, Toast.LENGTH_LONG) .show(); } }
效果:
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。