基本流程:
1.新建Android工程
2.Java文件中创建调用接口声明代码
3.javah生成C/C++的.h文件
4.编写C/C++实现代码
5.编写Android.mk脚本
6.编写Application.mk脚本
7.使用ndk编译工程生成.so动态库
8.编写加载库的代码载入.so文件
9.调用动态库的接口方法
详细流程:
1.新建工程,并对ndk进行配置
右键选择工程属性,按照图中1,2,3步骤
双击工程Builder,选择ndk-build.cmd所在路径以及工程的工作目录
按照如图所示配置ndk执行
点击Specify Resources按钮
2.创建com.example.jni包,并创建TestJNI.java文件,声明接口,内容如下:
package com.example.jni;
public class TestJNI {
public native boolean init();
public native int add (int x, int y);
public native void destroy();
}
3.使用命令:javah -classpath bin/classes -d jni com.example.jni.TestJNI
生成jni调用的.h文件,叫com_example_jni_TestJNI.h
参数解释:
-classpath 使用类的位置
-d jni需要生成jni的类,包括包名+类名
这个.h文件里面的内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_jni_TestJNI */
#ifndef _Included_com_example_jni_TestJNI
#define _Included_com_example_jni_TestJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_jni_TestJNI
* Method: init
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_example_jni_TestJNI_init
(JNIEnv *, jobject);
/*
* Class: com_example_jni_TestJNI
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_jni_TestJNI_add
(JNIEnv *, jobject, jint, jint);
/*
* Class: com_example_jni_TestJNI
* Method: destroy
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_example_jni_TestJNI_destroy
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
4.编写实现这些接口的C++代码
首先是Add.h,代码如下:
#ifndef JNI_TEST_ADD
#define JNI_TEST_ADD
class CAdd {
public:
CAdd();
~CAdd();
int add(int x, int y);
};
#endif
然后是Add.cpp,代码如下:
#include "Add.h"
CAdd::CAdd (){
}
CAdd::~CAdd (){
}
int CAdd::add( int x, int y){
return x+y;
}
最后是com_example_jni_TestJNI.cpp,代码如下:
PS:最好先把.h的代码先复制下来,然后再进行改动,防止缺少什么。
比如缺少jni.h和extern”C”都会有相应的问题(JNI_EXPORT/Native method not found等),导致不能编译成功
#include<stdio.h>
#include <stdlib.h>
#include <jni.h>
#include "Add.h"
#ifndef _Included_com_example_jni_TestJNI
#define _Included_com_example_jni_TestJNI
#ifdef __cplusplus
extern "C" {
#endif
CAdd *pcAdd = NULL;
/*
* Class: com_example_jni_TestJNI
* Method: init
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_example_jni_TestJNI_init
(JNIEnv *env, jobject object){
if(pcAdd==NULL){
pcAdd = new CAdd;
}
return pcAdd!=NULL;
}
/*
* Class: com_example_jni_TestJNI
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_jni_TestJNI_add
(JNIEnv *env, jobject object, jint x, jint y){
jint res = -1;
if(pcAdd != NULL){
res = pcAdd->add(x,y);
}
return res;
}
/*
* Class: com_example_jni_TestJNI
* Method: destroy
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_example_jni_TestJNI_destroy
(JNIEnv *env, jobject object){
if(pcAdd != NULL){
delete pcAdd;
pcAdd = NULL;
}
}
#ifdef __cplusplus
}
#endif
#endif
5.编写Android.mk和Application.mk文件
(Android.mk和.cpp可以通过右键项目->Android Tools->Add Native Support生成)
Android.mk内容:
LOCAL_PATH := $(call my-dir) #Android.mk的目录路径
include $(CLEAR_VARS) #清理LOCAL_xxx避免相互影响
LOCAL_MODULE : = TestJNI #要生成的动态库名称
### Add all source file names to be included in lib separated by a whitespace
LOCAL_SRC_FILES := com_example_jni_TestJNI.cpp\
Add.cpp #要编译的jni和底层源码
include $(BUILD_SHARED_LIBRARY) #生成动态库
Application.mk内容:
APP_STL := gnustl_static #使用STL库
APP_CPPFLAGS += -fpermissive #忽略一些语法错误,兼容老语法使其通过
makefile文件参数的解释:
Android NDK学习 <二> Android.mk的制作
Android NDK开发指南(一) Application.mk文件
Android.mk告诉NDK Build系统关于Source的信息。Android.mk将是GNU Makefile的一部分,且将被Build System解析一次或多次。所以,尽量少的在Android.mk中声明变量,也不要假定任何东西不会在解析过程中定义。
LOCAL_PATH := $(call my-dir)
每个Android.mk文件必须以定义LOCAL_PATH为开始。它用于在开发tree中查找源文件。
宏my-dir 则由Build System提供。返回包含Android.mk的目录路径。
include $(CLEAR_VARS)
CLEAR_VARS 变量由Build System提供。并指向一个指定的GNU Makefile,由它负责清理很多LOCAL_xxx.
例如:LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES等等。但不清理LOCAL_PATH. 这个清理动作是必须的,因为所有的编译控制文件由同一个GNU Make解析和执行,其变量是全局的。所以清理后才能避免相互影响。
LOCAL_MODULE := TestJNI
LOCAL_MODULE模块必须定义,以表示Android.mk中的每一个模块。名字必须唯一且不包含空格。Build System会自动添加适当的前缀和后缀。例如,foo,要产生动态库,则生成libfoo.so.
但请注意:如果模块名被定为:libfoo.则生成libfoo.so. 不再加前缀。
LOCAL_SRC_FILES :=
com_example_jni_TestJNI.cpp\ #\和回车隔开
Add.cpp
LOCAL_SRC_FILES变量必须包含将要打包如模块的C/C++ 源码。不必列出头文件,build System 会自动帮我们找出依赖文件。
缺省的C++源码的扩展名为.cpp. 也可以修改,通过LOCAL_CPP_EXTENSION。
include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY:是Build System提供的一个变量,指向一个GNU Makefile Script。它负责收集自从上次调用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息。并决定编译为什么。
APP_STL :默认,NDK构建系统提供由Android系统给出的最小C++运行时库(/system/lib/libstdc++.so)的C++头文件。
然而,NDK带有另一个C++实现,你可以在你自己的应用程序中使用或链接它。
定义APP_STL以选择它们其中的一个:
APP_STL := stlport_static –> static STLport library
APP_STL := stlport_shared –> shared STLport library
APP_STL := system –> default C++ runtime library
APP_CFLAGS : 一个C编译器开关集合,在编译任意模块的任意C或C++源代码时传递。
它可以用于改变一个给定的应用程序需要依赖的模块的构建,而不是修改它自身的Android.mk文件
6.编译工程,生成动态链接库.so文件
一般改完会自动编译,如果没有反应可以在cmd中跳转到jni目录,使用ndk-build对工程进行编译,这一方法在Android Studio中同样适用。
7.调用.so文件,运用在Activity中
其MainActivity.java内容如下:
package com.example.androidjnitest;
import com.example.jni.TestJNI;
import android.os.Bundle;
import android.app.Activity;
import android.widget.TextView;
public class MainActivity extends Activity {
static{
System. loadLibrary("TestJNI");
}
TextView tv_test;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout. activity_main);
InitView();
TestJNI jni = new TestJNI();
jni.init();
tv_test.setText( "1+1="+jni.add(1,1));
}
void InitView(){
tv_test = (TextView) findViewById(R.id. tv_test);
}
}
将其发布到手机或者Android虚拟机上即可看见结果。