標注:本文為個人學習使用斤儿,僅做自己學習參考使用裹虫,請勿轉(zhuǎn)載和轉(zhuǎn)發(fā)
2018-08-14: 初稿,參考SvenWang_
2018-08-24: 二稿娄琉,更新舊項目添加JNI使用CMake編譯的方法
0. 引言
- 在網(wǎng)上找了好幾篇關(guān)于ndk開發(fā)的文章次乓,一直沒有成功,最近關(guān)于android的so的開發(fā)相關(guān)的嘗試孽水,也就是c/c++等的開發(fā)票腰,所以準備從android的jni開始入手,以上是背景女气。
- 由于as不斷完善的ndk的開發(fā)杏慰,所以網(wǎng)上的博客就有了很多都已經(jīng)過時了,這個文章是從兩種方式講解as3.0 上面進行的ndk的配置炼鞠。
-
主要內(nèi)容
1.創(chuàng)建新的項目就開始進行ndk開發(fā)
2.在原有的項目的基礎(chǔ)上進行ndk開發(fā)
3.為什么生成的jni文件關(guān)聯(lián)不上
4.生成so各類庫 -
參考文獻
AndroidStudio3.0 NDK開發(fā)- 如何在已有項目中進行NDK開發(fā)
AndroidStudio3.0開發(fā)調(diào)試安卓NDK的C++代碼
1. 準備工作
首先需要下載關(guān)于ndk的相關(guān)工具
2. 創(chuàng)建新項目直接進行ndk開發(fā)
- 這種指的是直接在創(chuàng)建新的項目的時候缘滥,在向?qū)onfigure your new project 部分,選中 Include C++ Support 復選框谒主;
- 然后next朝扼;
- 正常填寫然后next;
- 在向?qū)У?Customize C++ Support 部分霎肯,您可以使用下列選項自定義項目:
1.C++ Standard:使用下拉列表選擇您希望使用哪種 C++ 標準擎颖。選擇 Toolchain Default 會使用默認的 CMake 設(shè)置榛斯。
2.Exceptions Support:如果您希望啟用對 C++ 異常處理的支持肠仪,請選中此復選框肖抱。如果啟用此復選框,Android Studio 會將 -fexceptions 標志添加到模塊級 build.gradle 文件的 cppFlags 中异旧,Gradle 會將其傳遞到 CMake意述。
3.Runtime Type Information Support:如果您希望支持 RTTI,請選中此復選框吮蛹。如果啟用此復選框荤崇,Android Studio 會將 -frtti 標志添加到模塊級 build.gradle 文件的 cppFlags 中,Gradle 會將其傳遞到 CMake潮针。 - 點擊 Finish术荤。
上面的操作比較簡單,在配置之后每篷,直接得到如下的界面
- 在cpp的包下瓣戚,可以找到屬于項目的所有的原生文件、.h文件焦读、預構(gòu)建庫子库。對于新的項目,Android Studio 會創(chuàng)建一個示例 C++ 源文件 native-lib.cpp矗晃,并將其置于應(yīng)用模塊的 src/main/cpp/ 目錄中仑嗅。本示例代碼提供了一個簡單的 C++ 函數(shù) stringFromJNI(),此函數(shù)可以返回字符串“Hello from C++”张症。
- 在 External Build Files 組中仓技,您可以找到 CMake 或 ndk-build 的構(gòu)建腳本。與 build.gradle 文件指示 Gradle 如何構(gòu)建應(yīng)用一樣俗他,CMake 和 ndk-build 需要一個構(gòu)建腳本來了解如何構(gòu)建您的原生庫脖捻。對于新項目,Android Studio 會創(chuàng)建一個 CMake 構(gòu)建腳本 CMakeLists.txt兆衅,并將其置于模塊的根目錄中郭变。
下面是關(guān)于CMakeLists.txt文件
# 有關(guān)使用CMake在Android Studio的更多信息,請閱讀文檔:https://d.android.com/studio/projects/add-native-code.html
# 設(shè)置CMake的最低版本構(gòu)建本機所需庫
cmake_minimum_required(VERSION 3.4.1)
# 創(chuàng)建并命名庫,將其設(shè)置為靜態(tài)的
# 或共享涯保,并提供其源代碼的相對路徑诉濒。
# 你可以定義多個library庫,并使用CMake來構(gòu)建夕春。
# Gradle會自動將包共享庫關(guān)聯(lián)到你的apk程序未荒。
add_library( # 設(shè)置庫的名稱
native-lib #這個根據(jù)庫名更改
# 將庫設(shè)置為共享庫。
SHARED
# 為源文件提供一個相對路徑及志。
src/main/cpp/native-lib.cpp ) #路徑下的庫名根據(jù)上面的庫名更改
# 搜索指定預先構(gòu)建的庫和存儲路徑變量片排。因為CMake包括系統(tǒng)庫搜索路徑中默認情況下,只需要指定想添加公共NDK庫的名稱寨腔,在CMake驗證庫之前存在完成構(gòu)建
find_library( # 設(shè)置path變量的名稱
log-lib
# 在CMake定位前指定的NDK庫名稱
log )
# 指定庫CMake應(yīng)該鏈接到目標庫中,可以鏈接多個庫率寡,比如定義庫迫卢,構(gòu)建腳本,預先構(gòu)建的第三方庫或者系統(tǒng)庫
target_link_libraries( # 指定目標庫
native-lib #根據(jù)上面更改庫名的更改
# 目標庫到日志庫的鏈接 包含在NDK
${log-lib} )
3. 在已有的項目的基礎(chǔ)上進行ndk開發(fā)
- 關(guān)于項目什么情況下是沒有引進c++庫呢冶共,就是在創(chuàng)建項目的情況下沒有勾選 Include C++ support乾蛤。
- 還有就是老項目沒有NDK,然后準備集成NDK開發(fā)捅僵,NDK開發(fā)主要有兩種編譯形式家卖,一種是原來的android.mk,也就是ndk-build庙楚,另一種是CMake上荡,CMake需要添加一個CMakeList.txt。
- 本文主要講解對CMake編譯方式進行依賴的方法馒闷。
- 舉個例子酪捡,創(chuàng)建一個沒有C++庫的demo
-
然后對這個項目進行一路的next,就是正常的as項目纳账,沒有ndk的相關(guān)文件沛善。
在MainActivity 中寫上 native的代碼,也可以單獨建立一個類塞祈,專門用來調(diào)用底層函數(shù)的util類
public class JavaNativeUtil {
static {
// 設(shè)置依賴庫,native-lib為依賴庫的名稱帅涂,可以自行修改
System.loadLibrary("native-lib");
}
public static native String hello();
}
- 之所以用static修飾了hello方法议薪,是準備將該方法直接在MainActivity中調(diào)用,然后發(fā)現(xiàn)hello方法是紅色報錯的媳友,暫時先不用處理斯议。
- 在main文件夾下建立一個cpp的文件夾,然后在cpp文件夾下創(chuàng)建一個native-lib的cpp文件醇锚,這個文件就是Java2C的橋梁
- 編寫c++實現(xiàn)方法
- 編寫native-lib.cpp文件時哼御,首先要包括頭文件jni.h,其次是包含extern “C”焊唬,注意extern “C”的{}一定要將下面的方法包裹起來恋昼,extern “C”主要就是為了兼容Java2C的JNI特殊書寫格式的方法。
- 編寫方法的的路徑一定不能寫錯赶促。
- 本方法和自動添加C++的Android項目一樣液肌,直接返回了一個字符串。
#include <jni.h>
extern "C" {
// 這個路徑參照上面創(chuàng)建的JavaNativeUtil的路徑進行編寫
JNIEXPORT jstring JNICALL Java_net_ankie_italker_jnitestdemo_JavaNativeUtil_hello(JNIEnv *env, jobject obj) {
// 返回字符串
return env->NewStringUTF("hello ndk");
}
}
-
創(chuàng)建一個CMakeLists.txt文件
- 這里的創(chuàng)建的名字必須是 CMakeLists.txt 然后把下面這些代碼復制進去
cmake_minimum_required(VERSION 3.4.1)
add_library( # Specifies the name of the library.
# 這里是你so的名字鸥滨∴露撸可以自己定義谤祖,但是在LoadLibrary的時候要對應(yīng)上
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
#這里是剛才 創(chuàng)建的c++ 代碼的名字
src/main/cpp/Hello.cpp )
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 )
target_link_libraries( # Specifies the target library.
# 這里是你so的名字。和上面的一樣
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
- 在配置完CMakeList.txt之后老速,可以配置gradle了粥喜,配置gradle時候有兩種方法,一種是點擊要配置的model右鍵橘券,添加Link C++ Project with Gradle额湘,然后添加對應(yīng)剛才創(chuàng)建的CMakeList.txt,右鍵項目選擇菜單:Link C++ Project with Gradle
- 另一種方法是直接添加在Gradle中下面增加的externalNativeBuild選項约郁,然后更新下gradle即可缩挑。
-
然后選擇我們使用的編譯模式CMake進行添加CMakeList.txt文件
- 添加之后會發(fā)現(xiàn)Gradle文件會增加externalNativeBuild選項,這樣我們就可以愉快的使用JNI了
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "net.ankie.italker.jnitestdemo"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild { // 這個就是剛才添加的新的關(guān)于CMakeList.txt的添加
cmake {
path 'CMakeLists.txt'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
- 然后編譯一下就可以了鬓梅,編譯的時候最好的方式是先clean build供置,然后再編譯下
- 然后會發(fā)現(xiàn)hello方法不紅了,可以執(zhí)行了绽快,能跳轉(zhuǎn)了芥丧,方法名之前可以跳轉(zhuǎn)到JavaNativeUtil方法里面了!
- 這樣就可以調(diào)用方法進行編譯調(diào)用了坊罢,本文直接調(diào)用的JNI的hello方法续担,然后在MainActivity中打印的Log
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e(TAG, "onCreate: " + JavaNativeUtil.hello());
}
}
4. 生成so文件
-
build之前應(yīng)該clean build,要不不能make Module ‘a(chǎn)pp’
-
然后就會生成so文件
5. 備注
- 本文主要講解的是關(guān)于AS中創(chuàng)建新的項目和在舊項目中添加NDK的CMake的編譯方式的方法活孩。
- 其中舊項目添加NDK有兩種方式物遇,網(wǎng)上主要介紹的是添加 .mk文件,然后使用ndk-build的方式憾儒,本文主要介紹的是添加CMakeList.txt文件CMake的編譯方式询兴。
- 主要講述了搭建JNI的形式,其中native-lib.cpp文件和native-lib庫的名稱可以自行根據(jù)需求定義起趾。
- 生成的so庫可以根據(jù)需要添加到不同的平臺下的AS中進行依賴诗舰。
- 其中JNI關(guān)聯(lián)不上的主要原因有native-lib.cpp文件中的方法的與在Java中的路徑的名不對稱,或者是CMakeList.txt沒有添加到Gradle中训裆,然后更新Gradle眶根。
- 還有可能就是沒有添加extern ”C“ {},或者其作用域沒有包含其他的JNI方法边琉。