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

JNI和NDK编程

2017-07-07 10:21 162 查看
(一) JNI

1.创建JniTest.java类

public class JniTest {

static {
System.loadLibrary("jnidemo");
}

public static void main(String[] args) {
JniTest jt = new JniTest();
System.out.println(jt.get());
jt.set("hello world!!!");
}

public native void set(String string);
public native String get();
}
2.依次执行:
javac JniTest.java
javah JniTest
分别完成编译,以及生成头文件。

3.实现头文件JniTest.h中声明的方法,保存为test.cpp

#include "JniTest.h"
#include <stdio.h>

JNIEXPORT jstring JNICALL Java_JniTest_get (JNIEnv *env, jobject thiz){
printf("invoke get in cpp \n");
return env->NewStringUTF("Hello from JNI~");
}

JNIEXPORT void JNICALL Java_JniTest_set (JNIEnv *env, jobject thiz, jstring string){
printf("invoke set from cpp\n");
char* str = (char*)env->GetStringUTFChars(string, NULL);
printf("%s\n", str);
env->ReleaseStringUTFChars(string, str);
}
4.执行以下命令
Mac OS:

g++ -dynamiclib -I /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include -I /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/darwin test.cpp -o libjnidemo.jnilib


Linux/Windows:

gcc -shared -I /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include -I /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/darwin -fPIC test.cpp -o libjnidemo.so


生成对应的动态库文件,-I /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include用于指定jni.h的路径,-I /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/darwin指定jni_md.h的路径。

5.运行

java -Djava.library.path=. JniTest
-Djava.library.path用于指定动态库的路径。
运行结果:

invoke get in cpp
Hello from JNI~
invoke set from cpp
hello world!!!


(二) NDK

1.创建一个Android项目,声明所需要的native方法

public class MainActivity extends Activity {

static {
System.loadLibrary("jni-test");
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Log.e("weilei", get());
set("Hello world from Demo---");
}

public native String get();
public native void set(String string);
}
2.实现Android项目中的native方法

在外部创建一个名为jni的目录(这里的目录名必须为jni),然后在jni目录下创建3个文件:test.cpp、Android.mk、Application.mk,它们的实现如下。

test.cpp

#include <jni.h>
#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

void Java_com_example_weilei_demoapp_activities_MainActivity_set(JNIEnv *env, jobject thiz, jstring string) {
printf("invoke set from cpp\n");
char* str = (char*)env->GetStringUTFChars(string, NULL);
printf("%s\n", str);
env->ReleaseStringUTFChars(string, str);
}

jstring Java_com_example_weilei_demoapp_activities_MainActivity_get(JNIEnv *env, jobject thiz) {
printf("invoke get in cpp \n");
return env->NewStringUTF("Hello from JNI~");
}

#ifdef __cplusplus
}
#endif


Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := jni-test
LOCAL_SRC_FILES := test.cpp

include $(BUILD_SHARED_LIBRARY)


Application.mk

APP_ABI := ARMEABI
LOCAL_MODULE代表模块的名称,LOCAL_SRC_FILES表示需要参与编译的源文件。APP_ABI表示CPU的架构平台类型。

4.切换到jni的父目录,执行ndk-build生成so库

这个时候NDK会创建一个与jni目录平级的目录libs,里面放的就是生成的so库。

5.然后在app/src/main中创建一个名为jniLibs的目录,将lib下的armeabi目录连同armeabi里的so库一起复制到jniLibs目录中,然后运行即可。

运行效果:

07-07 22:19:34.867 9057-9057/com.example.weilei.demoapp E/weilei: Hello from JNI~
说明连接成功。

在上面的步骤中需要将NDK编译的so库放置到jniLib中,如果想放到其他目录,可以按照如下方式修改App的build.gradle文件。

android {
...
sourceSets.main {
jniLibs.srcDir 'libs'
}
}
这样的话,就将DemoApp/app/libs/指定为新的存放so库的目录。

除了手动使用ndk-build创建so库,还可以通过AndroidStudio来自动编译产生so库。首先在App的build.gradle的defaultConfig中添加NDK选项,其中moduleName指定了模块的名称,即so库的名称。

android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.example.weilei.demoapp"
minSdkVersion 15
targetSdkVersion 22
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
ndk {
moduleName "jni-test"
}
}
}
接着讲JNI代码放到app/src/main/jni目录下(目录必须为jni),如果不想采用jni这个名字,可以通过如下方式指定:

android {
...
sourceSets.main {
jni.srcDirs 'src/main/jni_test'
}
}


最后在gradle.properties中添加android.useDeprecatedNdk=true

经过上面的步骤,AndroidStudio就可以自动编译JNI代码了,但是这个时候AndroidStudio会把所有CPU平台的so库都打包到apk中。按照如下方式修改build.gradle的位置,然后在执行gradle assembleArmDebug编译即可。

android {
...
productFlavors {
arm {
ndk {
abiFilter "armeabi"
}
}
x86 {
ndk {
abiFilter "x86"
}
}
}
}

(三) JNI调用Java的方法

1.接着NDK的这个例子,在MainActivity中创建一个方法,如下:

public static void printLog(String string) {
Log.e("MainActivity", string);
}
2.然后在test.cpp中创建一个调用这个方法的方法:

void CallJavaMethod(JNIEnv *env, jobject thiz) {
jclass clazz = env->FindClass("com/example/weilei/demoapp/activities/MainActivity");
if (clazz == NULL) {
printf("Find class MainActivity error!");
return;
}

jmethodID id = env->GetStaticMethodID(clazz, "printLog", "(Ljava/lang/String;)V");
if (id == NULL) {
printf("find method error!");
}
jstring msg = env->NewStringUTF("msg from JNI~~~");
env->CallStaticVoidMethod(clazz, id, msg);
}
这个过程首先根据完整的类名获取MainActivity的jclass对象,然后获取方法id,最后调用env.CallStaticMethod()方法来调用java中的方法。

3.为了查看是否调用成功,我们在test.cpp的get方法中添加对CallJavaMethod()的调用:

jstring Java_com_example_weilei_demoapp_activities_MainActivity_get(JNIEnv *env, jobject thiz) {
printf("invoke get in cpp \n");
CallJavaMethod(env, thiz);
return env->NewStringUTF("Hello from JNI~");
}


运行查看结果:
07-08 11:02:25.270 2732-2732/com.example.weilei.demoapp E/MainActivity: msg from JNI~~~
说明调用成功。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: