Android:通過CMake方式生成動態(tài)庫so文件

1阵翎、前言

Java JNI的本意是Java Native Interface(Java本地接口),它是為了方便Java調(diào)用C殃恒、C++等本地代碼所封裝的一層接口模蜡。通過Java JNI,用戶可以調(diào)用用C佑菩、C++所編寫的本地代碼
NDKAndroid所提供的一個工具集合盾沫,通過NDK可以在Android中更加方便地通過JNI來訪問本地代碼。

2殿漠、優(yōu)勢

  • 提高代碼的安全性赴精。由于so庫反編譯比較困難,因為NDK提高了Android程序的安全性绞幌。
  • 可以很方便地使用目前已有的C/C++開源庫
  • 便于平臺間的移植蕾哟。
  • 提高程序在某些特定情形下的執(zhí)行效率,但是并不能提升Android程序性能

注:JNINDK開發(fā)所用到的動態(tài)庫的格式是以.so為后綴的文件莲蜘,JNINDK主要用于底層和嵌入式開發(fā)谭确,在Android應(yīng)用層開發(fā)中使用比較少。

3票渠、JNI開發(fā)流程

3.1逐哈、在Android Studio配置NDK環(huán)境

打開SDKManager-tools下載NDK插件,下載后到SDK Location里面檢查里面的NDK路徑

3.2问顷、在Java中聲明native方法**

創(chuàng)建一個類昂秃,叫做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中實現(xiàn)的方法。在JniTest的頭部有一個加載動態(tài)庫的靜態(tài)代碼塊肠骆,其中jni-testso庫的標(biāo)識算途,so庫完整的名稱為libjni-test.so,這是加載so庫的規(guī)范

3.3哗戈、編譯Java源文件得到class文件郊艘,然后通過javah命令導(dǎo)出JNI的頭文件

進入cmd,(cd /d 任意目錄),選擇進入項目文件目錄唯咬,具體的命令為:

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

JDK10以前用:javah com.qinkl.JNITest
JDK10以后是沒有提供javah的纱注,要用javac代替javah命令:進入cmd,切換到文件所在的根目錄,執(zhí)行命令javac -encoding utf8 -h . JNITest.java

在當(dāng)前目錄下,會產(chǎn)生com_qinkl_JNITest.h的頭文件胆胰,它是上述命令生成的狞贱,內(nèi)容如下:

/* 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環(huán)境的指針,可以通過它來訪問JNI提供的接口方法
jobect:表示Java對象中的this
JNIEXPORTJNICALL:他們是JNI中所定義的宏蜀涨,可以在JNI.h這個頭文件中查找到

3.4瞎嬉、實現(xiàn)JNI方法

JNI方法是指Java中聲明的native方法,這里可以選擇用c++c來實現(xiàn)厚柳。
首先氧枣,在工程的主目錄下創(chuàng)建一個名為jni的子目錄,把之前生成的頭文件com_qinkl_JNITest復(fù)制進來别垮,接著創(chuàng)建test.cpp文件,內(nèi)容如下:

#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節(jié)點下,添加代碼:

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

這兩步就可以運行生成so庫了
但如果在gradle 3.0之后碳想,已經(jīng)不支持這樣的生成方式了烧董,會報錯,內(nèi)容如下

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

3.6胧奔、在gradle3.0以上的構(gòu)建方式

  • 首先先到SDKManager->SDK Tools逊移,下載CMakeLLDB
  • 在項目build.gradledefaultConfig節(jié)點下,添加代碼
externalNativeBuild{
    cmake{
        cppFlags ""
        //生成多個版本的so庫
        abiFilters 'armeabi-v7a','arm64-v8a'
    }
}
  • 在項目build.gradleandroid節(jié)點下,添加代碼
externalNativeBuild{
    cmake{
        path "CMakeLists.txt"http://編譯后so文件的名字
    }
}
  • 在項目app目錄下新建CMakeLists.txt文件龙填,內(nèi)容如下
# 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.
    # 設(shè)置so文件名稱.
    jni-test
    # Sets the library as a shared library.
    SHARED
    # 設(shè)置這個so文件為共享.
    # Provides a relative path to your source file(s).
    # 設(shè)置這個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.
    # 制定目標(biāo)庫.
    jni-test
    # Links the target library to the log library
    # included in the NDK.
    ${log-lib} )
  • 點擊build構(gòu)建一下胳泉,可能會出現(xiàn)一下問題
executing external native build for cmake

解決辦法:將gradle的版本3.1.2改成3.2.1,我這邊就解決了岩遗,再點擊build構(gòu)建一下扇商,就可以在app/build/cmake/debug/obj路徑下看到生成的so庫了,大功告成!

4喘先、案例下載

點擊這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钳吟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子窘拯,更是在濱河造成了極大的恐慌红且,老刑警劉巖坝茎,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異暇番,居然都是意外死亡嗤放,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門壁酬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來次酌,“玉大人,你說我怎么就攤上這事舆乔≡婪” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵希俩,是天一觀的道長吊宋。 經(jīng)常有香客問我,道長颜武,這世上最難降的妖魔是什么璃搜? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮鳞上,結(jié)果婚禮上这吻,老公的妹妹穿的比我還像新娘。我一直安慰自己篙议,他們只是感情好唾糯,可當(dāng)我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著涡上,像睡著了一般趾断。 火紅的嫁衣襯著肌膚如雪拒名。 梳的紋絲不亂的頭發(fā)上吩愧,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機與錄音增显,去河邊找鬼雁佳。 笑死,一個胖子當(dāng)著我的面吹牛同云,可吹牛的內(nèi)容都是我干的糖权。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼炸站,長吁一口氣:“原來是場噩夢啊……” “哼星澳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起旱易,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤禁偎,失蹤者是張志新(化名)和其女友劉穎腿堤,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體如暖,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡笆檀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了盒至。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酗洒。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖枷遂,靈堂內(nèi)的尸體忽然破棺而出樱衷,到底是詐尸還是另有隱情,我是刑警寧澤酒唉,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布箫老,位于F島的核電站,受9級特大地震影響黔州,放射性物質(zhì)發(fā)生泄漏耍鬓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一流妻、第九天 我趴在偏房一處隱蔽的房頂上張望牲蜀。 院中可真熱鬧,春花似錦绅这、人聲如沸涣达。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽度苔。三九已至,卻和暖如春浑度,著一層夾襖步出監(jiān)牢的瞬間寇窑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工箩张, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留甩骏,地道東北人。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓先慷,卻偏偏與公主長得像饮笛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子论熙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,630評論 2 359