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 <jni.h>
/* 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、案例下载

点击这里

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349