深入浅出Android NDK之Hello-JNI

目录
上一篇 深入浅出Android NDK之将Android.mk导入Android Studio

通过以上几章的介绍,相信你已经完全了解了怎么使用ndk-build来编译C/C++源文件了。
终于可以开始写代码了,要想在Java中调用C/C++函数,需要通过以下几步:
新建一个java类,在类中声明native函数。
java类中使用静态代码块调用System.loadLibrary()加载SO。
通过javah生成C头文件。
编译C/C++代码,实现头文件中的函数。
ndk-build编译,生成SO。
下面我们来看一个实例,首先新建一个名为hello-jni的android工程:
在这里插入图片描述
然后新建一个类Test.java:

package com.example.hello_jni;
import android.graphics.Bitmap;

public class Test {
    static {
        System.loadLibrary("hello-jni");
    }
    public        native int connectV1(byte p1, char p2, short p3, int p4, long p5, String p6, Bitmap p7);
    public static native int connectV2(byte p1, char p2, short p3, int p4, long p5, String p6, Bitmap p7);
}

Test.java中有connectV1和connectV2两个方法,两个方法的参数完全一样,唯一区别在于connectV2静态方法,而connectV2是实例方法。
方法前面的native关键字表示这是一个native方法,native方法在C/C++代码中实现,所以native方法是没有方法体的,直接以分号结尾。
Test.java中还有一个static静态块,静态块中调用了System.loadLibrary(“hello-jni”);运行这段代码,系统会尝试加载libhello-jni.so,如果找不到会抛异常。libhello-jni.so是我们一会要生成的so,里面有native方法的实现。

如果不出意外,Test.java应该已经编译为Test.class了,查看以下文件夹:
在这里插入图片描述
如果Test.class没有生成,就点Android Studio的那个锤子编译一下。
下面我们打开控制台运行以下命令:

C:\Users\zy>d:

D:\>cd D:\ndk-demo\hello-jni\app\build\intermediates\javac\debug\classes

D:\ndk-demo\hello-jni\app\build\intermediates\javac\debug\classes>javah -classpath ".;C:\Users\zy\AppData\Local\Android\sdk\platforms\android-19\Android.jar" com.example.hello_jni.Test

D:\ndk-demo\hello-jni\app\build\intermediates\javac\debug\classes>


运行完成后,可以看到在D:\ndk-demo\hello-jni\app\build\intermediates\javac\debug\classes目录下生成了一个文件com_example_hello_jni_Test.h的头文件。
javah命令可以用来生成java native的头文件。
-classpath参数用于指定类搜索路径,.代表当前目录,因为我们之前已经cd到了D:\ndk-demo\hello-jni\app\build\intermediates\javac\debug\classes,所以当前目录就是D:\ndk-demo\hello-jni\app\build\intermediates\javac\debug\classes,指定这个目录是为了让javah能够找到类com.example.hello_jni.Test,因为Test类中用到了android.graphics.Bitmap类,这个类在Android.jar中,所以要把Android.jar也加入classpath。classpath多条路径之间在windows系统上以分号隔开,在linux和mac上以冒号隔开。
com.example.hello_jni.Test是类的名字,类的名字必须是包名加类名,不能只写一个类名Test。

我们打开com_example_hello_jni_Test.h查看一下他的内容:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_hello_jni_Test */

#ifndef _Included_com_example_hello_jni_Test
#define _Included_com_example_hello_jni_Test
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_hello_jni_Test
 * Method:    connectV1
 * Signature: (BCSIJLjava/lang/String;Landroid/graphics/Bitmap;)I
 */
JNIEXPORT jint JNICALL Java_com_example_hello_1jni_Test_connectV1
  (JNIEnv *, jobject, jbyte, jchar, jshort, jint, jlong, jstring, jobject);

/*
 * Class:     com_example_hello_jni_Test
 * Method:    connectV2
 * Signature: (BCSIJLjava/lang/String;Landroid/graphics/Bitmap;)I
 */
JNIEXPORT jint JNICALL Java_com_example_hello_1jni_Test_connectV2
  (JNIEnv *, jclass, jbyte, jchar, jshort, jint, jlong, jstring, jobject);

#ifdef __cplusplus
}
#endif
#endif

Java_com_example_hello_1jni_Test_connectV1函数对应Test.java的com.example.hello_jni.Test的connectV1函数。
Java_com_example_hello_1jni_Test_connectV2函数对应Test.java的com.example.hello_jni.Test的connectV2函数。
下面我们观察Java_com_example_hello_1jni_Test_connectV1和Java_com_example_hello_1jni_Test_connectV2参数。
第一个参数都是JNIEnv。
第二个参数有些不一样,一个是jobject,一个是jclass。对于实例方法connectV1,第二个参数jobject参数其实对应的是Test类的this对像。对于静态方法connectV2,第二个参数其实对应的是其类对像。
后面的7个参数分别和java中的7个参数一一对应。
jbyte, jchar, jshort, jint, jlong是jni中的5种数据类型,分别对应java中的byte,char,short,int,long。
jni对String做了特别支持,String对应jstring。
除此之外所有的java对像都对应于jobject。

我们新建一个文件夹D:\ndk-demo\hello-jni\app\src\main\jni,将com_example_hello_jni_Test.h复制到此目录。
新建文件D:\ndk-demo\hello-jni\app\src\main\jni\com_example_hello_jni_Test.cpp
,内容如下:

#include "com_example_hello_jni_Test.h"
JNIEXPORT jint JNICALL Java_com_example_hello_1jni_Test_connectV1
  (JNIEnv *env, jobject thiz, jbyte p1, jchar p2, jshort p3, jint p4, jlong p5, jstring p6, jobject p7) {
    return 1;
  }

JNIEXPORT jint JNICALL Java_com_example_hello_1jni_Test_connectV2
  (JNIEnv *env, jclass cls, jbyte p1, jchar p2, jshort p3, jint p4, jlong p5, jstring p6, jobject p7) {
     return 2;
  }

新建D:\ndk-demo\hello-jni\app\src\main\jni\Android.mk内容如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := com_example_hello_jni_Test.cpp

include $(BUILD_SHARED_LIBRARY)

将Android.mk加入build.gradle,参考文章:
深入浅出Android NDK之将Android.mk导入Android Studio

基本工作完成,下面我们在MainActivity中调用一下Test的connectV1和connectV2函数,然后运行程序。
在这里插入图片描述
我们再来将上述程疏理一遍整个过程:
Test test = new Test();运行这段代码之前虚拟机首先需要加载Test类,加载类的时候会调用static静态代码块,静态代码块调用了 System.loadLibrary(“hello-jni”);加载了libhello-jni.so。
test.connectV1((byte)0, ‘0’, (short)0,0,0,null, null);运行这段代码的时候,虚拟机首先从libhello-jni.so中查找Java_com_example_hello_1jni_Test_connectV1函数,然后调用函数Java_com_example_hello_1jni_Test_connectV1(JNIEnv, test,(jbyte)0,(jchar) ‘0’, (jshort)0,(jint)0,(jlong)0,(jstring)null, (jobject)null);

下一篇 深入浅出Android NDK之java.lang.UnsatisfiedLinkError

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. linux下jni环境搭建 参考:http://blog.csdn.net/zhouyuanjing/article/details/7553706 2. 编写HelloJni工程,在主Activity(本例:HelloJni.java)里声明native函数: 如下: public native String stringFromJNI(); public native double add(double a, double b); public native double sub(double a, double b); public native double multi(double a, double b); public native double div(double a, double b); static { System.loadLibrary("hello-jni"); } 3. 在根目录下创建 jni 目录(mkdir jni). 4. 利用命令生成相应的头文件,在根目录下执行:javah -classpath bin/classes -d jni com.xxx.hello.HelloJni ————————————— ——————— ^ ^ 包名 类名 5. 编写相应的.c文件(hello-jni.c) #include<string.h> #include<jni.h> JNIEXPORT jstring JNICALL Java_com_xxx_hello_HelloJni_stringFromJNI(JNIEnv *env, jobject obj) { return(*env)->NewStringUTF(env, "Hello World from JNI !"); } JNIEXPORT jdouble JNICALL Java_com_xxx_hello_HelloJni_add(JNIEnv *env, jobject obj, jdouble a, jdouble b) { return a + b; } JNIEXPORT jdouble JNICALL Java_com_xxx_hello_HelloJni_sub(JNIEnv *env, jobject obj, jdouble a, jdouble b) { return a - b; } JNIEXPORT jdouble JNICALL Java_com_xxx_hello_HelloJni_multi(JNIEnv *env, jobject obj, jdouble a, jdouble b) { return a * b; } JNIEXPORT jdouble JNICALL Java_com_xxx_hello_HelloJni_div(JNIEnv *env, jobject obj, jdouble a, jdouble b) { return a / b; } 6. jni目录下编写Android.mk文件 LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello-jni LOCAL_SRC_FILES := hello-jni.c include $(BUILD_SHARED_LIBRARY) 在根目录下运行:ndk-build xxx@xion-driver:~/android_env/eclipse/Workspace/HelloJni$ndk-build Install : libhello-jni.so => libs/armeabi/libhello-jni.so 可以看到已经正确的生成了libhello-jni.so共享库了。 7. Eclipse运行该工程即可。 ~~完~~

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值