上篇文章中已经介绍了,关于NDK开发环境的搭建,这里不做赘述。这篇文章主要是通过一个例子来说明如何通过eclispe自动生成.h头文件,适合初学者。本篇文章主要是参考:http://blog.csdn.net/super_level/article/details/21243533。
1 创建含有本地代码的Android Project
该过程分为以下两步:
- 创建普通的Android Application工程(注意最小支持的API版本要不小于14)
- 加入本地代码支持 目的是可以自动生成.so文件,以便在MainActivitiy中能够加载库文件从而调用c/c++
加入本地代码支持:
名字选择默认的TestNDK就可以了。
完成结果:
2 编写java端代码和c++端代码
2.1 java端代码
Java端,注意不要继承自Android中的类,否则javah编译头文件时要指定android类路径。
在NativeClass.java文件中:
package com.example.testndk;
import android.util.Log;
public class NativeClass {
//数组a中的每个元素都加上b,返回值为在C++中数据是否为a中数据拷贝得到的(按值拷贝还是传递指针)
public static native boolean jniArrayAdd(int[] a, int b); //加
// 在C++中创建Java中的int数组,其中元素为 数组a中的对应元素乘以b
public static native int[] jnitArrayMul(int[] a,int b); //乘以
static {
Log.i("NativeClass","before load library");
System.loadLibrary("TestNDK");//注意这里为自己指定的.so文件,无lib前缀,亦无后缀
Log.i("NativeClass","after load library");
}
}
Java端调用JNI方法的代码:
将MainActivity改为:
package com.example.testndk;
import java.util.Arrays;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = new TextView(this);
int[] array = new int[]{1,2,3};
String str = "数组,调用C++前" + Arrays.toString(array);//将数组转成字符串
boolean isCopyOfArrayInCpp = NativeClass.jniArrayAdd(array, 1);
System.out.println(isCopyOfArrayInCpp);//然后java在print的时候都是调用对象的toString方法就会把o改成false输出
str += "\n 在c++中为副本? " + isCopyOfArrayInCpp;
str += "\n数组,调用C++后:" + Arrays.toString(array);
tv.setText(str);
setContentView(tv);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
2.2 编写c++代码,实现java中native函数
#include <jni.h>
#include "com_example_testndk_NativeClass.h"
#include <android/log.h>
#ifdef __cplusplus
extern "C"{
#endif
JNIEXPORT jboolean JNICALL Java_com_example_testndk_NativeClass_jniArrayAdd
(JNIEnv * env, jclass, jintArray array, jint b){
jsize size = env ->GetArrayLength(array);
jboolean isCopy = false;
jint *pArray = (jint*) env ->GetPrimitiveArrayCritical(array,&isCopy);
for(int i = 0; i < size ; i++){
pArray[i] += b;
}
env->ReleasePrimitiveArrayCritical(array,pArray,JNI_COMMIT);//JNI_COMMIT 对java数组进行更行,但是不释放java数组
__android_log_print(ANDROID_LOG_DEBUG, "frank", "isCopy= %d",isCopy);//输出的的isCopy= 0;说明是false,0是false也是Null(string 输出的时候)
return isCopy;
}
#ifdef __cplusplus
}
#endif
如果要log输出的话,android.mk里面需要配置:
添加:LOCAL_LDLIBS := -landroid -llog {这个库文件}
3 配置自动生成头文件:
3.1 对每一个程序配置javah命令
配置如下:
里面需要写的分别是:
C:\Program Files\Java\jdk1.7.0_51\bin\javah.exe
${workspace_loc:/WifiIp}
-classpath src -d jni com.example.wifiip.MainActivity(注意这种写法是在MainAvitvity里面去声明native方法的时候写的一种格式)
然后再run里面执行 wifiip_javah 就可以了(这里并不是TestNDK这个工程,换了另外一个工程)
3.2 通用的头文件生成配置如下
如果每一个程序都像上面一样去配置是不是会很麻烦,所以就有一种通用的配置:
需要填的分别是:
Generate C and C++ Header
C:\Program Files\Java\jdk1.7.0_51\bin\javah.exe
${project_loc}/jni
-classpath "${project_classpath};${env_var:ANDROID_SDK_HOME}/platforms/android-14/android.jar"${java_type_name}
注意这个ANDROID_SDK_HOME,需要在path环境变量里面去配置:
在尾部加上D:\program\studyprogram\adt-bundle-windows-x86-20130219\sdk;(你的SDK的路径)
然后配置好之后。就可以用这个命令去生成.h头文件了,注意生成的时候一定要:
4 配置生成的.so文件的目标平台
ava是跨平台的可是C++生成动态链接文件不是!!!同是Android,底层的CPU架构不同,动态链接文件也不同。。。好吧,这个我不知道原因。。。
于是乎,还得为不同的CPU创建不同的动态链接库文件,好在一行命令搞定~所有的动态链接一起打包,管他是哪个CPU,统统适用,Happy啊。
再编译时在libs下面就会生成三种平台下的.so文件。
5 结果
后续:要进行对jni层进行调试。比较麻烦。等用到的时候再去学习吧。
这样就把整个java层调用c/c++层说明了。