1、简介(一些相关概念)
Android NDK是一组允许你将C或C++嵌入到Android应用中的工具。可用于:
1)在平台之间移植应用,用C/C++写的库可以方便的在其他嵌入式的平台上再次使用
2)重复使用现有库,或者提供其自己的库供重复使用
3)在某些情况下提高性能,特别是像游戏这种计算密集型应用
4)代码的保护,apk的java层代码很容易被反编译,而C/C++库反汇难度教大
交叉编译:指在一个平台上生成另一个平台可执行的代码
JNI(Java Native Interface):是Java和C/C++组件用以互相沟通的接口
JNI实现流程
1编写java类代码 -->.java
2)编译成字节代码-->.class
3)产生c头文件-->.h
4)编写JNI实现代码-->.c
5)编译成链接库文字-->.dll或.os
=======================================================================
2、下载NDK
可通过官网下载:
https://developer.android.com/ndk/downloads/older_releases.html?hl=zh-cn#ndk-12b-downloads
可能需要文明上网
=======================================================================
3、配置NDK环境
把下载的NDK解压
然后依次右键我的电脑 -->属性-->更改设置-->高级-->环境变量-->在系统变量中找到PATH,在输入框中加入『;你的解压后的NDK的路径』。
比如我的NDK解压后路径为,配置为『;E:\mysoft\android-ndk-r12b-windows-x86_64\android-ndk-r12b』。
打开cmd,输入ndk-build,如果输出为如下所示,说明环境配置成功。
=======================================================================
4、开发第一个NDK项目
1)在eclipse中创建一个Android工程,在MainActivity中添加一个native方法
public static native String getStringFromC();
2)生成.h头文件
在项目中添加jni文件目录
打开cmd页面,使用cd指令跳转到你的项目路径下,比如我的:
接着在cmd中使用指令生成.h文件:javah -classpath bin/classes;D:\androidSDK\platforms\android-25\android.jar -d jni com.bajie.hellondk.MainActivity
其中『bin/classes』指要编译的class文件的路径;
『D:\androidSDK\platforms\android-25\android.jar』指androidsdk里面的一个android.jar的路径,注意『bin/classes』与『D:\androidSDK\platforms\android-25\android.jar』之间是一个『;』而不是『:』;
『jni』指生成的.h文件存放的路径,这个jni指我们刚刚添加的目录;
『com.bajie.hellondk.MainActivity』指要编译的类。
大家根据自己项目的实际情况改写以上指令,成功运行之后在eclipse中刷新一下项目,jni路径下将会生成一个.h文件
3)实现.h文件的方法
在jni目录中新建一个c文件,编写代码实现getStringFromC()方法:
#include <stdio.h>
#include <stdlib.h>
#include "com_bajie_hellondk_MainActivity.h"
JNIEXPORT jstring JNICALL Java_com_bajie_hellondk_MainActivity_getStringFromC
(JNIEnv *evn, jclass jclass) {
return (*evn)->NewStringUTF(evn, "Hello from JNI");
}
4)编写Android.mk
在jni目录中新建File,命名为Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello
LOCAL_SRC_FILES := hello.c
include $(BUILD_SHARED_LIBRARY)
其中,LOCAL_MODULE的值为要生成的.so文件的名称,LOCAL_SRC_FILES为要编译的.c文件的名称。
5)生成.so文件
在cmd中跳转到项目所在路径,然后执行ndk-build。执行完成后在eclipse中刷新项目,项目将新增一个obj目录,目录包含生成的.so文件
6)调用JNI方法
在MainActivity中使用一个静态块去加载生成的.so文件,然后用一个TextView去显示JNI方法返回的字符串。
package com.bajie.hellondk;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
static {
System.loadLibrary("hello");
}
private TextView textView;
public static native String getStringFromC();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
textView.setText(getStringFromC());
}
}
运行Android项目
自此,一个简单的ndk项目已经完成。
=======================================================================
5、在eclipse中配置ndk环境
上面开发NDK项目中,每次改变.c文件的内容,再去生成.so文件的时候,都需要打开控制台使用ndk-build命令行去生成,比较麻烦。通过配置eclipse的ndk环境,可以在运行项目的时候进行自动编译。
1)Window-->Preferences-->Android-->NDK,配置你的NDK目录
2)右键你的NDK项目-->Properties-->Builders-->New-->Program。在Location中点击BrowseFileSystem...,定位到你的ndk的一个ndk-build.cmd文件;在Working Directory中点击Browse Workspace...定位到你的项目。
然后在当前页面点选选项卡Build Options,点选During auto builds。
点击Apply-->OK。然后修改.c文件的内容,直接运行项目,无须另外运行ndk-build命令行,eclipse会自己编译生成.so文件。
=======================================================================
6、JNI交互的字符串处理
1)在第一个NDK项目的MainActivity中添加一个native方法。
public static native void updateFile(String path);
2)使用指令『javah -classpath bin/classes;D:\androidSDK\platforms\android-25\android.jar -d jni com.bajie.hellondk.MainActivity』生成.h头文件。指令的具体含义上文有介绍。
3)在.c文件中编写实现native层实现代码。
JNIEXPORT void JNICALL Java_com_bajie_hellondk_MainActivity_updateFile
(JNIEnv *env, jclass jclass, jstring path) {
// 生成native的char指针
const char * file_path = (*env)->GetStringUTFChars(env, path, NULL);
if(file_path != NULL) {
LOGV("from c file_path %s", file_path);
}
// 打开文件
FILE* file = fopen(file_path, "a+");
if(file != NULL) {
LOGV("from c open file success");
}
char data[] = "I am a girl";
// 写文件
int count = fwrite(data, strlen(data), 1, file);
if(count > 0) {
LOGV("from c write file success");
}
if(file != NULL) {
// 关闭文件
fclose(file);
}
// 释放指针
(*env) -> ReleaseStringUTFChars(env, path, file_path);
}
4)在MainActivity中调用该native方法
updateFile("/mnt/sdcard/hellondk.txt");
5)运行Android程序,成功后,打开.txt『/mnt/sdcard/hellondk.txt』文件,文件内容为『I am a girl』
=======================================================================
7、JNI交互的数组处理
以下例子步骤都和上个例子都一样,下面例子只重点给出.c文件的代码,其他步骤省略
JNI关于数组的处理主要分两种方式1)第一种方式是生成native层的数组拷贝
.c代码
JNIEXPORT jintArray JNICALL Java_com_bajie_hellondk_MainActivity_updateIntArray
(JNIEnv * env, jclass jclass, jintArray array) {
jint nativeArray[5];
// 把java层的array数据拷贝到native层的nativeArray
(*env) -> GetIntArrayRegion(env, array, 0, 5, nativeArray);
int j;
for(j = 0; j < 5; j ++) {
nativeArray[j] += 5;
LOGV("from c int %d", nativeArray[j]);
}
// 把native层的nativeArray数据拷贝到java层的array
(*env) -> SetIntArrayRegion(env, array, 0, 5, nativeArray);
return array;
}
在MainActivity中调用native方法:
int[] data = {1,2,3,4,5};
int[] result = updateIntArray(data);
for(int i : result) {
Log.i("nate", "i = " + i);
}
运行结果:
2)第二种方式是直接调用数组指针进行操作
.c代码:
JNIEXPORT jintArray JNICALL Java_com_bajie_hellondk_MainActivity_updateIntArray
(JNIEnv * env, jclass jclass, jintArray array) {
// 生成字符指针
jint *data = (*env) -> GetIntArrayElements(env, array, NULL);
jsize len = (*env) -> GetArrayLength(env, array);
int j;
for(j = 0; j < len; j ++) {
data[j] += 5;
LOGV("from c int %d", data[j]);
}
// 释放资源
(*env) -> ReleaseIntArrayElements(env, array, data, 0);
return array;
}
=======================================================================
8、配置在eclipse中生成native方法对应的.h文件
在我们之前的使用中,都是使用javah命令行来生成.h文件,现在通过配置,可以让eclipse直接生成.h文件
Run-->ExternalTools->External Tools Configurations-->右键Program->New,输入如下:
其中Location:${system_path:javah}
Working Directory:${project_loc}\jni
Arguments:-classpath "${project_loc}\bin\classes;D:\androidSDK\platforms\android-19\android.jar" -d "${project_loc}\jni" ${java_type_name}
然后点击你要生成.c文件对应的.java文件,再点击如下:
刷新项目,jni目录下会生成一个对应的.h文件,测试成功。
=======================================================================
9、在eclipse中编写C++代码时展开提示
1)右键项目-->Android Tools-->Add Native Support-->Finish
2)右键项目-->Properties-->C/C++ General-->Paths and Symbols-->Add-->File system-->定位到你的ndk的platforms\任意一个android版本\arch-arm\usr\include-->按照提示完成配置。