本系列文章如下:
本地內容主要簡介如下:
- 1免姿、環(huán)境展示
- 2培己、傳統(tǒng)方式的具體流程
- 3盈咳、傳統(tǒng)方式的相關問題
- 4油够、傳統(tǒng)方式的so文件
- 5、通過CMake工具demo演示流程
- 6腮敌、CMake工具demo的背后原理
- 7录平、CMake的應用
- 8、使用
experimental-plugin
插件編譯
一缀皱、環(huán)境展示
操作系統(tǒng)為
Android環(huán)境為:
NDK環(huán)境
模擬器為
二斗这、傳統(tǒng)方式的具體流程
具體流程如下:
(一) 創(chuàng)建項目
首先在Android Studio創(chuàng)建一個Android項目,包名為gebilaolitou.ndkdemo
(二) 創(chuàng)建引用本地庫的工具類
然后創(chuàng)建一個class
為NDKTools
代碼如下:
package gebilaolitou.ndkdemo;
public class NDKTools {
public static native String getStringFromNDK();
}
(三) 修改相關UI顯示
MainActivity對應的xml中的textview
添加id
如下:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="gebilaolitou.ndkdemo.MainActivity">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
然后修改MainActivity啤斗,在里面調用NDKTools
的getStringFromNDK()
方法表箭。
package gebilaolitou.ndkdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String text = NDKTools.getStringFromNDK();
Log.i("gebilaolitou","text="+text);
((TextView)findViewById(R.id.tv)).setText(text);
}
}
(四) 獲取classes文件
在Android Studio中點擊Build
中的Make Project
或者Rebuild Project
進行編譯來獲取中間文件。如下圖
編譯完成后钮莲,我們就可以獲取class文件如下圖
(五) 進入相應目錄
點擊Android Studio下面的Terminal免钻,然后跳到NDKDemo/app/build/intermediates/classes/debug
下(其中NDKDemo為是項目的根目錄),在Terminal執(zhí)行pwd
確認目錄崔拥。
(六) 獲取.h文件
在NDKDemo/app/build/intermediates/classes/debug
下執(zhí)行下面的命令javah -jni gebilaolitou.ndkdemo.NDKTools
极舔。如果沒有問題,則會在NDKDemo/app/build/intermediates/classes/debug
下面生成gebilaolitou_ndkdemo_NDKTools.h
文件链瓦。如下圖
其內容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class gebilaolitou_ndkdemo_NDKTools */
#ifndef _Included_gebilaolitou_ndkdemo_NDKTools
#define _Included_gebilaolitou_ndkdemo_NDKTools
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: gebilaolitou_ndkdemo_NDKTools
* Method: getStringFromNDK
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
如下圖
(七) 增加對應的.c文件
在工程main目錄下創(chuàng)建一個名字為jni目錄拆魏,然后將剛才的.h文件剪切過來。在jni目錄下新建一個c文件慈俯。命名為ndkdemotest.c
渤刃。此時項目目錄如下:
(八) 編寫ndkdemotest.c文件
將ndkdemotest.c協(xié)商如下內容
#include "gebilaolitou_ndkdemo_NDKTools.h"
JNIEXPORT jstring JNICALL Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK
(JNIEnv *env, jobject obj){
return (*env)->NewStringUTF(env,"Hellow World,這是隔壁老李頭的NDK的第一行代碼");
}
內容不多贴膘,就是兩部分卖子,第一部分就是 添加gebilaolitou_ndkdemo_NDKTools.h
頭文件,然后就是具體實現(xiàn)Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK
函數
(九) 添加并編寫Android.mk文件
同樣在jni目錄下刑峡,添加一個Android.mk文件洋闽,其目錄結構如下:
同樣在Android.mk文件里面編寫如下內容
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ndkdemotest-jni
LOCAL_SRC_FILES := ndkdemotest.c
include $(BUILD_SHARED_LIBRARY)
關于Android.mk語言后面會單獨寫一篇文章進行講解玄柠,這里重點說上面代碼的內容
LOCAL_PATH := $(call my-dir)
:每個Android.mk文件必須以定義開始。它用于在開發(fā)tree中查找源文件诫舅。宏my-dir
則由Build System 提供羽利。返回包含Android.mk目錄路徑。include $(CLEAR_VARS)
:CLEAR_VARS
變量由Build System提供骚勘。并指向一個指定的GNU Makefile铐伴,由它負責清理很多LOCAL_xxx撮奏。例如LOCAL_MODULE俏讹,LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARIES等等畜吊。但不是清理LOCAL_PATH泽疆。這個清理是必須的,因為所有的編譯控制文件由同一個GNU Make解析和執(zhí)行玲献,其變量是全局的殉疼。所以清理后才能便面相互影響。LOCAL_MODULE := ndkdemotest-jni
:LOCAL_MODULE模塊必須定義捌年,以表示Android.mk中的每一個模塊瓢娜。名字必須唯一且不包含空格。Build System 會自動添加適當的前綴和后綴礼预。例如眠砾,demo,要生成動態(tài)庫托酸,則生成libdemo.so褒颈。但請注意:如果模塊名字被定義為libabd,則生成libabc.so励堡。不再添加前綴谷丸。LOCAL_SRC_FILES := ndkdemotest.c
:這行代碼表示將要打包的C/C++源碼。不必列出頭文件应结,build System 會自動幫我們找出依賴文件刨疼。缺省的C++ 源碼的擴展名為.cpp。include $(BUILD_SHARED_LIBRARY)
:BUILD_SHARED_LIBRARY
是Build System提供的一個變量鹅龄,指向一個GUN Makefile Script币狠。它負責收集自從上次調用include $(CLEAR_VARS)
后的所有LOCAL_xxxxinx。并決定編譯什么類型
BUILD_STATIC_LIBRARY
:編譯為靜態(tài)庫BUILD_SHARED_LIBRARY
:編譯為動態(tài)庫BUILD_EXECUTABLE
:編譯為Native C 可執(zhí)行程序BUILD_PREBUILT
:該模塊已經預先編譯
PS:這里不編寫Android.mk會提示如下問題:
Error:Execution failed for task ':app:compileDebugNdk'.
> Error: Flag android.useDeprecatedNdk is no longer supported and will be removed in the next version of Android Studio. Please switch to a supported build system.
Consider using CMake or ndk-build integration. For more information, go to:
https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile
To get started, you can use the sample ndk-build script the Android
plugin generated for you at:
/Users/gebilaolitou/AndroidStudioProjects/JNIDemo/app/build/intermediates/ndk/debug/Android.mk
Alternatively, you can use the experimental plugin:
https://developer.android.com/r/tools/experimental-plugin.html
To continue using the deprecated NDK compile for another 60 days, set
android.deprecatedNdkCompileLease=1523001628930 in gradle.properties
全是英文砾层,簡單的翻譯下如下:
錯誤:執(zhí)行app:compileDebugNdk任務失敗
錯誤:不再支持android.useDeprecatedNdk標志漩绵,并且將會在未來的Android Studio版本中刪除這個標志。請切換到CMake構建系統(tǒng)或者ndk-build中集成肛炮。更多的信息請參考https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile
止吐。您可以使用Android的示例ndk-build腳本在以下位置生成的插件:
/Users/gebilaolitou/AndroidStudioProjects/JNIDemo/app/build/intermediates/ndk/debug/Android.mk
宝踪。另外,你也可以使用實驗性插件https://developer.android.com/r/tools/experimental-plugin.html
如果你還想繼續(xù)再使用已經被棄用的NDK編譯60天碍扔,你需要再gradle.properties中設置android.deprecatedNdkCompileLease=1523001628930
因為以上原因瘩燥,我所以我們需要設置Android.mk
(十) 修改相應的配置文件
首先檢查local.properties
文件中是否有NDK路徑,如果有沒有NDK路徑不同,則添加NDK路徑厉膀,比如我的如下:
ndk.dir=/Users/debilaolitouLibrary/Android/sdk/ndk-bundle
sdk.dir=/Users/debilaolitouLibrary/Library/Android/sdk
其次修改app module目錄下的build.gradle中的內容,如下:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "gebilaolitou.ndkdemo"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
ndk{
moduleName "ndkdemotest-jni"
abiFilters "armeabi", "armeabi-v7a", "x86"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
externalNativeBuild {
ndkBuild {
path 'src/main/jni/Android.mk'
}
}
sourceSets.main {
jni.srcDirs = []
jniLibs.srcDirs = ['src/main/jniLibs']
}
}
}
這樣就有了so文件(此時還沒生成so文件)
(十一) 修改引用類
最后在NDKTools
類中添加靜態(tài)初始化代碼二拐,如下:
public class NDKTools {
static {
System.loadLibrary("ndkdemotest-jni");
}
public static native String getStringFromNDK();
}
最后run一下即可服鹅,如下圖
三、傳統(tǒng)方式的相關問題
有的同學在運行的時候百新,會報如下錯誤:
Error:Execution failed for task ':app:compileDebugNdk'.
> Error: Your project contains C++ files but it is not using a supported native build system.
Consider using CMake or ndk-build integration. For more information, go to:
https://d.android.com/r/studio-ui/add-native-code.html
Alternatively, you can use the experimental plugin:
https://developer.android.com/r/tools/experimental-plugin.html
首先把檢查你項目中gradle.properties文件后面加上一句
Android.useDeprecatedNdk=true
四企软、傳統(tǒng)方式的so文件
大家可能會有疑問,那so去哪里了饭望,我們平時使用第三方的sdk的so的時候仗哨,會要粘貼復制到項目里面,而我們上所述整個過程铅辞,并沒有出現(xiàn).so這個文件厌漂,那么這個.so去哪里了?
其實Android Studio自動幫我們把so放到apk里面斟珊,如果我們想找也能找到苇倡,如下圖:
上面這套方式是傳統(tǒng)的Android Studio的模式,那有沒有更簡單的方式倍宾,是有的雏节,那下面我們就繼續(xù)來看下
五、通過CMake工具demo演示流程
(一) 首先確保你本地有CMake高职,我們來看下SDK Tools
上面看到第三個 CMake
我本地沒有钩乍,所以我要進行安裝
(二) 勾選Include C++ Support
復選框。
在向導的 Configure your new project 部分怔锌,選中 Include C++ Support 復選框寥粹。
如下圖
這里有個坑,就是有好多同學說我沒有這個
Include C++ Support
復選框埃元,這是因為Android Studio設計的的"bug"涝涤,你把這個對話框進行拉大,就出現(xiàn)了岛杀,因為一般的Android 項目用不到阔拳,所以在設計這個的時候,如果不特意的拉大类嗤,就選擇性的"隱藏"了,太JB坑了糊肠。
然后一直下一步辨宠,直到Customize C++ Support
部分
(三) Customize C++ Support
的自定義項目
如下:
里面有個三個項目
- C++ Standard:即C++標準,使用下拉列表選擇你希望使用的C++的標準货裹,選擇Toolchain Default 會使用默認的
CMake
設置嗤形。- Exceptions Support:如果你希望啟用對C++異常處理的支持,請選擇此復選框弧圆。如果啟動此復選框赋兵,Android Studio 會將
-fexceptions
標志添加到模塊級build.gradle
文件的cppFlags
中,Gradle會將其傳遞到CMake搔预。- Runtime Type Information Support:如果開發(fā)者希望支持RTTI霹期,請選中此復選框。如果啟用此復選框斯撮,Android Studio 會將
-frtti
標志添加到模塊級build.gradle
文件的cppFlags中经伙,Gradle會將其傳遞到CMake扶叉。
最后點擊 Finish
勿锅。
(四) 檢查 Android 目錄
在Android Studio 完成新項目的創(chuàng)建后,請從IDE左側打開Project 礦口并選擇Android 視圖枣氧。如下圖所示溢十,Android Studio 將添加cpp
和External Build Files 組
:
該圖為開發(fā)者的原生源文件和外部構建腳本的Android 視圖組。
PS:(此視圖無法反應磁盤上的實際文件層次結構达吞,而是將相似文件分到一組中张弛,簡化項目導航)。如果為Project
模式則如下:
那我們簡單介紹下這兩個多出來的文件夾:
- 在 cpp 文件夾中:可以找到屬于項目的所有原生源文件等構建庫酪劫。對于新項目吞鸭,Android Studio會創(chuàng)建一個示例C++源文件
native-lib.cpp
,并將其置于應用模塊src/main/cpp/
目錄中覆糟。這個示例代碼提供了一個簡單的C++函數stringFromJNI()
刻剥,此函數可以返回字符串“Hello from C++”- 在 External Build Files 文件夾中:可以找到CMake或 ndk-build 的構建腳本。與
build.gradle
文件指示Gradle構建應用一樣滩字,CMake和ndk-build需要一個構建腳本來了解如何構原生庫造虏。對于新項目,Android Studio 會創(chuàng)建一個CMake 構建腳本CMakeLists.txt
麦箍,并將其置于模塊根目錄中漓藕。
(五) 直接運行項目
我們來直接 run一下這個項目,看下結果
(六) 修改native-lib.cpp
這時候我們修改下native-lib.cpp
挟裂,native-lib.cpp
內容如下:
再直接run一下項目享钞,看下結果。如下:
我們看到對應的文字已經修改了
六诀蓉、CMake工具demo的背后原理
我們看打了栗竖,我們什么都沒做寝姿,就自動實現(xiàn)了C++的實現(xiàn),它的背后原理是什么那划滋?我們大家就思考一下饵筑?
(一)CMake的入口
它既然可以跑起來,一定有一個入口处坪,那這個入口在哪里那?
~~~~~~~~~~~~~~~~~~~~~~~~~~分隔符~~~~~~~~~~~~~~~~~~~~
先和大家說下我是怎么想象的根资,首先我們在點擊Android Studio中的run
按鈕的時候,它是執(zhí)行Gradle來進行打包的同窘,所以說關于CMake的是怎么植入進去的玄帕,一定在項目的build.gradle
,有相應的入口想邦。
通過上面的思想牺六,我們能舉一反三得到什么今艺?對的,就是類似于這種操作,一般都是在
build.gradle
里面實現(xiàn)的票灰,因為在目前Android Studio就是通過Gradle是實現(xiàn)的
那我們就來看下它的build.gradle
里面的代碼刑棵,如下:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "gebilaolitou.cmakendkdemo"
minSdkVersion 23
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
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.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
和我們平時搭建的項目差不多砚婆,就是多出來一塊內容意蛀,externalNativeBuild。那這里我們重點說下externalNativeBuild
(二) externalNativeBuild
我們在build.gradle
里面看到夺饲,有兩個地方用到了externalNativeBuild
奸汇,一個是在defaultConfig
里面,是一個是在defaultConfig
外面往声。
- 在
defaultConfig
外面的externalNativeBuild
里面的cmake
指明了CMakeList.txt
的路徑(在本項目下擂找,和是build.gradle
在同一個目錄里面)。- 在
defaultConfig
里面的externalNativeBuild
里面的cmake
主要填寫的是CMake
的命令參數浩销。即由arguments中的參數最后轉化成一個可執(zhí)行的CMake的命令贯涎,可以在
defaultConfig外面的 externalNativeBuild - cmake,指明了 CMakeList.txt 的路徑撼嗓;
defaultConfig 里面的 externalNativeBuild - cmake柬采,主要填寫 CMake 的命令參數。即由 arguments 中的參數最后轉化成一個可執(zhí)行的 CMake 的命令且警,可以在 app/externalNativeBuild/cmake/debug/{abi}/cmake_build_command.txt
中查到粉捻。如下
路徑位置如下圖:
內容如下:
arguments :
-H/Users/gebilaolitou/Desktop/codeLib/CMakeNDKDemo/app
-B/Users/gebilaolitou/Desktop/codeLib/CMakeNDKDemo/app/.externalNativeBuild/cmake/debug/x86
-DANDROID_ABI=x86
-DANDROID_PLATFORM=android-23
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/Users/gebilaolitou/Desktop/codeLib/CMakeNDKDemo/app/build/intermediates/cmake/debug/obj/x86
-DCMAKE_BUILD_TYPE=Debug
-DANDROID_NDK=/Users/gebilaolitou/Library/Android/sdk/ndk-bundle
-DCMAKE_CXX_FLAGS=
-DCMAKE_TOOLCHAIN_FILE=/Users/gebilaolitou/Library/Android/sdk/ndk-bundle/build/cmake/android.toolchain.cmake
-DCMAKE_MAKE_PROGRAM=/Users/gebilaolitou/Library/Android/sdk/cmake/3.6.4111459/bin/ninja
-GAndroid Gradle - Ninja
jvmArgs :
更多的可以填寫的命令參數和含義可以參見Android NDK-CMake文檔
ok上面既然提到了CMakeLists.txt
,那我們就來看下CMakeLists.txt
(三) CMakeLists.txt
CMakeLists.txt
這個文件主要定義了哪些文件需要編譯斑芜,以及和其他庫的關系等肩刃,那讓我們來看下我們項目中的CMakeLists.txt
的內容
# 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.
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.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
# 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.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
上面很多是注釋,我們除去注釋來個"精簡干練版"的如下:
cmake_minimum_required(VERSION 3.4.1)
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.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.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
CMakeLists.txt
我們看到這里主要是分為四個部分,下面我們就依次來看下
- cmake_minimum_required(VERSION 3.4.1):指定CMake的最小版本
- add_library:創(chuàng)建一個靜態(tài)或者動態(tài)庫盈包,并提供其關聯(lián)的源文件路徑沸呐,開發(fā)者可以定義多個庫,CMake會自動去構建它們呢燥。Gradle可以自動將它們打包進APK中崭添。
- 第一個參數——native-lib:是庫的名稱
- 第二個參數——SHARED:是庫的類別,是
動態(tài)的
還是靜態(tài)的
- 第三個參數——src/main/cpp/native-lib.cpp:是庫的源文件的路徑
- find_library:找到一個預編譯的庫叛氨,并作為一個變量保存起來呼渣。由于CMake在搜索庫路徑的時候會包含系統(tǒng)庫,并且CMake會檢查它自己之前編譯的庫的名字寞埠,所以開發(fā)者需要保證開發(fā)者自行添加的庫的名字的獨特性屁置。
- 第一個參數——log-lib:設置路徑變量的名稱
- 第一個參數—— log:指定NDK庫的名子,這樣CMake就可以找到這個庫
- target_link_libraries:指定CMake鏈接到目標庫仁连。開發(fā)者可以鏈接多個庫蓝角,比如開發(fā)者可以在此定義庫的構建腳本,并且預編譯第三方庫或者系統(tǒng)庫饭冬。
- 第一個參數——native-lib:指定的目標庫
- 第一個參數——${log-lib}:將目標庫鏈接到NDK中的日志庫使鹅,
這其實是一個最基礎的CMakeLists.txt
,其實CMakeLists.txt
里面可以非常強大伍伤,比如自定義命令并徘、查找文件遣钳、頭文件包含扰魂、設置變量等等。這里推薦CMake的官網文檔蕴茴,不過是英文的劝评,不好閱讀,大家可以參考中文的CMake手冊
上面分析完畢CMakeLists.txt
倦淀,我們就大致的知道了CMake整體的構建流程蒋畜,那我們就來看下
(四) CMake的運轉流程
- 1、Gradle 調用外部構建腳本
CMakeLists.txt
- 2撞叽、CMake 按照構建腳本的命令將 C++ 源文件
native-lib.cpp
編譯到共享的對象庫中姻成,并命名為libnative-lib.so
,Gradle 隨后會將其打包到APK中- 3愿棋、運行時科展,應用的
MainActivity
會使用System.loadLibrary()
加載原生庫。應用就是可以使用庫的原生函數stringFromJNI()
糠雨。
PS:這里注意一點就是:Instant Run
與使用原生的項目不兼容
如果想看Gradle是否將原生庫打包到APK中才睹,可以使用Analyze APK
來檢測。
七、CMake的應用
我們在做日常需求的時候琅攘,往往會遇到一個問題垮庐,即在已有的項目中,添加C庫坞琴,這樣就不能通過上面的
創(chuàng)建
流程哨查,來使用CMake。那怎么辦剧辐?
其實沒關系的解恰,CMake也提供這樣的功能的,現(xiàn)在我們就回到上面的第一個demo中浙于,刪除和NDK的有關的所有代碼护盈,刪除后其目錄如下:
(一) 創(chuàng)建源文件
即在main
目錄下新建一個目錄,我們就叫cpp
好了羞酗。然后在該目錄下創(chuàng)建一個C++ Source File(右鍵點擊您剛剛創(chuàng)建的目錄腐宋,然后選擇 New > C/C++ Source File)。我們將其命名為native-lib檀轨。
創(chuàng)建后胸竞,目錄如下:
(二) 創(chuàng)建CMake構建腳本
因為目前這個項目沒有CMake的構建腳本,所以咱們需要自行創(chuàng)建一個并包含適當的CMake命令参萄。CMake構建腳本是一個純文本的文件卫枝,而且這個名字必須是是CMakeLists.txt
要常創(chuàng)建一個可以用作CMake構建腳本的純文本文件,請按以下步驟操作:
- 1讹挎、從Android Studio左側打開Project窗格并從下拉菜單中選擇Project視圖校赤。
- 2、右鍵點擊 模塊的根目錄并選擇
New
——>File
筒溃。
PS:這個位置不是不固定的马篮,位置可以隨意,但是配置構建腳本時怜奖,需要將這個位置寫入構建腳本
- 3浑测、輸入
CMakeLists.txt
作為文件并點擊OK
創(chuàng)建后,目錄如下:
(三) 向CMake腳本文件寫入數據
這塊上面講解了過了歪玲,就不詳細說明了迁央,內容如下:
cmake_minimum_required(VERSION 3.4.1)
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
find_library( # Defines the name of the path variable that stores the
# location of the NDK library.
log-lib
# Specifies the name of the NDK library that
# CMake needs to locate.
log )
target_link_libraries( # Specifies the target library.
native-lib
# Links the log library to the target library.
${log-lib} )
(四) 向Gradle 關聯(lián)到原生庫
要將Gradle關聯(lián)到原生庫,需要提供一個指向CMake或ndk-build 腳本文件的路徑滥崩。在構建應用時岖圈,Gradle會以依賴項的形式運行CMake或ndk-build,并將共享的庫打包到APK中夭委。Gradle還是用構建腳本來了解將那些文件添加到Android 項目中幅狮。
如果原生文件還沒有構建腳本募强,需要創(chuàng)建CMake構建腳本
關于 關聯(lián)到原生庫有兩種方式,一種是通過Android Studio崇摄,一種是手動擎值,其實其背后的東西是一致的,我們就一一來說明
1逐抑、通過Android Studio 實現(xiàn)
- 1鸠儿、從IDE 左側打開
Project
窗格 并選擇Android
視圖- 2、右鍵點擊想要關聯(lián)到原生庫的模塊(咱們這里是app 模塊)厕氨,并從菜單中選擇
Link C++ Project with Gradle
进每。如下圖- 3、在下拉菜單中選擇CMake命斧。使用Project Pat來為外部的CMake項目指定剛剛的``CMakeLists.txt`腳本文件
- 4田晚、點擊
OK
。
2国葬、手動實現(xiàn)
要手動配置Gradle 以關聯(lián)到原生庫贤徒,需要將externalNativeBuild{}
塊添加到模塊級 build.gradle
文件中,并使用cmake {}
對其進行配置
代碼如下:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "gebilaolitou.ndkdemo"
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 {
cmake {
path 'CMakeLists.txt'
}
}
}
(五) 編寫native-lib.cpp
這塊很簡單汇四,內容如下:
#include <jni.h>
#include <string>
extern "C"
JNIEXPORT jstring
JNICALL
Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK(
JNIEnv *env, jobject /* this */) {
std::string hello = "(*^__^*) 嘻嘻……~Hello from C++ 隔壁老李頭";
return env->NewStringUTF(hello.c_str());
}
然后在NDKTools.java
添加引用接奈,如下:
package gebilaolitou.ndkdemo;
public class NDKTools {
static {
System.loadLibrary("native-lib");
}
public static native String getStringFromNDK();
}
然后直接運行,即可通孽,結果如下:
八序宦、使用experimental-plugin插件簡介
我們在使用NDK開發(fā)有件比較麻煩的事情,就是編寫Android.mk和Application.mk背苦,兒Android Studio的插件gradle-experimental就是用來解決這個問題的互捌。所以使用gradle-experimental插件可以不用再編寫.mk文件情況下進行NDK開發(fā)。
gradle-experimental是Android Studio的一個實驗性的項目糠惫,是基于gradle的一個插件疫剃,主要用來自動化NDK的配置實現(xiàn),無需自己編寫Android.mk和Android.mk硼讽,對于調試NDK項目也更加友好,不過現(xiàn)在已經 不支持 牲阁,詳細請看Experimental Plugin User Guide
Note to experimental Android plugin users: The experimental plugin will no longer be supported after version 0.11.0 (released October 25, 2017). That's because the experimental plugin is designed around a Software Component Model that Gradle announced they will no longer support (read their blog post here). Gradle has backported many features from the component model, which are now available with Android plugin 3.0.0, such as variant-aware dependency resolution, and api and implementation dependency configurations. Gradle is working on backporting built-in support for compiling C/C++ code, and the Android plugin will integrate that support when it becomes available. Until then, you can either keep using experimental plugin 0.11.0 with Android Studio 3.0 or later, or migrate to Android Studio's support for using external native build tools.
簡單翻譯下如下:
對使用
experimental
Android插件的用戶請注意:自2017年10月25日發(fā)布的0.11.0后固阁,我們將不再支持experimental
插件了。因為Gradle不再支持這個依靠軟件組件模型設計experimental
插件了(通過他們的博客)城菊。在Gradle Android插件的3.0.0版本备燃,現(xiàn)在已經支持組建模型中的許多功能。例如variant-aware dependency resolution
和api and implementation dependency configurations
凌唬。Gradle現(xiàn)在支持編譯C/C++代碼的內置支持并齐,并且Android插件再可用時集成該支持。在此之間,您可以繼續(xù)使用Android Studio3.0或者更高版本的experimental
插件况褪,或者使用Android Studio支持的外部原生構建工具撕贞。
上一篇文章Android JNI(一)——NDK與JNI基礎
下一篇文章Android JNI學習(三)——Java與Native相互調用