
更新了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,大概意思就是說:
- android.useDeprecatedNdk不再支持了
- 讓使用CMake or ndk-build
- 然后還有鏈接
考慮使用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)庫
-
頭文件
也可以配置頭文件路徑,方法是(注意這里指定的是目錄而非文件):
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
下面的配置實(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)境,我們只需要改成我們需要的文件名即可。相比低版本的各種配置,是簡單方便了很多。