JNI和NDK编程

来源:互联网 发布:企业app建站源码 编辑:程序博客网 时间:2024/06/06 05:21

(一) 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.javajavah 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 cpphello 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 __cplusplusextern "C" {#endifvoid 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-testLOCAL_SRC_FILES := test.cppinclude $(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~~~
说明调用成功。