Carson帶你學(xué)Android:JNI 與 NDK到底是什么?(含實(shí)例教學(xué))


前言

  • Android開發(fā)中,使用 NDK開發(fā)的需求正逐漸增大
  • 但很多人卻搞不懂 JNINDK 到底是怎么回事
  • 今天,我將先介紹JNINDK & 之間的區(qū)別,手把手進(jìn)行 NDK的使用教學(xué),希望你們會(huì)喜歡

目錄

目錄

1. JNI介紹

1.1 簡(jiǎn)介

  • 定義:Java Native Interface,即 Java本地接口
  • 作用: 使得Java 與 本地其他類型語言(如C、C++)交互

即在 Java代碼 里調(diào)用 C、C++等語言的代碼 或 C、C++代碼調(diào)用 Java 代碼

  • 特別注意:
    1. JNIJava 調(diào)用 Native 語言的一種特性
    2. JNI 是屬于 Java 的,與 Android 無直接關(guān)系

1.2 為什么要有 JNI

  • 背景:實(shí)際使用中,Java 需要與 本地代碼 進(jìn)行交互
  • 問題:因?yàn)?Java 具備跨平臺(tái)的特點(diǎn),所以Java 與 本地代碼交互的能力非常弱
  • 解決方案: 采用 JNI特性 增強(qiáng) Java 與 本地代碼交互的能力

1.3 實(shí)現(xiàn)步驟

  1. Java中聲明Native方法(即需要調(diào)用的本地方法)
  2. 編譯上述 Java源文件javac(得到 .class文件)
  3. 通過 javah 命令導(dǎo)出JNI的頭文件(.h文件)
  4. 使用 Java需要交互的本地代碼 實(shí)現(xiàn)在 Java中聲明的Native方法

Java 需要與 C++ 交互,那么就用C++實(shí)現(xiàn) JavaNative方法

  1. 編譯.so庫文件
  2. 通過Java命令執(zhí)行 Java程序,最終實(shí)現(xiàn)Java調(diào)用本地代碼

更加詳細(xì)過程請(qǐng)參考本文第4節(jié):具體使用


2. NDK介紹

2.1 簡(jiǎn)介

  • 定義:Native Development Kit,是 Android的一個(gè)工具開發(fā)包

NDK是屬于 Android 的,與Java并無直接關(guān)系

  • 作用:快速開發(fā)C、 C++的動(dòng)態(tài)庫,并自動(dòng)將so和應(yīng)用一起打包成 APK

即可通過 NDKAndroid中 使用 JNI與本地代碼(如C、C++)交互

  • 應(yīng)用場(chǎng)景:在Android的場(chǎng)景下 使用JNI

Android開發(fā)的功能需要本地代碼(C/C++)實(shí)現(xiàn)

  • 特點(diǎn)
示意圖
  • 額外注意
示意圖

2.2 使用步驟

  1. 配置 Android NDK環(huán)境
  2. 創(chuàng)建 Android 項(xiàng)目,并與 NDK進(jìn)行關(guān)聯(lián)
  3. Android 項(xiàng)目中聲明所需要調(diào)用的 Native方法
  4. 使用 Android需要交互的本地代碼 實(shí)現(xiàn)在Android中聲明的Native方法

比如 Android 需要與 C++ 交互,那么就用C++ 實(shí)現(xiàn) JavaNative方法

  1. 通過 ndk - bulid 命令編譯產(chǎn)生.so庫文件
  2. 編譯 Android Studio 工程,從而實(shí)現(xiàn) Android 調(diào)用本地代碼

更加詳細(xì)過程請(qǐng)參考本文第4節(jié):具體使用


3. NDK與JNI關(guān)系

示意圖

4. 具體使用

本文根據(jù)版本的不同介紹了兩種在Android Studio中實(shí)現(xiàn) NDK的方法:Android Studio2.2 以下 & 2.2以上

4.1 Android Studio2.2 以下實(shí)現(xiàn)NDK

  • 步驟如下

    1. 配置 Android NDK環(huán)境
    2. 關(guān)聯(lián) Andorid Studio項(xiàng)目 與 NDK
    3. 創(chuàng)建本地代碼文件(即需要在 Android項(xiàng)目中調(diào)用的本地代碼文件)
    4. 創(chuàng)建 Android.mk文件 & Application.mk文件
    5. 編譯上述文件,生成.so庫文件,并放入到工程文件中
    6. Andoird Studio項(xiàng)目中使用 NDK實(shí)現(xiàn) JNI 功能
  • 步驟詳解

步驟1:配置 Android NDK環(huán)境

具體請(qǐng)看文章一定能成功的Android NDK環(huán)境配置教程

步驟2: 關(guān)聯(lián)Andorid Studio項(xiàng)目 與 NDK

  • 當(dāng)你的項(xiàng)目每次需要使用 NDK 時(shí),都需要將該項(xiàng)目關(guān)聯(lián)到 NDK
  1. 此處使用的是Andorid Studio,與Eclipse不同
  2. 還在使用Eclipse的同學(xué)請(qǐng)自行查找資料配置
  • 具體配置如下

a. 在Gradlelocal.properties中添加配置

ndk.dir=/Users/Carson_Ho/Library/Android/sdk/ndk-bundle

ndk目錄存放在SDK的目錄中,并命名為ndk-bundle,則該配置自動(dòng)添加

示意圖

b. 在Gradlegradle.properties中添加配置

android.useDeprecatedNdk=true 
// 對(duì)舊版本的NDK支持
示意圖

c. 在Gradle的build.gradle添加ndk節(jié)點(diǎn)

示意圖
  • 至此,將Andorid Studio的項(xiàng)目 與 NDK 關(guān)聯(lián)完畢
  • 下面,將真正開始講解如何在項(xiàng)目中使用NDK

步驟3:創(chuàng)建本地代碼文件

  • 即需要在Android項(xiàng)目中調(diào)用的本地代碼文件

此處采用 C++作為展示

test.cpp

# include <jni.h>
# include <stdio.h>

extern "C"
{
   
    JNIEXPORT jstring JNICALL Java_scut_carson_1ho_ndk_1demo_MainActivity_getFromJNI(JNIEnv *env, jobject obj ){
       // 參數(shù)說明
       // 1. JNIEnv:代表了VM里面的環(huán)境,本地的代碼可以通過該參數(shù)與Java代碼進(jìn)行操作
       // 2. obj:定義JNI方法的類的一個(gè)本地引用(this)
    return env -> NewStringUTF("Hello i am from JNI!");
    // 上述代碼是返回一個(gè)String類型的"Hello i am from JNI!"字符串
    }
}

此處需要注意:

  • 如果本地代碼是C++.cpp或者.cc),要使用extern "C" { }把本地方法括進(jìn)去
  • JNIEXPORT jstring JNICALL中的JNIEXPORTJNICALL不能省
  • 關(guān)于方法名Java_scut_carson_1ho_ndk_1demo_MainActivity_getFromJNI
    1. 格式 = Java _包名 _ 類名_Java需要調(diào)用的方法名
    2. Java必須大寫
    3. 對(duì)于包名,包名里的.要改成_,_要改成_1

    如我的包名是:scut.carson_ho.ndk_demo,則需要改成scut_carson_1ho_ndk_1demo

最后,將創(chuàng)建好的test.cpp文件放入到工程文件目錄中的src/main/jni文件夾

若無jni文件夾,則手動(dòng)創(chuàng)建。

下面我講解一下JNI類型與Java類型對(duì)應(yīng)的關(guān)系介紹


如下圖

步驟4:創(chuàng)建Android.mk文件

  • 作用:指定源碼編譯的配置信息

如工作目錄,編譯模塊的名稱,參與編譯的文件等

  • 具體使用

Android.mk

LOCAL_PATH       :=  $(call my-dir)
// 設(shè)置工作目錄,而my-dir則會(huì)返回Android.mk文件所在的目錄

include              $(CLEAR_VARS)
// 清除幾乎所有以LOCAL——PATH開頭的變量(不包括LOCAL_PATH)

LOCAL_MODULE     :=  hello_jni
// 設(shè)置模塊的名稱,即編譯出來.so文件名
// 注,要和上述步驟中build.gradle中NDK節(jié)點(diǎn)設(shè)置的名字相同

LOCAL_SRC_FILES  :=  test.cpp
// 指定參與模塊編譯的C/C++源文件名

include              $(BUILD_SHARED_LIBRARY)
// 指定生成的靜態(tài)庫或者共享庫在運(yùn)行時(shí)依賴的共享庫模塊列表。

// 示例說明
// 示例1:c++文件參與編譯
    // 單個(gè)c++文件參與編譯
    include $(CLEAR_VARS)
    LOCAL_MODULE := Carsontest // 編譯出來的文件名
    LOCAL_SRC_FILES := test1.cpp 

    // 多個(gè)c++文件參與編譯
    include $(CLEAR_VARS)
    LOCAL_MODULE := Carsontest // 編譯出來的文件名
    LOCAL_SRC_FILES := \
        test1.cpp \
        test/ftest2.cpp \
        test3.c \

// 示例2:.so文件參與模塊編譯
    include $(CLEAR_VARS)
    LOCAL_MODULE := Carsontest // 編譯出來的文件名
    LOCAL_SRC_FILES := test/Carsontest.so
    include $(PREBUILT_SHARED_LIBRARY) // 設(shè)置可被依賴

// 示例3:設(shè)置需依賴的.so文件
    LOCAL_SHARED_LIBRARIES := \
        test1 \
        Carsontest

最后,將上述文件同樣放在src/main/jni文件夾中。

步驟5:創(chuàng)建Application.mk文件

  • 作用:配置編譯平臺(tái)相關(guān)內(nèi)容
  • 具體使用

Application.mk

APP_ABI := armeabi
// 最常用的APP_ABI字段:指定需要基于哪些CPU平臺(tái)的.so文件
// 常見的平臺(tái)有armeabi x86 mips,其中移動(dòng)設(shè)備主要是armeabi平臺(tái)
// 默認(rèn)情況下,Android平臺(tái)會(huì)生成所有平臺(tái)的.so文件,即同APP_ABI := armeabi x86 mips
// 指定CPU平臺(tái)類型后,就只會(huì)生成該平臺(tái)的.so文件,即上述語句只會(huì)生成armeabi平臺(tái)的.so文件

最后,將上述文件同樣放在src/main/jni文件夾中

步驟6:編譯上述文件,生成.so庫文件

  • 經(jīng)過上述步驟,在src/main/jni文件夾中已經(jīng)有3個(gè)文件
示意圖
  • 打開終端,輸入以下命令
// 步驟1:進(jìn)入該文件夾
cd /Users/Carson_Ho/AndroidStudioProjects/NDK_Demo/app/src/main/jni 
// 步驟2:運(yùn)行NDK編譯命令
ndk-build
示意圖
  • 編譯成功后,在src/main/會(huì)多了兩個(gè)文件夾libs & obj,其中libs下存放的是.so庫文件
示意圖

步驟7:在src/main/中創(chuàng)建一個(gè)名為jniLibs的文件夾,并將上述生成的so文件夾放到該目錄下

  1. 要把名為 CPU平臺(tái)的文件夾放進(jìn)去,而不是把.so文件放進(jìn)去
  2. 如果本來就有.so文件,那么就直接創(chuàng)建名為jniLibs的文件夾并放進(jìn)去就可以
示意圖

步驟8:在Andoird Studio項(xiàng)目中使用NDK實(shí)現(xiàn)JNI功能

  • 此時(shí),我們已經(jīng)將本地代碼文件編譯成.so庫文件并放入到工程文件中
  • Java代碼中調(diào)用本地代碼中的方法,具體代碼如下:

MainActivity.java

public class MainActivity extends AppCompatActivity  {

    // 步驟1:加載生成的so庫文件
    // 注意要跟.so庫文件名相同
    static {

        System.loadLibrary("hello_jni");
    }
    
    // 步驟2:定義在JNI中實(shí)現(xiàn)的方法
    public native String getFromJNI();
    
    // 此處設(shè)置了一個(gè)按鈕用于觸發(fā)JNI方法
    private Button Button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 通過Button調(diào)用JNI中的方法
        Button = (Button) findViewById(R.id.button);
        Button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Button.setText(getFromJNI());
                
            }
        });
    }

主布局文件:activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="scut.carson_ho.ndk_demo.MainActivity">

    // 此處設(shè)置了一個(gè)按鈕用于觸發(fā)JNI方法
    <Button
        android:id="@+id/button"
        android:layout_centerInParent="true"
        android:layout_width="300dp"
        android:layout_height="50dp"
        android:text="調(diào)用JNI代碼" />

</RelativeLayout>

結(jié)果展示

結(jié)果展示

源碼地址

Carson-Ho的Github地址:NDK_Demo


4.2 Android Studio2.2 以上實(shí)現(xiàn)NDK

  • 如果你的Android Studio是2.2以上的,那么請(qǐng)采用下述方法

因?yàn)?code>Android Studio2.2以上已經(jīng)內(nèi)部集成 NDK,所以只需要在Android Studio內(nèi)部進(jìn)行配置就可以

  • 步驟講解

步驟1:按提示創(chuàng)建工程

在創(chuàng)建工程時(shí),需要配置 NDK,根據(jù)提示一步步安裝即可。

示意圖

步驟2:根據(jù)需求使用NDK

  • 配置好NDK后,Android Studio會(huì)自動(dòng)生成C++文件并設(shè)置好調(diào)用的代碼
  • 你只需要根據(jù)需求修改C++文件 & Android就可以使用了。
示意圖

5. 總結(jié)

  • 本文主要講解 JavaJNIAndroidNDK相關(guān)知識(shí)
  • 下面我將繼續(xù)對(duì) Android中的NDK進(jìn)行深入講解 ,感興趣的同學(xué)可以繼續(xù)關(guān)注Carson_Ho的簡(jiǎn)書

相關(guān)系列文章閱讀
Carson帶你學(xué)Android:學(xué)習(xí)方法
Carson帶你學(xué)Android:四大組件
Carson帶你學(xué)Android:自定義View
Carson帶你學(xué)Android:異步-多線程
Carson帶你學(xué)Android:性能優(yōu)化
Carson帶你學(xué)Android:動(dòng)畫


歡迎關(guān)注Carson_Ho的簡(jiǎn)書

不定期分享關(guān)于安卓開發(fā)的干貨,追求短、平、快,但卻不缺深度


請(qǐng)點(diǎn)贊!因?yàn)槟愕墓膭?lì)是我寫作的最大動(dòng)力!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,055評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,365評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,346評(píng)論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,889評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,118評(píng)論 0 286
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,637評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,558評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,739評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,980評(píng)論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評(píng)論 1 280
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,347評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,702評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容