Android:通过CMake方式生成动态库so文件

1、前言

Java JNI的本意是Java Native Interface(Java本地接口),它是为了方便Java调用CC++等本地代码所封装的一层接口。通过Java JNI,用户可以调用用CC++所编写的本地代码
NDKAndroid所提供的一个工具集合,通过NDK可以在Android中更加方便地通过JNI来访问本地代码。

2、优势

  • 提高代码的安全性。由于so库反编译比较困难,因为NDK提高了Android程序的安全性。
  • 可以很方便地使用目前已有的C/C++开源库
  • 便于平台间的移植。
  • 提高程序在某些特定情形下的执行效率,但是并不能提升Android程序性能

注:JNINDK开发所用到的动态库的格式是以.so为后缀的文件,JNINDK主要用于底层和嵌入式开发,在Android应用层开发中使用比较少。

3、JNI开发流程

3.1、在Android Studio配置NDK环境

打开SDKManager-tools下载NDK插件,下载后到SDK Location里面检查里面的NDK路径

3.2、在Java中声明native方法**

创建一个类,叫做JNITest.java

package com.qinkl;
public class JNITest{
    static{
        System.loadLibrary("jni-test");
    }

    public static void main(String args[]){
        JNITest jniTest = new JNITest();
        System.out.println(jniTest.jniGet());
        jniTest.jniSet("hello world");
    }

    public native String jniGet();
    public native void jniSet(String str);

}

可以看到上面的代码中,声明了两个native方法:jniGetjniSet,这两个就是需要在JNI中实现的方法。在JniTest的头部有一个加载动态库的静态代码块,其中jni-testso库的标识,so库完整的名称为libjni-test.so,这是加载so库的规范

3.3、编译Java源文件得到class文件,然后通过javah命令导出JNI的头文件

进入cmd,(cd /d 任意目录),选择进入项目文件目录,具体的命令为:

javac com/qinkl/JNITest.java
javah com.qinkl.JNITest

JDK10以前用:javah com.qinkl.JNITest
JDK10以后是没有提供javah的,要用javac代替javah命令:进入cmd,切换到文件所在的根目录,执行命令javac -encoding utf8 -h . JNITest.java

在当前目录下,会产生com_qinkl_JNITest.h的头文件,它是上述命令生成的,内容如下:

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

#ifndef _Included_com_qinkl_JNITest
#define _Included_com_qinkl_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_qinkl_JNITest
 * Method:    jniGet
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_qinkl_JNITest_jniGet
  (JNIEnv *, jobject);

/*
 * Class:     com_qinkl_JNITest
 * Method:    jniSet
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_qinkl_JNITest_jniSet
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

JNIEnv*:表示一个指向JNI环境的指针,可以通过它来访问JNI提供的接口方法
jobect:表示Java对象中的this
JNIEXPORTJNICALL:他们是JNI中所定义的宏,可以在JNI.h这个头文件中查找到

3.4、实现JNI方法

JNI方法是指Java中声明的native方法,这里可以选择用c++c来实现。
首先,在工程的主目录下创建一个名为jni的子目录,把之前生成的头文件com_qinkl_JNITest复制进来,接着创建test.cpp文件,内容如下:

#include "com_qinkl_JNITest.h"

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

JNIEXPORT void JNICALL Java_com_qinkl_JNITest_jniSet(JNIEnv *env,jobject thiz,jstring string){
    printf("invoke set in c++\n");
    char* str = (char*)(*env)->GetStringUTFChars(env,string,NULL);
    printf("%s\n",str);
    (*env)->ReleaseStringUTFChars(env,string,str);
}

3.5、生成so库

gradle3.0以前生成的方式是,在根目录gradle.properties下面加上:

android.useDeprecatedNdk=true

然后在项目build.gradledefaultConfig节点下,添加代码:

ndk{
    moduleName "jni-test"//指定生成的so文件名
    abiFilters "armeabi","armeabi-v7a","x86"//CPU的类型
}

这两步就可以运行生成so库了
但如果在gradle 3.0之后,已经不支持这样的生成方式了,会报错,内容如下

Error: Flag android.useDeprecatedNdk is no longer supported and will be removed in the next version of Android Studio. 

3.6、在gradle3.0以上的构建方式

  • 首先先到SDKManager->SDK Tools,下载CMakeLLDB
  • 在项目build.gradledefaultConfig节点下,添加代码
externalNativeBuild{
    cmake{
        cppFlags ""
        //生成多个版本的so库
        abiFilters 'armeabi-v7a','arm64-v8a'
    }
}
  • 在项目build.gradleandroid节点下,添加代码
externalNativeBuild{
    cmake{
        path "CMakeLists.txt"//编译后so文件的名字
    }
}
  • 在项目app目录下新建CMakeLists.txt文件,内容如下
# For more information about using CMake with Android Studio, read the
#documentation: https://d.android.com/studio/projects/add-native-code.html
#Sets the minimum version of CMake required to build the native library.
#CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)

#Creates and names a library, sets it as either STATIC
#or SHARED, and provides the relative paths to its source code.
#You can define multiple libraries, and CMake builds them for you.
#Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
    # 设置so文件名称.
    jni-test
    # Sets the library as a shared library.
    SHARED
    # 设置这个so文件为共享.
    # Provides a relative path to your source file(s).
    # 设置这个so文件为共享.
    src/main/jni/test.c)

#Searches for a specified prebuilt library and stores the path as a
#variable. Because CMake includes system libraries in the search path by
#default, you only need to specify the name of the public NDK library
#you want to add. CMake verifies that the library exists before
#completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )

#Specifies libraries CMake should link to your target library. You
#can link multiple libraries, such as libraries you define in this
#build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
    # 制定目标库.
    jni-test
    # Links the target library to the log library
    # included in the NDK.
    ${log-lib} )
  • 点击build构建一下,可能会出现一下问题
executing external native build for cmake

解决办法:将gradle的版本3.1.2改成3.2.1,我这边就解决了,再点击build构建一下,就可以在app/build/cmake/debug/obj路径下看到生成的so库了,大功告成!

4、案例下载

点击这里

你可能感兴趣的:(Android:通过CMake方式生成动态库so文件)