第一次使用JNI
来源:互联网 发布:淘宝网店如何刷钻 编辑:程序博客网 时间:2024/06/05 00:19
编程语言的第一课一般都是通过 “Hello World”开始的,所以我也从"Hello World"开始学习使用JNI
使用JNI编程需要以下几个工具:
1、android开发环境,包括用到的SDK, Eclispe, JDK,android开发环境的搭建网上有很多,搭建起来就行了。
2、NDK。由google提供,用来编译C/C++源文件。NDK提供了各个平台各个android版本下的头文件和.o文件,使用这些头文件里面定义的函数。具体各个函数的用法可以参考linux应用编程。
NDK环境搭建
在http://developer.android.com/tools/sdk/ndk/index.html点击打开链接 下载相应平台的NDK,我是在windows下开发的,下载后直接双击解压即可。然后将解压后的目录加入到windows环境变量path中,打开windows 控制台,键入命令ndk-build,如果不是说找不到命令,则表示可以使用了。我们可以看看这个ndk-build是个什么文件,在解压后的目录下用记事本打开ndk-build,发现这个文件是一个shell脚本,因此在正常情况下,这个脚本在windows下是不可以使用的,需要安装一些工具。我这里是安装的MinGW,也是在window下使用的比较多的,怎么安装,baidu即可。
使用JNI编程。
step1:编程Java程序
打开Eclipse,创建android工程,我这里创建的工程为NDKTest。
package com.example.ndktest;import android.app.Activity;import android.os.Bundle;import android.widget.TextView;public class MainActivity extends Activity {static{System.loadLibrary("hello_world"); //加载静态库} @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView text = (TextView)findViewById(R.id.textView1); text.setText(getText()); //调用native方法。 } private native String getText(); //native方法,返回的值为文本框的text.}
这里创建了一个简单的文本框,并调用了native方法,显示native方法返回的文本。所以在java中使用native方法很简单,只需要将函数名前加一个native关键字即可。并且在使用native函数之前,加载包含该函数的动态库即可。
到此,我们的java程序算是写完了,接下来编译android程序,得到.class文件。为什么需要.class文件?接下来在step2就知道了。实际上在这里我们只用编译MainActivity.java,因为只有这个类中用到了native函数,得到MainActivity.class。该文件放在工程目录下的bin/classes/{package}目录下。
step2:生成头文件
第一步我们在java程序中申明并调用了native函数,但是我们的native函数具体定义应该是什么样的呢?这两个函数的调用关系是怎样建立起来的?我们先执行完以下的步骤再回过来看这两个问题。
使用javah 工具生成相应的头文件。
javah生成我们需要的头文件,其中-d表示我们要生成的头文件的目录,-classpath表示我们的类的路径,最后的"com.example.ndktest.MainActivity"是我们在step1中生成的MainActivity.class的路径。这里有三个地方要注意:
1、一般-d 接的目录为jni,原因会在step4中讲述。
2、-classpath 接的参数为class路径,如果没有android.jar目录,则会出现找不到Activity类的错误,因为我们的MainActivity是继承的Activity类。
3、最后的参数com.example.ndktest.MainAcitivy是我们类的路径,而不是绝对路径名。
OK,到这里如果顺利的话就会生成我们的头文件了,因为我们是在 C:\Users\Administrator 目录下运行的javah命令,所以到该目录下就可以找到jni目录并且生成了相应的头文件,我们来看看头文件是什么样子的。
com_example_ndktest_MainActivity.h:
/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h> /* Header for class com_example_ndktest_MainActivity */#ifndef _Included_com_example_ndktest_MainActivity#define _Included_com_example_ndktest_MainActivity#ifdef __cplusplusextern "C" {#endif#undef com_example_ndktest_MainActivity_MODE_PRIVATE#define com_example_ndktest_MainActivity_MODE_PRIVATE 0L#undef com_example_ndktest_MainActivity_MODE_WORLD_READABLE#define com_example_ndktest_MainActivity_MODE_WORLD_READABLE 1L#undef com_example_ndktest_MainActivity_MODE_WORLD_WRITEABLE#define com_example_ndktest_MainActivity_MODE_WORLD_WRITEABLE 2L#undef com_example_ndktest_MainActivity_MODE_APPEND#define com_example_ndktest_MainActivity_MODE_APPEND 32768L#undef com_example_ndktest_MainActivity_MODE_MULTI_PROCESS#define com_example_ndktest_MainActivity_MODE_MULTI_PROCESS 4L#undef com_example_ndktest_MainActivity_MODE_ENABLE_WRITE_AHEAD_LOGGING#define com_example_ndktest_MainActivity_MODE_ENABLE_WRITE_AHEAD_LOGGING 8L#undef com_example_ndktest_MainActivity_BIND_AUTO_CREATE#define com_example_ndktest_MainActivity_BIND_AUTO_CREATE 1L#undef com_example_ndktest_MainActivity_BIND_DEBUG_UNBIND#define com_example_ndktest_MainActivity_BIND_DEBUG_UNBIND 2L#undef com_example_ndktest_MainActivity_BIND_NOT_FOREGROUND#define com_example_ndktest_MainActivity_BIND_NOT_FOREGROUND 4L#undef com_example_ndktest_MainActivity_BIND_ABOVE_CLIENT#define com_example_ndktest_MainActivity_BIND_ABOVE_CLIENT 8L#undef com_example_ndktest_MainActivity_BIND_ALLOW_OOM_MANAGEMENT#define com_example_ndktest_MainActivity_BIND_ALLOW_OOM_MANAGEMENT 16L#undef com_example_ndktest_MainActivity_BIND_WAIVE_PRIORITY#define com_example_ndktest_MainActivity_BIND_WAIVE_PRIORITY 32L#undef com_example_ndktest_MainActivity_BIND_IMPORTANT#define com_example_ndktest_MainActivity_BIND_IMPORTANT 64L#undef com_example_ndktest_MainActivity_BIND_ADJUST_WITH_ACTIVITY#define com_example_ndktest_MainActivity_BIND_ADJUST_WITH_ACTIVITY 128L#undef com_example_ndktest_MainActivity_CONTEXT_INCLUDE_CODE#define com_example_ndktest_MainActivity_CONTEXT_INCLUDE_CODE 1L#undef com_example_ndktest_MainActivity_CONTEXT_IGNORE_SECURITY#define com_example_ndktest_MainActivity_CONTEXT_IGNORE_SECURITY 2L#undef com_example_ndktest_MainActivity_CONTEXT_RESTRICTED#define com_example_ndktest_MainActivity_CONTEXT_RESTRICTED 4L#undef com_example_ndktest_MainActivity_RESULT_CANCELED#define com_example_ndktest_MainActivity_RESULT_CANCELED 0L#undef com_example_ndktest_MainActivity_RESULT_OK#define com_example_ndktest_MainActivity_RESULT_OK -1L#undef com_example_ndktest_MainActivity_RESULT_FIRST_USER#define com_example_ndktest_MainActivity_RESULT_FIRST_USER 1L#undef com_example_ndktest_MainActivity_DEFAULT_KEYS_DISABLE#define com_example_ndktest_MainActivity_DEFAULT_KEYS_DISABLE 0L#undef com_example_ndktest_MainActivity_DEFAULT_KEYS_DIALER#define com_example_ndktest_MainActivity_DEFAULT_KEYS_DIALER 1L#undef com_example_ndktest_MainActivity_DEFAULT_KEYS_SHORTCUT#define com_example_ndktest_MainActivity_DEFAULT_KEYS_SHORTCUT 2L#undef com_example_ndktest_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL#define com_example_ndktest_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL 3L#undef com_example_ndktest_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL#define com_example_ndktest_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL 4L/* * Class: com_example_ndktest_MainActivity * Method: getText * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_example_ndktest_MainActivity_getText (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif头文件的名字我们可以通过 -o 来指定,如果不指定则默认为 {packagename.classname}.h。现在生成了头文件,头文件中申明了jni函数(看最后几行),函数的命名很长,java函数中调用的native函数就是调用的这个函数,那么接下来就是函数的实现了。
step3:jni函数的实现
新建一个.c文件,名字可以随便取,为了和前面生成的头文件对应,我新建了一个com_example_ndktest_MainActivity.c的文件,并且将其放到和.h同一个目录下,内容如下:
#include <string.h>#include <jni.h>jstring Java_com_example_ndktest_MainActivity_getText(JNIEnv *env, jobject thiz){return (*env)->NewStringUTF(env, "Hello World");}
step4:生成.so文件
我们的C文件也写完了, 接下来就是怎样生成.so文件,也就是我们的静态库文件,在java程序里面的 System.LoadLibrary("hello_world"); 就是加载在此处生成的.so文件。
在生成.so文件之前还需要新一个makefile文件,如下所示,文件名为Android.mk,也放在jni文件夹下。
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := hello_worldLOCAL_SRC_FILES := com_example_ndktest_MainActivity.cinclude $(BUILD_SHARED_LIBRARY)其实LOCAL_MODULE为我们希望生成的静态库的名称,LOCAL_SRC_FILES是要编译到的源文件,多个文件以空格隔开。
然后按win+R输入 cmd打开windows的控制终端,并进入到 jni 目录或其上一级目录,运行ndk-build命令,会看到如下的执行结果。
我们可以看到生成了名称为libhello_world.so的文件,实际程序加载时也是加载的这个静态库,只是我们书写时只需要写hello_world就可以了,剩余的工作系统会自动帮我们完成。
我们进入到C:\Users\Xxing 目录,发现除了之前在生成.h文件时生成的jni文件夹之外,编译.so文件之后还生成了另外两个文件夹,将这三个文件夹拷贝到android项目文件夹下。(实际上我们可以将我们控制台的工作目录切换到android的工作目录,这样就不用拷贝文件夹这么麻烦)。
我们在step2的时候说过,一般在生成.h头文件的时候指定文件夹名称为 jni,这是因为如果不为jni,运行ndk-build的时候则会出现"Please define the NDK_PROJECT_PATH variable to point to it" 的错误,这里我们只需要将编译的文件放入到jni文件夹即可。
或者也可以采用以下的方法,运行ndk-build并指定以下变量:
step5:重新编译android程序。
回到eclipse,重新编译android程序,并运行,则可出现如下结果:
- 第一次使用JNI
- Android JNI 第一次使用
- 第一次尝试使用 AS 调用JNI~
- 第一次调用JNI
- jni.h no such file or directory(第一次使用JNI的常见问题)
- 第一次使用
- 第一次使用
- 第一次使用
- 第一次使用
- 第一次使用
- 第一次使用
- 第一次使用
- 第一次使用
- 第一次使用
- 第一次使用
- 第一次使用
- 第一次使用
- 第一次使用
- 仿真LED数码管显示控件
- const的用法
- ZooKeeper的学习与应用
- SilverLight之利用Visifire生成柱状图、曲线图、饼状图
- 用bootstrap实现最新提示,统计,排名
- 第一次使用JNI
- Android学习笔记(五)——屏幕间的跳转和事件的传递
- [PHP]基于Redis Set处理社交图谱业务逻辑
- 【Java.NIO】NIO就绪处理之OP_CONNECT
- STM32的USB中断说明
- ubuntu下hadoop-2.2.0搭建
- (自定义View)左右滑动控件ScrollLayout
- ooziedb 初始化的时候需要执行以下语句
- 移动医疗开源方案