JNI開發(fā)(基于AS高低版本說明)

更新了Android Studio版本之后,在按照之前的方式搭建JNI的開發(fā)環(huán)境,發(fā)現(xiàn)不斷的報(bào)錯(cuò),通過查看日志發(fā)現(xiàn),android.useDeprecatedNdk不再支持了讓,使用CMake or ndk-build。本文介紹了在低版本和高版本上分別怎么實(shí)現(xiàn),以及總結(jié)在搭建過程中遇到的錯(cuò)誤和解決辦法。

Android Studio 2.2版本以下

1.首先通過SDKManager-SDK Tools下載NDK插件

2.在local.properties文件里面配置NDK路徑:

一種方式是在該文件中直接填寫NDK的路徑

另一種方式是在SDK Location里面配置:在項(xiàng)目上右鍵->選擇open Moulde Settings

3.在在gradle.properties文件中追加下面代碼:

android.useDeprecatedNdk=true

4.使用native關(guān)鍵字編寫JNI接口

public class JNIUtils {
    public static native String getStringFromC();
}

編寫之后,make project,在工程目build\intermediates\classes\debug\自己的包名下就可以看到編譯后的class文件JNIUtils.class如下圖所示:


5.使用javah命令生成.h頭文件

5.1 打開Terminal,然后在命令行中先進(jìn)入到工程的build\intermediates\classes\debug目錄下(cd 直接拖拽debug目錄即可)
5.2 輸入命令:javah 包名.類名

javah com.bysj.myapplication.JNIUtils

在debug目錄下就會(huì)生成對(duì)應(yīng)的頭文件(.h文件)


6. 實(shí)現(xiàn)上述頭文件里面的方法

6.1 在main目錄下新建jni文件夾



6.2 將上述的頭文件復(fù)制進(jìn)來,文件名可以隨意該,但是內(nèi)容不能改。
6.3 新建一個(gè)c或者c++源文件實(shí)現(xiàn)頭文件中的方法,并在源文件中引入頭文件(此處我自己建立的是C++的文件)

#include "com_bysj_jniapplication_JNIUtils.h" //頭文件的名字要對(duì)應(yīng)

JNIEXPORT jstring JNICALL Java_com_bysj_jniapplication_JNIUtils_getStringFromC
        (JNIEnv *env, jclass) {
    return env -> NewStringUTF("Hello from C++");
    }

7. 添加NDK配置

7.1 開發(fā)app內(nèi)build.gradle,在android/defaultConfig下面添加ndk配置

android {
       。。。。。。
 defaultConfig {
      。。。。。。
        ndk {
            moduleName "JNISample" //生成so庫的名字
        }
    }
    buildTypes {
    。。。。。。
    }
}

7.2 加載so庫,在JNIUtils類中添加以下的代碼:

public class JNIUtils {
    static {
        System.loadLibrary("JNISample");//名字要與build.gradle中配置的一致
    }
    public native static String getStringFromC();
}

最后編譯運(yùn)行,可以看到結(jié)果:


以上是在AS2.2版本以下這樣做會(huì)成功,但是在2.2以后在編譯的時(shí)候會(huì)報(bào)以下的錯(cuò)誤:

我們仔細(xì)看下Log,大概意思就是說:

  1. android.useDeprecatedNdk不再支持了
  2. 讓使用CMake or ndk-build
  3. 然后還有鏈接
    考慮使用CMake或ndk構(gòu)建集成。要了解更多信息,請?jiān)L問:
    https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile
    首先,您可以使用Android的ndk構(gòu)建腳本示例插件為您生成:
    /Users/apple/Desktop/AndroidJNITest/app/build/intermediates/ndk/debug/Android.mk
    或者,你可以使用實(shí)驗(yàn)插件:
    https://developer.android.com/r/tools/experimental-plugin.html
    繼續(xù)使用已棄用的NDK編譯60天,設(shè)置在gradle.properties
    android.deprecatedNdkCompileLease = 1523416167903(這個(gè)測試不起作用)
    經(jīng)過各種查資料,發(fā)現(xiàn)原來在gradle3.0以上以前這種方法不再支持

所以在AS2.2版本以后進(jìn)行NDK開發(fā)要使用CMake,下面演示以下具體的實(shí)現(xiàn)方法(你會(huì)發(fā)現(xiàn)很簡單,省去類很多的事)

Android Studio 2.2版本以上

1. 先通過SDKManager下載:CMake和LLDB

2.在新建項(xiàng)目的時(shí)候勾選 Include C++ Support

接下來的步驟跟創(chuàng)建普通項(xiàng)目一樣。

2.1 配置C++支持功能(Customize C++ Support)在Customize C++ Support界面默認(rèn)即可。


  • C++ Standard

指定編譯庫的環(huán)境,其中Toolchain Default使用的是默認(rèn)的CMake環(huán)境;C++ 11也就是C++環(huán)境。兩種環(huán)境都可以編庫,至于區(qū)別,后續(xù)會(huì)跟進(jìn),當(dāng)前博文使用的是CMake環(huán)境。

  • Exceptions Support

如果選中復(fù)選框,則表示當(dāng)前項(xiàng)目支持C++異常處理,如果支持,在項(xiàng)目Module級(jí)別的build.gradle文件中會(huì)增加一個(gè)標(biāo)識(shí) -fexceptions到cppFlags屬性中,并且在so庫構(gòu)建時(shí),gradle會(huì)把該屬性值傳遞給CMake進(jìn)行構(gòu)建。

  • Runtime Type Information Support

同理,選中復(fù)選框,項(xiàng)目支持RTTI,屬性cppFlags增加標(biāo)識(shí)-frtti

點(diǎn)擊完成,Android Studio會(huì)自動(dòng)把JIN開發(fā)的環(huán)境搭建好,可以直接運(yùn)行,比起低版本的是不是省很多事?。?/p>

勾選了 Include C++ Support以后,會(huì)自動(dòng)在build.gradle中添加以下配置

android {
    compileSdkVersion 27
    defaultConfig {
        。。。。。。。。
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt" //該文件用于配置JNI項(xiàng)目屬性
        }
    }
}

3.CMakeLists.txt文件說明

CMakeLists.txt文件用于配置JNI項(xiàng)目屬性,主要用于聲明CMake使用版本、so庫名稱、C/CPP文件路徑等信息,下面是該文件內(nèi)容:

# Sets the minimum version of CMake required to build the native
# library. You should either keep the default value or only pass a
# value of 3.4.0 or lower.

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 it for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
            # 設(shè)置so文件名稱
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             # Associated headers in the same location as their source
             # 設(shè)置這個(gè)so文件為共享.
             # file are automatically included.
             src/main/cpp/native-lib.cpp )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because system libraries are included 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 the
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
  • cmake_minimum_required(VERSION 3.4.1)
    CMake最小版本使用的是3.4.1。

    • add_library()
      配置so庫信息(為當(dāng)前當(dāng)前腳本文件添加庫)

    • native-lib
      這個(gè)是聲明引用so庫的名稱,在項(xiàng)目中,如果需要使用這個(gè)so文件,引用的名稱就是這個(gè)。值得注意的是,實(shí)際上生成的so文件名稱是libnative-lib。當(dāng)Run項(xiàng)目或者build項(xiàng)目是,在Module級(jí)別的build文件下的intermediates\transforms\mergeJniLibs\debug\folders\2000\1f\main下會(huì)生成相應(yīng)的so庫文件。* SHARED
      這個(gè)參數(shù)表示共享so庫文件,也就是在Run項(xiàng)目或者build項(xiàng)目時(shí)會(huì)在目錄intermediates\transforms\mergeJniLibs\debug\folders\2000\1f\main下生成so庫文。此外,so庫文件都會(huì)在打包到.apk里面,可以通過選擇菜單欄的Build->Analyze Apk...*查看apk中是否存在so庫文件,一般它會(huì)存放在lib目錄下。

    • src/main/cpp/native-lib.cpp
      構(gòu)建so庫的源文件。

STATIC:靜態(tài)庫,是目標(biāo)文件的歸檔文件,在鏈接其它目標(biāo)的時(shí)候使用。
SHARED:動(dòng)態(tài)庫,會(huì)被動(dòng)態(tài)鏈接,在運(yùn)行時(shí)被加載。
MODULE:模塊庫,是不會(huì)被鏈接到其它目標(biāo)中的插件,但是可能會(huì)在運(yùn)行時(shí)使用dlopen-系列的函數(shù)動(dòng)態(tài)鏈接。
更詳細(xì)的解釋請參考這篇文章:C++靜態(tài)庫與動(dòng)態(tài)庫

下面的配置實(shí)際上與自定義的JNI項(xiàng)目(自定義的so庫)沒有太大關(guān)系。

  • find_library()
    這個(gè)方法與我們要?jiǎng)?chuàng)建的so庫無關(guān)而是使用NDK的Apis或者庫,默認(rèn)情況下Android平臺(tái)集成了很多NDK庫文件,所以這些文件是沒有必要打包到apk里面去的。直接聲明想要使用的庫名稱即可(猜測:貌似是在Sytem/libs目錄下)。在這里不需要指定庫的路徑,因?yàn)檫@個(gè)路徑已經(jīng)是CMake路徑搜索的一部分。如示例中使用的是log相關(guān)的so庫。

  • log-lib
    這個(gè)指定的是在NDK庫中每個(gè)類型的庫會(huì)存放一個(gè)特定的位置,而log庫存放在log-lib中

  • log
    指定使用log庫

  • target_link_libraries()
    如果你本地的庫(native-lib)想要調(diào)用log庫的方法,那么就需要配置這個(gè)屬性,意思是把NDK庫關(guān)聯(lián)到本地庫。

  • native-lib
    要被關(guān)聯(lián)的庫名稱

  • ${log-lib}
    要關(guān)聯(lián)的庫名稱,要用大括號(hào)包裹,前面還要有$符號(hào)去引用。

實(shí)際上,我們可以自己創(chuàng)建CMakeLists.txt文件,而且路徑不受限制,只要在build.gradle中配置externalNativeBuild.cmake.path來指定該文件路徑即可。

說明

AS2.2版本以后不會(huì)再創(chuàng)建jin文件夾,而創(chuàng)建的是cpp文件夾


總結(jié)

Android Studio2.2以上版本進(jìn)行JNI開發(fā),只需要在新建項(xiàng)目的時(shí)候勾選 Include C++ Support選項(xiàng),AS會(huì)自動(dòng)幫我們搭建好JNI開發(fā)環(huán)境,我們只需要改成我們需要的文件名即可。相比低版本的各種配置,是簡單方便了很多。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,234評(píng)論 25 708
  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 14,054評(píng)論 2 59
  • 向您的項(xiàng)目添加 C 和 C++ 代碼 本文內(nèi)容 下載 NDK 和構(gòu)建工具 創(chuàng)建支持 C/C++ 的新項(xiàng)目 構(gòu)建和運(yùn)...
    會(huì)飛的大象_閱讀 3,921評(píng)論 0 3
  • 昨晚告誡自己一定不可以熬夜晚睡,強(qiáng)制自己10點(diǎn)關(guān)燈睡覺。今早6點(diǎn)半自然醒,神清氣爽,把要做的的事在早上完成不是更好...
    KellyWei閱讀 178評(píng)論 0 0
  • 琳經(jīng)過一夜的思考,決定把玉叫過來商量一下。她跟玉直接了當(dāng)?shù)恼f,現(xiàn)在自己有個(gè)機(jī)會(huì)可以重新返回珠海,老板承諾加薪,畢竟...
    碎花紛飛閱讀 384評(píng)論 1 2

友情鏈接更多精彩內(nèi)容