AndroidNDK——makefile語法詳解

1、編譯流程詳解

編譯流程html

  • 編譯:將高級語言編寫的程序轉換爲二進制代碼可執行性目標程序的過程
  • 四大過程:預處理、編譯、彙編、連接

一、預處理

完成宏替換、文件引入,以及去除空行、註釋等,爲下一步的編譯作準備;也就是對各類預處理命令進行處理,包括頭文件的包含、宏定義的擴展、條件編譯的選擇等。java

// test.c文件內容
#include <stdio.h>
int main(){
	printf("hello world!\n");
	return 0;
}
複製代碼

對test.c文件進行預處理:android

$ gcc -E test.c -o test.i
複製代碼
  • 選項-E:讓gcc在預處理結束後中止編譯,test.i文件爲預處理後輸出的文件。
  • 選項-o:指定輸出文件。

此時,test.i 就是 test.c 預編譯後的產物,體積會增大,此時test.i仍是一個文本文件,能夠用文本編譯器打開查看。shell

二、編譯

  • 將預處理後的代碼編譯成彙編代碼。在這個階段中,首先要檢查代碼的規範性、是否有語法錯誤等,以肯定代碼實際要作的工做,在檢查無誤後,再把代碼翻譯成彙編語言。
  • 編譯程序執行時,先分析,後綜合。分析,就是指詞法分析、語法分析、語義分析和中間代碼生成。綜合,就是指代碼優化和代碼生成。
  • 大多數的編譯程序直接產生機器語言的目標代碼,造成可執行的目標文件,也有的是先產生彙編語言一級的符號代碼文件,再調用匯編程序進行翻譯和加工處理,最後產生可執行的機器語言目標文件。
extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;

extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 868 "/usr/include/stdio.h" 3 4

# 2 "test.c" 2

# 3 "test.c"
int main(){
 printf("hello world\n");
 return 0;
}
複製代碼

上面是預處理後test.i文件的部份內容,下面對test.i文件進行編譯:編程

$ gcc -S test.i -o test.s
複製代碼
  • 選項-S:讓gcc在編譯結束後中止編譯過程,"test.s"文件爲編譯後生成的彙編代碼。

此時,test.s 就是 test.i 文件彙編後的產物,一樣也能夠用文本編譯器打開查看。ubuntu

三、彙編

彙編就是把編譯階段生成的".s"文件轉成二進制目標代碼,也就是機器代碼(01序列)。數組

.file	"test.c"
	.text
	.section	.rodata
.LC0:
	.string	"hello world"
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	leaq	.LC0(%rip), %rdi
	call	puts@PLT
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0"
	.section	.note.GNU-stack,"",@progbits
複製代碼

上面是編譯後生成的test.s文件裏的彙編代碼,下面對test.s文件進行彙編:bash

$ gcc -c test.s -o test.o
複製代碼
  • 選項-c:讓gcc在彙編結束後中止編譯過程,"test.o"文件爲彙編後生成的機器碼目標文件。

四、連接

連接就是將多個目標文件以及所需的庫文件連接生成可執行目標文件的過程。ide

下面對test.o進行連接:模塊化

$ gcc test.o -o test
$ ./test
hello world!
複製代碼
  • 選項-o:本質上是一個重命名選項。不使用-o選項時,默認生成的是a.out文件。這裏生成的是可執行文件test。
  • ./test執行後輸出hello world!

五、簡化

通常狀況下,咱們會使用gcc命令,一步生成可執行文件,簡化編譯流程:

$ gcc -o test test.c
$ ./test
hello world!
複製代碼

2、 靜態庫與動態庫原理

一、 靜態庫

1) 什麼是靜態庫

  • 靜態庫實際就是一些目標文件(通常以.o結尾)的集合,靜態庫通常以.a結尾,只用於生成可執行文件階段。
  • 在連接步驟中,連接器將從庫文件取得所需代碼,複製到生成的可執行文件中。這種庫稱爲靜態庫。其特色是可執行文件中包含了庫代碼的一份完整拷貝,在編譯過程當中被載入程序中。缺點就是屢次使用就會有多份冗餘拷貝,而且對程序的更新、部署和發佈會帶來麻煩,若是靜態庫有更新,那麼全部使用它的程序都須要從新編譯、發佈。

2) 生成靜態庫

  • 首先生成test.o目標文件。
  • 使用ar命令將test.o打包成libtest.a靜態庫。
# 生成目標文件
$ gcc -c test.c -o test.o
# 使用ar命令將目標文件打包成靜態庫
$ ar libtest.a test.o
ar: creating libtest.a
# 使用ar t libtest.a 查看靜態庫內容
$ar t libtest.a
test.o
複製代碼

選項rcs各自的含義:

  • 選項r:更新或增長新文件到靜態庫中。
  • 選項c:建立一個庫,無論存在與否,都建立。
  • 選項s:建立文檔索引,在建立較大的庫時,可以加快編譯速度。

二、 動態庫

1)什麼是動態庫

  • 動態庫在連接階段沒有被複制到程序中,而是在程序運行時由系統動態加載到內存中供程序調用。
  • 系統只需載入一次動態庫,不一樣的程序能夠獲得內存中相同動態庫的副本,所以節省了不少內存。

2)生成動態庫

  • 首先生成test.o目標文件。
  • 使用-shared和-fPIC參數生成動態庫。
# 首先生成目標文件
$ gcc -c test.c -o test.o
# 使用-fPIC和-shared生成動態庫
$ gcc -shared -fPIC -o libtest.so test.o
複製代碼

fPIC:全稱是 Position Independent Code, 用於生成位置無關代碼。

三、案例

編寫一個工具方法(tool.h + tool.c文件),查找出數組的最大值:

// tool.h 文件
int find_max(int arr[], int n);

// tool.c 文件
#include "tool.h"
int find_max(int arr[], int n){
	int max = arr[0];
	int i;
	for(i = 0; i < n; i++){
		if(arr[i] > max){
			max = arr[i];
		}
	}
	return max;
}
複製代碼

在main.c文件中,調用tool.h的find_max函數:

// main.c 文件
#include <stdio.h>
#include "tool.h"

int main(){
	int arr[] = {1,3,5,8,2};
	int max = find_max(arr, 5);
	printf("max = %d\n", max);
	return 0;
}
複製代碼

1)編譯&使用靜態庫

編譯tool靜態庫:

# 編譯tool.c。能夠省略"-o tool.o",默認gcc會生成一個與tool.c同名的.o文件。
$ gcc -c tool.c
 # 編譯生成libtool.a靜態庫
$ ar rcs libtool.a tool.o
 # 編譯main可執行文件。
# -l用來指定要連接的庫,後面接庫的名字;-L表示編譯程序根據指定路徑尋找庫文件。
$ gcc -o main main.c -L. -ltool
 $ ./main
max = 8
複製代碼

能夠用ldd命令查看main文件依賴了哪些庫:

$ ldd main
複製代碼

2)編譯&使用動態庫

# 編譯tool.c,生成tool.o
$ gcc -c tool.c
 # 編譯生成libtool.so動態庫
$ gcc -shared -fPIC -o libtool.so tool.o
 # 編譯main可執行文件
$ gcc -o main main.c -L. -ltool
 $ ./main
./main: error while loading shared libraries: libtool.so: cannot open shared object file: No such file or directory
複製代碼

注意,當靜態庫與動態庫同名時,gcc會優先加載動態庫。即,此時目錄下即有libtool.a,又有libtool.so,編譯main時指定了-ltool,gcc會連接libtool.so!

能夠用ldd命令查看main文件依賴了哪些庫:

$ ldd main
複製代碼

能夠看到,libtool.so找不到,這是由於在系統的默認動態連接庫路徑下沒有這個libtool.so文件,能夠在執行以前,給main設置環境變量解決:

# 將當前目錄設置到環境變量中
$ LD_LIBRARY_PATH=. ./main
max = 8
複製代碼

LD_LIBRARY_PATH 指定查找共享庫,即動態連接庫時,除默認路徑之外,其餘的路徑。

四、區別總結

載入時刻不一樣:

  • 靜態庫:在程序編譯時會連接到目標代碼中,程序運行時再也不須要靜態庫,所以體積較大。並且每次編譯都須要載入靜態代碼,所以內存開銷大。
  • 動態庫:在程序編譯時不會被連接到目標代碼中,而是在程序運行時才被載入,程序運行時須要動態庫存在,所以體積較小。並且系統只需載入一次動態庫,不一樣程序能夠獲得內存中相同的動態庫副本,所以內存開銷小。

3、makefile走讀與語法基礎

一、makefile是什麼

在一個工程中,源文件不少,按類型、功能、模塊分別被存放在若干個目錄中,須要按必定的順序、規則進行編譯,這時就須要使用到makefile。

  • makefile定義了一系列的規則來指定,哪些文件須要先編譯,哪些文件須要從新編譯,如何進行連接等操做。
  • makefile就是「自動化編譯」,告訴make命令如何編譯和連接。

makefile是make工具的配置腳本,默認狀況下,make命令會在當前目錄下去尋找該文件(按順序找尋文件名爲「GNUmakefile」「makefile」「Makefile」的文件)。

在這三個文件名中,最好使用「Makefile」這個文件名,由於,這個文件名第一個字符爲大寫,這樣有一種顯目的感受。 最好不要用「GNUmakefile」,這個文件是GNU的make識別的。有另一些make只對全小寫的「makefile」文件名敏感。 可是基本上來講,大多數的make都支持「makefile」和「Makefile」這兩種默認文件名。

固然,配置文件的文件名也能夠不是makefile,好比:config.debug,這時須要經過 -f--file 指定配置文件,即:

# 使用-f
$ make -f config.debug
# 使用--file
$ make --file config.debug
複製代碼

二、makefile裏有什麼

makefile包含如下五個:

  • 顯示規則:說明了如何生成一個或多個目標文件。
  • 隱晦規則:make有自動推導功能,能夠用隱晦規則來簡寫makefile。
  • 變量定義:在makefile中能夠變量一系列的變量,變量通常是字符串,相似c語言中的宏,當makefile被執行時,其中的變量都會被擴展相應的位置上。
  • 文件指示:包括3個部分:①在makefile引用另外一個makefile,相似C語言中的include;②根據條件指定makefile中的有效部分,相似C語言中的預編譯#if同樣;③定義多行的命令。
  • 註釋:只有行註釋,使用#字符表示。

三、makefile的規則

target ... : prerequisites ...
	command
或者:
target ... : prerequisites ... ; command
複製代碼

若prerequisites與command在同一行,須要用;分隔。 若prerequisites與command不在同一行,則command前面須要用tab鍵開頭。 另外,若是命令太長,能夠用\做爲換行符。

  • target:目標文件。能夠是ObjectFile,也能夠是執行文件,還能夠是標籤(Label);若是有多個文件,能夠用空格隔開;可使用通配符。
  • prerequisites:依賴文件,既要生成那個target所須要的文件或其餘target。
  • command:make須要執行的命令。

makefile的做用:

告訴make,文件的依賴關係,以及如何生成目標文件。prerequisites中,若是有一個及以上的文件比target要新的話,target就會被認爲是過期的,須要從新生成,command就會被執行,從而生成新的target。

四、makefile示例

# 當前目錄存在main.c、tool.c、tool.h三個文件
# 下面是makefile文件內容
main: main.o tool.o
	gcc main.o tool.o -o main
.PHONY: clean
clean:
	-rm main *.o
-----------------------------
// 執行 make 後輸出以下:
cc	-c -o main.o main.c
cc	-c -o tool.o tool.c
gcc main.o tool.o -o main
// 而且生成了一個可執行文件main
複製代碼
  • -o:指定可執行文件的名稱。
  • clean:標籤,不會生成「clean」文件,這樣的target稱之爲「僞目標」,僞目標的名字不能和文件名重複。clean通常放在文件最後。
  • .PHONY:顯示地指明clean是一個「僞目標」。

make會自動推導main.o、tool.o如何生成。 僞目標的名字不能和文件名重複,即當前目錄下,不能有clean文件。 能夠經過 make clean 執行刪除命令。

五、makefile如何工做

默認方式下,輸入make命令後:

  • make會在當前目錄下找名字叫「Makefile」或「makefile」的文件。
  • 若是找到,它會找文件中第一個目標文件(target),並把這個target做爲最終的目標文件,如前面示例中的「main」。
  • 若是main文件不存在,或main所依賴的.o文件的修改時間要比main文件要新,那麼它會執行後面所定義的命令來生成main文件。
  • 若是main所依賴的.o文件也存在,那麼main會在當前文件中找目標爲.o文件的依賴性,若找到則根據規則生成.o文件。
  • make再用.o文件聲明make的終極任務,也就是執行文件「main」。

六、makefile中使用變量

objects = main.o tool.o
main: $(objects)
	gcc $(objects) -o main
.PHONY: clean
clean:
	-rm main $(objects)
-----------------------------
// 執行 make 後輸出以下:
cc	-c -o main.o main.c
cc	-c -o tool.o tool.c
gcc main.o tool.o -o main
複製代碼
  • 爲了makefile的易維護,在makefile中咱們可使用變量。makefile的變量也就是一個字符串,理解成C語言中的宏可能會更好。
  • 好比:咱們聲明一個變量,叫objects,因而,咱們就能夠很方便地在咱們的makefile中以「$(objects)」的方式來使用這個變量了。

七、makefile中引用其餘的makefile

# 語法格式
include <filename>

# 舉個例子,你有這樣幾個 Makefile:a.mk、b.mk、c.mk,還有一個文件叫 # foo.make,以及一個變量$(bar),其包含了 e.mk 和 f.mk

include foo.make *.mk $(bar)
# 等價於:
include foo.make a.mk b.mk c.mk e.mk f.mk

# 若是文件找不到,而你但願make時不理會那些沒法讀取的文件而繼續執行
# 能夠在include前加一個減號「-」,如:
-include <filename>
複製代碼

使用include關鍵字能夠把其它Makefile包含進來,include語法格式: include <filename>

八、環境變量MAKEFILES

MAKEFILES

若是當前環境中字義了環境變量 MAKEFILES,那麼,make會把這個變量中的值作一個相似於 include 的動做。這個變量中的值是其它的 Makefile,用空格分隔。只是,它和include不一樣的是,從這個環境中引入的Makefile的「目標」不會起做用,若是環境變量中定義的文件發現錯誤,make也會不理。可是建議不要使用這個環境變量,由於只要這個變量一被定義,那麼當你使用make時,全部的Makefile都會受到它的影響。 也許有時候Makefile出現了奇怪的事,那麼能夠查看當前環境中有沒有定義這個變量。

九、Makefile預約義變量

變量名 描述 默認值
CC C語言編譯器的名稱 cc
CPP C語言預處理器的名稱 $(CC) -E
CXX C++語言編譯器的名稱 g++
RM 刪除文件程序的名稱 rm -f
CFLAGS C語言編譯器的編譯選項
CPPFLAGS C語言預處理器的編譯選項
CXXFLAGS C++語言編譯器的編譯選項

十、Makefile自動變量

自動變量 描述
$* 目標文件的名稱,不包含擴展名
$@ 目標文件的名稱,包含擴展名
$+ 全部的依賴文件,以空格隔開,可能含有重複的文件
$^ 全部的依賴文件,以空格隔開,不重複
$< 依賴項中第一個依賴文件的名稱
$? 依賴項中全部比目標文件新的依賴文件

十一、Makefile函數

define本質是定義一個多行的變量,沒辦法直接調用,但能夠在call的做用下,看成函數來使用。

不帶參數

define FUNC
$(info echo "hello")
endef

$(call FUNC)
--------------------
輸出:hello
複製代碼

帶參數

define FUNC1
$(info echo $(1)$(2))
endef

$(call FUNC1,hello,world)
--------------------
輸出:hello world
複製代碼

十二、make的工做流程

GNU的make工做時的執行步驟以下:

  1. 讀入全部的Makefile。
  2. 讀入被include的其它Makefile。
  3. 初始化文件中的變量。
  4. 推導隱晦規則,並分析全部規則。
  5. 爲全部的目標文件建立依賴關係鏈。
  6. 根據依賴關係,決定哪些目標要從新生成。
  7. 執行生成命令。

1~5是第一階段,6~7爲第二階段。在第一階段中,若是定義的變量被使用了,那麼make會把變量展開在使用的位置,可是make並非徹底的立刻展開,若是變量出如今依賴關係的規則中,那麼只有當這條依賴被決定要使用的時候,變量纔會被展開。

3、Android.mk基礎

一、Android.mk簡介

Android.mk是一個向Android NDK構建系統描述NDK項目的GNU makefile片斷。主要用來編譯生成如下幾種:

  • APK程序:通常的Android應用程序,系統級別的直接push便可。
  • JAVA庫:Java類庫,編譯打包生成JAR文件。
  • C\C++應用程序:可執行的C\C++應用程序。
  • C\C++靜態庫:編譯生成C\C++靜態庫,並打包成.a文件。
  • C\C++共享庫:編譯生成共享庫,並打包成.so文件。

二、Android.mk基本格式

這是一個簡單的Android.mk文件的內容:

# 定義模塊當前路徑(必須定義在文件開頭,只需定義一次)
LOCAL_PATH := $(call my-dir)

# 清空當前環境變量(LOCAL_PATH除外)
include $(CLEAR_VARS)

# 當前模塊名(這裏會生成libhello-jni.so)
LOCAL_MODULE := hello-jni

# 當前模塊包含的源代碼文件
LOCAL_SRC_FILES := hello-jni.c

# 表示當前模塊將被編譯成一個共享庫
include $(BUILD_SHARED_LIBRARY)
複製代碼
  • my-dir:是由編譯系統提供的宏函數,返回當前.mk文件的路徑。
  • CLEAR_VARS:是由編譯系統提供的變量,指向一個特定的GNU makefile片斷,能夠清除除了LOCAL_PATH之外的以LOCAL_開頭的變量,如:LOCAL_MODULELOCAL_SRC_FILES。這樣作是由於編譯系統在單次執行中,會解析多個構建文件和模塊定義,而以LOCAL_開頭的變量是全局變量,因此描述每一個模塊以前,都會聲明CLEAR_VARS變量,能夠避免衝突。
  • LOCAL_MODULE:定義當前模塊名,模塊名必須惟一,並且不能包含空格。模塊名爲"hello-jni"時,會生成libhello-jni.so,若是模塊名爲"libhello-jni"時,則生成的仍是libhello-jni.so!
  • LOCAL_SRC_FILES:當前模塊包含的源文件,當源文件有多個時,用空格隔開。

三、編譯多個共享庫

一個Android.mk可能編譯產生多個共享庫模塊。

LOCAL_PATH := $(call my-dir)

# 模塊1
include $(CLEAR_VARS)
LOCAL_MODULE := module1
LOCAL_SRC_FILES := module1.c
include $(BUILD_SHARED_LIBRARY)

# 模塊2
include $(CLEAR_VARS)
LOCAL_MODULE := module2
LOCAL_SRC_FILES := module2.c
include $(BUILD_SHARED_LIBRARY)
複製代碼

這裏會產生libmodule1.so和libmodule2.so兩個動態庫。

四、編譯靜態庫

雖然Android應用程序不能直接使用靜態庫,但靜態庫能夠用來編譯動態庫。好比在將第三方代碼添加到原生項目中時,能夠不用直接將第三方源碼包括在原生項目中,而是將第三方源碼編譯成靜態庫,而後併入共享庫。

LOCAL_PATH := $(call my-dir)

# 第三方AVI庫
include $(CLEAR_VARS)
LOCAL_MODULE := avilib
LOCAL_SRC_FILES := avilib.c platform_posix.c
include $(BUILD_STATIC_LIBRARY)

# 原生模塊
include $(CLEAR_VARS)
LOCAL_MODULE := module
LOCAL_SRC_FILES := module.c
# 將靜態庫模塊名添加到LOCAL_STATIC_LIBRARIES變量
LOCAL_STATIC_LIBRARIES := avilib
include $(BUILD_SHARED_LIBRARY)
複製代碼

五、使用共享庫共享通用模塊

靜態庫能夠保證源代碼模塊化,可是當靜態庫與共享庫相連時,它就變成了共享庫的一部分。在多個共享庫的狀況下,多個共享庫與靜態庫鏈接時,須要將通用模塊的多個副本與不一樣的共享庫重複相連,這樣就增長了APP的大小。這種狀況,能夠將通用模塊做爲共享庫。

LOCAL_PATH := $(call my-dir)

# 第三方AVI庫
include $(CLEAR_VARS)
LOCAL_MODULE := avilib
LOCAL_SRC_FILES := avilib.c platform_posix.c
include $(BUILD_SHARED_LIBRARY)

# 原生模塊1
include $(CLEAR_VARS)
LOCAL_MODULE := module1
LOCAL_SRC_FILES := module1.c
LOCAL_SHARED_LIBRARIES := avilib
include $(BUILD_SHARED_LIBRARY)

# 原生模塊2
include $(CLEAR_VARS)
LOCAL_MODULE := module2
LOCAL_SRC_FILES := module2.c
LOCAL_SHARED_LIBRARIES := avilib
include $(BUILD_SHARED_LIBRARY)
複製代碼

以上的作法必須基於同一個NDK項目。

六、在多個NDK項目間共享模塊

  • 首先將avilib源代碼移動到NDK項目之外的位置,好比:C:\android\shared-modules\transcode\avilib
  • 做爲共享庫模塊,avilib須要有本身的Android.mk文件。
  • transcode/avilib爲參數調用函數宏import-module添加到NDK項目的Android.mk文檔末尾。

import-module函數宏在NDK版本r5之後纔有。

# avilib模塊本身的Android.mk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := avilib
LOCAL_SRC_FILES := avilib.c platform_posix.c
include $(BUILD_SHARED_LIBRARY)
---------------------------------------------
# 使用共享模塊的NDK項目1的Android.mk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := module1
LOCAL_SRC_FILES := module1.c
LOCAL_SHARED_LIBRARIES := avilib
include $(BUILD_SHARED_LIBRARY)
$(call import-module,transcode/avilib)
---------------------------------------------
# 使用共享模塊的NDK項目2的Android.mk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := module2
LOCAL_SRC_FILES := module2.c
LOCAL_SHARED_LIBRARIES := avilib
include $(BUILD_SHARED_LIBRARY)
$(call import-module,transcode/avilib)
複製代碼

小心細的你在看到$(call import-module,transcode/avilib)這句時,必定會問,爲何NDK會知道要去C:\android\shared-modules\目錄下面找transcode/avilib呢?是的,NDK並無這麼智能,默認狀況下,import-module函數宏只會搜索AndroidNDK下面的sources目錄。

如個人NDK路徑是:C:\Users\lqr\AppData\Local\Android\Sdk\ndk-bundle,那麼import-module函數宏默認的尋找目錄就是C:\Users\lqr\AppData\Local\Android\Sdk\ndk-bundle\sources

要正確使用import-module,就須要對NDK_MODULE_PATH進行配置,把C:\android\shared-modules\配置到環境變量中便可,當有多個共享庫目錄時,用;隔開。

更多關於import-module的介紹,請翻到文末查看。

七、使用預編譯庫

  • 想在不發佈源代碼的狀況下將模塊發佈給他人。
  • 想使用共享庫模塊的預編譯版來加速編譯過程。

如今咱們手上有第三方預編譯好的庫libavilib.so,想集成到本身項目中使用,則須要在Android.mk中進行以下配置:

# 預編譯共享模塊的Android.mk文件
LOCAL_PATH := $(call my-dir)
# 第三方預編譯的庫
include $(CLEAR_VARS)
LOCAL_MODULE := avilib
LOCAL_SRC_FILES := libavilib.so
include $(PREBUILT_SHARED_LIBRARY)
複製代碼

能夠看到,LOCAL_SRC_FILES指向的再也不是源文件,而是預編譯好的libavilib.so,相對於LOCAL_PATH的位置。

八、編譯獨立的可執行文件

爲了方便測試和進行快速開發,能夠編譯成可執行文件。不用打包成APK就能夠獲得到Android設備上直接執行。

# 獨立可執行模塊的Android.mk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := module
LOCAL_SRC_FILES := module.c
LOCAL_STATIC_LIBRARIES := avilib
include $(BUILD_EXECUTABLE)
複製代碼

九、注意事項

假如咱們本地庫libhello-jni.so依賴於libTest.so(可使用NDK下的ndk-depends查看so的依賴關係)。

  • 在Android6.0版本以前,須要在加載本地庫前先加載被依賴的so。
  • 在Android6.0版本以後,不能再使用預編譯的動態庫(靜態庫沒問題)。
// Android 6.0版本以前:
System.loadlibrary("Test");
System.loadlibrary("hello-jni");

// Android 6.0版本以後: 
System.loadlibrary("hello-jni");
複製代碼

4、附加

一、import_module 詳解

如下內容引用自 《import-module的注意事項與NDK_MODULE_PATH的配置》

相關文章
相關標籤/搜索