用ndk-build构建JNI
step1、用Android Studio创建一个Android工程

最终的工程结构如下:

step2、创建一个Java类,包含JNI信息
package com.fpliu.jnitest;

public class XX {

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

    public native String getText();
}

这里面特别注意:加载动态库的方式,通常是在Java类的静态代码块中,因为类加载器在加载一个类的时候, 类的静态成员和静态代码块最先执行,然后才会调用构造方法进行构造实例,之后,那些native方法才能正确执行。 当然,你也可以在其他地方加载,只是需要进行判断操作。

native修饰的方法就是要使用C/C++实现的方法,所以,它只是声明,没有方法体。

native修饰的方法也可以被其他修饰符所修饰,比如static

step3、通过javah命令生成头文件

1、切换到app/src/main/java目录:

cd ~/JNITest/app/src/main/java

2、使用javah命令生成头文件

javah -jni com.fpliu.jnitest.XX

这样就在app/src/main/java目录中生成了头文件com.fpliu.jnitest.XX.h,文件内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_fpliu_jnitest_XX */

#ifndef _Included_com_fpliu_jnitest_XX
#define _Included_com_fpliu_jnitest_XX
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_fpliu_jnitest_XX
 * Method:    getText
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_fpliu_jnitest_XX_getText
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
step4、在app/src/main/jni目录中实现

1、创建app/src/main/jni目录:

mkdir -p app/src/main/jni

2、把com.fpliu.jnitest.XX.h剪切到app/src/main/jni目录中:

mv java/com.fpliu.jnitest.XX.h jni/

3、编写实现文件xx.c,内容如下:

#include "com_fpliu_jnitest_XX.h"

JNIEXPORT jstring JNICALL Java_com_fpliu_jnitest_XX_getText (JNIEnv *env, jobject obj) {
    return (*env)->NewStringUTF(env, "Hello NDK !");
}

注意:你可以使用C实现, 也可以使用C++实现,根据自己的需要而定,但是, 语言不同,env这个变量的使用不一样喔,注意这一点!

step5、使用ndk-build进行构建

1、在app/src/main/jni目录中创建Android.mk文件,内容如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := xx
LOCAL_SRC_FILES := xx.c

include $(BUILD_SHARED_LIBRARY)

2、在app/src/main目录中执行ndk-build命令:

3、将生成的app/src/main/libs目录剪切到某个gradle模块的根目录下:

mv app/src/main/libs app/

4、配置此gradle模块中的build.gradle

android {
    ...
    defaultConfig {
        ...
        ndk {
            moduleName "xx" //生成的so名字
            abiFilters "armeabi", "armeabi-v7a", "x86"  //输出指定三种abi体系结构下的so库
        }
    }

    sourceSets.main {
        jniLibs.srcDir(['libs'])
        jni.srcDirs = [] //disable automatic ndk-build call
    }
}

jni.srcDirs = []这表示,在构建apk的过程中, 不自动使用ndk-build构建so,因为我们自己构建好了。

abiFilters "armeabi", "armeabi-v7a", "x86"这个有两方面的作用:一方面是, 当gradle自动构建so的时候,根据此生成对应平台的so, 没有设置的话,就是所有平台的so都会生成;另一方面是控制生成的apklib目录下包含哪些目录, 只有这里列出的才包含,如果这里没有设置,那就看你自己生成的CPU平台有哪些,如果没有自己生成的,那么就是全部CPU平台的都有。

这里,在实际使用的过程中要特别注意,因为你引用了别人的SDK或者库,它可能只支持某些CPU平台的, 你不得不放弃它不支持的CPU平台的话,就配置这个就行。

如果你不知道你引用的那些SDK或者库到底支持了哪些CPU平台, 构建完成apk后,解压看看lib目录下的那些个文件夹里面的so数量是否一样多, 如果不一样多,一定有问题,少的那个CPU平台在运行过程中就要崩溃了。

5、如何你是让gradle自动帮你构建你的JNI代码的, 请确保工程根目录下的local.properties中配置了正确的ndk路径:

ndk.dir=/usr/local/opt/android-ndk/android-ndk-r14b
sdk.dir=/usr/local/share/android-sdk

6、运行工程,看效果吧!