
本文主要講解了在Windows環(huán)境下如何使用ndk-build構建工具來進行NDK開發(fā),以及ndk-build構建工具在Android Stuido中的快捷工具配置。
在上一篇文章《Android NDK開發(fā)(一) 使用CMake構建工具進行NDK開發(fā)》中,我們學習了如何使用CMake構建工具來進行NDK開發(fā),但是一些老項目還是使用的ndk-build構建工具進行開發(fā)的,今天我們就來學習一下如何使用ndk-build構建工具。
1.環(huán)境搭建
在SDK Tools中安裝NDK開發(fā)環(huán)境(File > Settings > Appearance & Behavior > System Settings > Android SDK > SDK Tools):

新建一個普通的Android項目,在main目錄下新建jni目錄,在此目錄下編寫原生代碼:

在main目錄下新建jniLibs目錄,此目錄為Android Stuido加載so文件的默認目錄,看下項目結構:

2.快捷鍵配置
打開File > Settings > Tools > External Tools選項,點擊【+】按鈕添加生成jni頭文件以及ndk-build命令的快捷工具:
生成頭文件

-
Name:javah-jni
工具名稱
-
Program:$JDKPath$/bin/javah
javah所在的路徑,$JDKPath$代表在環(huán)境變量中配置的JDK路徑。
-
Parameters:-jni -encoding UTF-8 -d $ModuleFileDir$\src\main\jni $FileClass$
命令參數(shù):
-jni代表生成JNI樣式的標頭文件,文件名為當前包名+類名($FileClass$)
-encoding代表編碼格式為UTF-8
-d代表指定頭文件的輸出路徑為jni目錄($ModuleFileDir$\src\main\jni )
-
Working directory:$ModuleFileDir$\src\main\java
工作目錄,$ModuleFileDir$為當前module的路徑。
javah用法:
javah [options] <classes>
其中, [options] 包括:
-o <file> 輸出文件 (只能使用 -d 或 -o 之一)
-d <dir> 輸出目錄
-v -verbose 啟用詳細輸出
-h --help -? 輸出此消息
-version 輸出版本信息
-jni 生成 JNI 樣式的標頭文件 (默認值)
-force 始終寫入輸出文件
-classpath <path> 從中加載類的路徑
-cp <path> 從中加載類的路徑
-bootclasspath <path> 從中加載引導類的路徑
<classes> 是使用其全限定名稱指定的
(例如, java.lang.Object)。
NDK構建

ndk-build的配置和javah-jni類似,其中C:\Tools\NDK\android-ndk-r14b\ndk-build.cmd為ndk-build構建工具的路徑,需要按照實際NDK安裝路徑進行修改。
如何調用
右擊項目選擇External Tools:

3.NDK開發(fā)
準備工作都做完了,下面進入正題,看下MainActivity:
public class MainActivity extends AppCompatActivity {
// 加載native-lib,不加lib前綴
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 將獲取的字符串顯示在TextView上
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
/**
* native-lib中的原生方法
*/
public native String stringFromJNI();
}
首先加載native-lib庫,然后調用其中的stringFromJNI方法,將其返回的字符串顯示在TextView上,此時還沒有native-lib庫,別急,繼續(xù)往下看:
對著MainActivity的類名右擊鼠標,選擇External Tools > javah-jni,控制臺執(zhí)行完命令后,會在jni目錄生成一個頭文件:

看下生成的頭文件:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_yl_ndkdemo_MainActivity */
#ifndef _Included_com_yl_ndkdemo_MainActivity
#define _Included_com_yl_ndkdemo_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_yl_ndkdemo_MainActivity
* Method: stringFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_yl_ndkdemo_MainActivity_stringFromJNI
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
可以看到第一行注釋寫到:這是自動生成的,不要去修改它。好,不改就不改,Go on:
在jni目錄中新建cpp類native-lib.cpp:
#include "com_yl_ndkdemo_MainActivity.h"
JNIEXPORT jstring JNICALL Java_com_yl_ndkdemo_MainActivity_stringFromJNI
(JNIEnv *env, jobject) {
return env->NewStringUTF("Hello from C++");
}
引用上文中生成的頭文件,返回一個字符串給Java層,方法名是通過 Java_包名類名方法名 的方式命名的。
接著在jni目錄下創(chuàng)建Android.mk和Application.mk配置文件,分別來看看:
Android.mk
# 當前路徑
LOCAL_PATH := $(call my-dir)
# 清除LOCAL_XXX變量
include $(CLEAR_VARS)
# 原生庫名稱
LOCAL_MODULE := native-lib
# 原生代碼文件
LOCAL_SRC_FILES =: native-lib.cpp
# 編譯動態(tài)庫
include $(BUILD_SHARED_LIBRARY)
在app的build.gradle文件中關聯(lián)Android.mk:
android {
...
externalNativeBuild {
ndkBuild {
path 'src/main/jni/Android.mk'
}
}
}
相當于執(zhí)行了【Link C++ Project with Gradle】:

Application.mk
# 原生庫名稱
APP_MODULES := native-lib
# 指定機器指令集
APP_ABI := armeabi armeabi-v7a arm64-v8a x86 x86_64 mips mips64
到這里基本的開發(fā)流程就已經完成了,運行程序看下效果:

分析一下APK文件,可以看到so文件已經打包進去了:

4.編譯so文件
對著jni目錄右擊鼠標,選擇External Tools > ndk-build,會在main目錄下生成libs和obj目錄,編譯出的so文件就在libs目錄下:

將so文件拷貝到jniLibs目錄下就可以正常使用了,也可以在app的build.gradle文件中設置so文件的路徑。
注意:編譯出的so文件就相當于java中的jar包,上文中的jni就相當于library,兩者不要重復使用。
5.遇到的問題
在ndk-build的過程中遇到了下面這行警告,但是沒有影響編譯so文件,沒有找到好的解決方法,有知道的同學可以留言告訴我,多謝!
Android NDK: WARNING: Unsupported source file extensions in jni/Android.mk for module native-lib
6.寫在最后
源碼已經上傳到GitHub上了,歡迎Fork,覺得還不錯就Start一下吧!