您的位置:首页 > 编程语言 > Java开发

01_JNI是什么,为什么使用,怎么用JNI,Cygwin环境变量配置,NDK案例(使用Java调用C代码),javah命令使用

2015-08-02 00:04 856 查看
1 什么是JNI
JNI Java本地开发接口
JNI是一个协议,这个协议用来沟通java代码和外部的本地代码(C/C++) 通过这个协议,java代码就可以调用外部的C/C++代码,外部的C/C++代码也可以调用Java代码。
2 为什么用JNI
1  JNI扩展了java虚拟机的能力,驱动开发(wifi-hotspot)2.3无线热点共享 2  Native code效率高,数学运算,实时渲染的游戏上,音视频处理(极品飞车),opengl,ffmpeg 3  复用代码(文件压缩,人脸识别) 4  特殊的业务场景
3 怎么用JNI
1 C/C++语言 2 掌握java ini流程 3 NDK(native develop kits)
5 配置cygwin64的环境变量的方式是编写\Cygwin\etc\profile,将ndk的路径配置到path里面,截图如下:

打开Cygwin输入:Make –v,结果如下:

显示出来GNI Make的版本,说明我们的cygwin模拟的Linux编辑环境模拟成功。输入ndk-build,出现以下结果:

显示出上面的效果,说明环境变量已经配置好了。 6 同样可以将这个变量配置到windows下的环境变量中:配置如下:

进入CMD命令行窗口中,如果出现以下结果,说明配置成功:

 当配置了第6步之后,若cygwin没有配置成功时,也可以在window中的cmd命令行中使用ndk-build. 7 jni中的数据类型(后面是Java中的类型,左边是C语言中的类型):
#ifdef HAVE_INTTYPES_H # include <inttypes.h>      /* C99 */ typedef uint8_t         jboolean;       /* unsigned 8 bits */ typedef int8_t          jbyte;          /* signed 8 bits */ typedef uint16_t        jchar;          /* unsigned 16 bits */ typedef int16_t         jshort;         /* signed 16 bits */ typedef int32_t         jint;           /* signed 32 bits */ typedef int64_t         jlong;          /* signed 64 bits */ typedef float           jfloat;         /* 32-bit IEEE 754 */ typedef double         jdouble;        /* 64-bit IEEE 754 */ #else typedef unsigned char   jboolean;       /* unsigned 8 bits */ typedef signed char     jbyte;          /* signed 8 bits */ typedef unsigned short  jchar;          /* unsigned 16 bits */ typedef short          jshort;         /* signed 16 bits */ typedef int            jint;           /* signed 32 bits */ typedef long long      jlong;          /* signed 64 bits */ typedef float          jfloat;         /* 32-bit IEEE 754 */ typedef double        jdouble;        /* 64-bit IEEE 754 */ #endif   /* "cardinal indices and sizes" */ typedef jint            jsize;
8 案例效果(下面的土司是调用C语言代码显示的):

接下来编写案例,使用java代码调用C代码,创建项目helloworldformc,代码结构如下:



其中libs下的内容如下:

9 编写MainActivity,代码如下:
package com.example.helloworldformc;   import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Toast;   public class MainActivity extends Activity {          // 1 定义一个c方法的接口   相当于在java代码中定义了一个接口          //接口的实现方法是C语言实现的          public native String helloWorldFromC();           
         // 变态命名
         public native String hello_world_from_c();
         // 5 步在java代码中 引入库函数          static{                    System.loadLibrary("hello");// 注意事项 去掉前面的lib 后面的.so          }                   @Override          protected void onCreate(Bundle savedInstanceState) {                    super.onCreate(savedInstanceState);                    setContentView(R.layout.activity_main);          }                   public void click(View view){                    // 弹出一个土司 土司的内容 是c代码写出来                           // 第6步                    Toast.makeText(getApplicationContext(), helloWorldFromC(), 0).show();          } }
10 在项目下创建一个jni目录(这个是必须的),开始编写Hello.c,代码如下:
#include <stdio.h> #include <jni.h>   /*  * 对应的是:public native String helloWorldFromC(){}  * jstring:表示的是返回的是String类型的  * 函数名称规则是:Java_包名_类名_方法名。  *  * 注意:包名之间的"."在下面要变成下划线  */ jstring Java_com_example_helloworldformc_MainActivity_helloWorldFromC(JNIEnv* env,jobject obj) {     //2步  实现C代码     //返回一个java String类型的字符串,使用一下函数     //jstring     (*NewStringUTF)(JNIEnv*, const char*);     //(*env) 相当于 JNINativeInterface* JNIEnv     //*(*env)  相当于 JNINativeInterface       //可以通过下面两种方式返回参数     //return (**env).NewStringUTF(env,"helloworldfromc");       return  (*env)->NewStringUTF(env,"helloworldfromc");       //android.mk 告诉编译器  如何把C代码打包成函数库     //3 生成.mk文件     //4 步  把C代码  打包成函数库 }
/*这里JNIEXPORT表示的是通过工具生成的,这时候也可以将JNIEXPORT去掉*/JNIEXPORT jstring JNICALL Java_com_example_helloworldformc_MainActivity_hello_1world_1from_1c
 (JNIEnv * env, jobject obj){
}

注意:上面的这个源文件的类名也可以通过javah命令生成,生成过程如下:
(1)       如果JDK使用的是1.6,使用javah命令前要到达:项目目录\bin\classes目录下

(2)       如果JDK使用的是1.7,使用javah命令前要达到:项目目录\bin\src目录下: 例如(我使用的是JDK1.7): 在项目中生成.h:

11 Android.mk文件的内容如下:
#一个Android.mk文件必须下面这个变量开头,它用于定位 #它用于定位你的源文件,my-dir这个宏是构件系统提供的, #它用于返回当前目录的路径(这个目录包含Android.mk它自己) LOCAL_PATH := $(call my-dir)   #CLEAR_VARS也是build system构件系统提供的,指定到一个指定的GNU Makefile文件 #这个Makefile文件帮你去清除一些LOCAL_XXX变量(比如:LOCAL_MODULE,LOCAL_SRC_FILES #,LOCAL_STATIC_LIBRARIES.等等...),除了LOCAL_PATH. include $(CLEAR_VARS)   #LOCAL_MODULE变量必须在你的Android.mk文件中定义。名称必须唯一 #并且不能包含一些空格,要注意的是构件系统会自动的添加适当的前缀和前缀 #换一句话说,一个命名为"foo"的共享库模块,会被生成"libfoo.so"文件。 #重要提示:如果你定义的模块名称是"libfoo",构建系统将不会添加"lib"前缀, #并且也会生成"libfoo.so"文件。 LOCAL_MODULE    := hello     #LOCAL_SRC_FILES变量必须包含一个C或者C++的源文件集合,这些文件最终会被用于 #生成一个module,注意的是,这里不应该有头文件和包含文件,因为构件系统自动为您 #添加这些文件,只需要列出源文件就行了。   #注意:默认的C++源文件的后缀名是.cpp,同样你也可以通过LOCAL_CPP_EXTENSION来自定义 #CPP的后缀名(例如:".cxx"可以,"cxx"不可以) LOCAL_SRC_FILES := Hello.c   include $(BUILD_SHARED_LIBRARY)
12 接下来使用cygwin,使用ndk-build来编译程序
(1)、如想将c变成.so的库,需要使用ndk-build命令 (2)、将路径路径切换到工程目录下。 (3)、然后执行ndk-build这个操作(这个操作直接指定到项目路径下就可以了)

注意:在使用这个ndk-build功能之前,需要有Android.mk文件(告诉编译器,如何将C代码打包成函数库。具体的Android.mk怎么使用可以看ndk中的Android.html文档)
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: