Android Studio 4.0.+NDK .so庫生成打包
JNI開發(fā)系列目錄
?
Android Studio 4.0.+NDK項目開發(fā)詳細教學
Android NDK與JNI的區(qū)別有何不同?
Android Studio 4.0.+NDK .so庫生成打包
Android JNI的深度進階學習
Android Studio 4.0.+NDK開發(fā) This files is not part of the project
目錄
?
前言
1. 什么是.so庫
2. cmake生成.so方案
1. 創(chuàng)建項目
2. 創(chuàng)建native函數(shù)
3. 測試JNI函數(shù)
4. 獲取.so文件
5. 生成多類型.so文件
6 .so文件測試
7. 小結(jié)
3. 傳統(tǒng)生成.so方案
1. 在Java類中聲明一個本地方法
2. 執(zhí)行指令javah獲得C聲明的.h文件
3. 獲得.c文件并實現(xiàn)本地方法
4. 創(chuàng)建Android.mk和Application.mk
5. 打包.so庫
6. 測試.so庫
4. external tools配置
1. javah配置
2. ndk-build配置
總結(jié)
?
總結(jié)
?
以Android studio 4.0.2來分析講解,gradle=6.1.1,如圖文和網(wǎng)上其他資料不一致,可能是別的資料版本較低而已
?
前言
涉及到一些算法或者底層驅(qū)動的時候,往往需要使用jni來開發(fā)。關(guān)于NDK和JNI如果還不了解,請查看我的另一篇博文《Android NDK與JNI的區(qū)別有何不同?》進行科普。
?
本篇博文主要是教學兩種.so庫的打包,穩(wěn)文中有詳細的圖文指導,跟著一步步操作就能學會.so文件的打包,對于NDK開發(fā)學習請閱讀我的《NDK開發(fā)系列》文章。
?
關(guān)于.so文件的生成有兩種方式可以提供給大家參考,一種是CMake自動生成法,另一種是傳統(tǒng)打包法。
?
1. 什么是.so庫
NDK為了方便使用,提供了一些腳本,使得更容易的編譯C/C++代碼,這個編譯文件為.so文件,它就C/C++庫,類似java庫.jar文件一樣。so是shared object的縮寫,見名思義就是共享的對象,機器可以直接運行的二進制代碼。大到操作系統(tǒng),小到一個專用軟件,都離不開.so,.so主要存在于Unix和Linux系統(tǒng)中。
?
在Android開發(fā)中它的生成是需要使用JNI將C/C++文件打包成so庫的,當然在其他開發(fā)軟件中,由其他工具將其打包成so庫。
?
.so文件在程序運行時就會加載,所以想使用Java調(diào)用.so文件,必有某個Java類運行時load了native庫,并通過JNI調(diào)用了它的方法。
?
2. cmake生成.so方案
使用該種方案生成.so文件,需要先創(chuàng)建一個支持Cmake的 C++ Project,如果不會創(chuàng)建項目請閱讀我的博文《Android Studio 4.0.+NDK項目開發(fā)詳細教學》
?
1. 創(chuàng)建項目
根據(jù)Android Studio 引導創(chuàng)建一個native C++ Project
?
2. 創(chuàng)建native函數(shù)
創(chuàng)建一個類,然后寫一個native 函數(shù),如:在這里插入圖片描述
?
?
鼠標懸空報紅的getData處,點擊Create JNI function。。。在這里插入圖片描述
?
在創(chuàng)建項目時,有自動生成一個native-lib.cpp文件,此時該文件中多了一個JNI getData函數(shù)在這里插入圖片描述
?
完善JNI getData函數(shù)在這里插入圖片描述
?
3. 測試JNI函數(shù)
public class MainActivity extends AppCompatActivity {
????static {
????????System.loadLibrary("native-lib");
????}
?
????@Override
????protected void onCreate(Bundle savedInstanceState) {
????????super.onCreate(savedInstanceState);
????????setContentView(R.layout.activity_main);
?
????????// Example of a call to a native method
????????TextView tv = findViewById(R.id.sample_text);
????????tv.setText(new MyNdkTest().getData());
????}
}
運行結(jié)果可見,JNI函數(shù)已被成功調(diào)用
?
在這里插入圖片描述
?
4. 獲取.so文件
將生成的.apk文件改為.zip文件,然后進行解壓縮,就能看到.so文件,默認支持x86_64在這里插入圖片描述
?
5. 生成多類型.so文件
默認生成的是x86_64,如果想支持多種庫架構(gòu),則可在app module的build.gradle中配置ndk支持。
?
android {
????.......
????defaultConfig {
????????...........
????????testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
????????externalNativeBuild {
????????????cmake {
????????????????cppFlags ""
????????????}
????????}
????????ndk {
????????????// 設(shè)置支持的 SO 庫構(gòu)架,注意這里要根據(jù)你的實際情況來設(shè)置
????????????abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
????????}
????}
????externalNativeBuild {
????????cmake {
????????????path "src/main/cpp/CMakeLists.txt"
????????????version "3.6.0"
????????}
????}
}
?
6 .so文件測試
新建一個普通的Android程序,將庫放入程序中運行在這里插入圖片描述
?
1將生成的.so庫放入lib文件夾中
2之前生成.so文件函數(shù)的類,在調(diào)用程序中依然需要相同的包名、文件名及方法名
3可以將庫的加載放在java文件中,當程序啟動時會自動加載.so類庫
?
在這里插入圖片描述
?
7. 小結(jié)
在Android Studio自動創(chuàng)建的native C++項目默認支持CMake方式,它支持JNI函數(shù)調(diào)用的入口在build.gradle中。
?
在這里插入圖片描述
?
CMake的NDKx項目它有自己一套運行流程
?
1 Gradle 調(diào)用外部構(gòu)建腳本CMakeLists.txt
2 CMake 按照構(gòu)建腳本的命令將 C++ 源文件 native-lib.cpp 編譯到共享的對象庫中,并命名為 libnative-lib.so ,Gradle 隨后會將其打包到APK中
3 運行時,應(yīng)用的MainActivity 會使用System.loadLibrary()加載原生庫。應(yīng)用就是可以使用庫的原生函數(shù)getData()。
?OK,自動生成.so庫的方法就講到這了,Android Studio幫我們自動化做了很多東西,所以so easy。 下面講講傳統(tǒng)的.so庫生成方案。
?
3. 傳統(tǒng)生成.so方案
使用該種方案生成.so文件一定要先配置好NDK,如果不清楚如何配置NDK,請閱讀一篇關(guān)于配置NDK的博文《Android Studio 4.0.+NDK項目開發(fā)詳細教學》。其打包.so文件的基本流程如下所述
?
1. 在Java類中聲明一個本地方法
public class NDKTest {
????private native ?int ?count();
}
?
2. 執(zhí)行指令javah獲得C聲明的.h文件
在terminal中cd 到\app\src\main\java目錄下執(zhí)行如下指令:
terminal可能出現(xiàn)不能用,則使用cmd命令行
?
javah -encoding utf-8 -d ../jni -jni ?com.xuanyuan.ndktest.NdKTest
?
// ??javah:生成頭文件指令
// ??-encoding utf-8:編碼格式 utf-8
// ??-d ../jni:生成的文件放到與java目錄同級的jni文件中,jni文件若不存在會自動創(chuàng)建
// ?-jni:當前目錄下生成.h文件,當前目錄是cd進入的目錄,這里是\app\src\main\java
// ?com.xuanyuan.ndktest.NdKTest:包名+類名
指命令后會在…\app\src\main\jni目錄下生成一個com_xuanyuan_ndktest_NdKTest.h文件
?
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_xuanyuan_ndktest_NdKTest */
?
#ifndef _Included_com_xuanyuan_ndktest_NdKTest
#define _Included_com_xuanyuan_ndktest_NdKTest
#ifdef __cplusplus
extern "C" {
#endif
/*
?* Class: ????com_xuanyuan_ndktest_NdKTest
?* Method: ???test
?* Signature: ()Ljava/lang/String;
?*/
JNIEXPORT jstring JNICALL Java_com_xuanyuan_ndktest_NdKTest_test
??(JNIEnv *, jobject);
?
#ifdef __cplusplus
}
#endif
#endif
另一種方式是使用External tools工具,使用其javah指令,該指令需要我們自行配置,后面會單獨講解。
?
在這里插入圖片描述
?
3. 獲得.c文件并實現(xiàn)本地方法
生成的.h文件中函數(shù)相當于是一個抽象方法,具體實現(xiàn)需要我們來自定義。此時在jni中重建一個demo.c文件,將com_xuanyuan_ndktest_NdKTest.h中的完全復制過來,將函數(shù)完整實現(xiàn)。
自己實現(xiàn)的C++方法要寫對,不太熟悉C++的人找C++工程師支援,否則無法制作.so文件
?
在這里插入圖片描述
?
4. 創(chuàng)建Android.mk和Application.mk
在jni目錄中創(chuàng)建Android.mk和Application.mk兩文件,并配置其參數(shù),兩個文件如不編寫或編寫正常會出現(xiàn)報錯。
?
//Android.mk 參數(shù)
?
//設(shè)置工作目錄,它用于在開發(fā)tree中查找源文件。宏my-dir由Build System提供,會返回Android.mk文件所在的目錄
LOCAL_PATH := $(call my-dir)
?
//CLEAR_VARS變量由Build System提供。指向一個指定的GNU Makefile,由它負責清理LOCAL_xxx類型文件,但不是清理LOCAL_PATH
//所有的編譯控制文件由同一個GNU Make解析和執(zhí)行,其變量是全局的。所以清理后才能便面相互影響,這一操作必須有
include $(CLEAR_VARS)
?
// LOCAL_MODULE模塊必須定義,以表示Android.mk中的每一個模塊,名字必須唯一且不包含空格
//Build System 會自動添加適當?shù)那熬Y和后綴。例如,demo,要生成動態(tài)庫,則生成libdemo.so。但請注意:如果模塊名字被定義為libabd,則生成libabc.so。不再添加前綴。
LOCAL_MODULE := DEMO
?
?
// 指定參與模塊編譯的C/C++源文件名。不必列出頭文件,build System 會自動幫我們找出依賴文件。缺省的C++ 源碼的擴展名為.cpp。
LOCAL_SRC_FILES := demo.c
?
?
// BUILD_SHARED_LIBRARY是Build System提供的一個變量,指向一個GUN Makefile Script。它負責收集自從上次調(diào)用include $(CLEAR_VARS)后的所有LOCAL_xxxxinx。并決定編譯什么類型
//1. BUILD_STATIC_LIBRARY:編譯為靜態(tài)庫
//2. BUILD_SHARED_LIBRARY:編譯為動態(tài)庫
//3. BUILD_EXECUTABLE:編譯為Native C 可執(zhí)行程序
//4. BUILD_PREBUILT:該模塊已經(jīng)預先編譯
include $(BUILD_SHARED_LIBRARY)
?
**************************************************
#Application.mk 參數(shù)
// 默認生成支持的多種類型.so
APP_ABI := all
?
//APP_PLATFORM := android-16不配置,打包.so會出錯
APP_PLATFORM := android-16
如果我們需要進行多個C++文件一起編譯或者已有的.so參與編譯,這里給出配置示例
?
# Android.mk
????// 示例1:單個c++文件參與編譯
????include $(CLEAR_VARS)
????// 編譯出來的文件名
????LOCAL_MODULE := DEMO
????LOCAL_SRC_FILES := demo.c
?
????// 多個c++文件參與編譯
????include $(CLEAR_VARS)
????// 編譯出來的文件名
????LOCAL_MODULE := DEMO
????LOCAL_SRC_FILES := \
????????????test1.cpp \
??????????????test/ftest2.cpp \
???????????????test3.c \
?
// 示例2:.so文件參與模塊編譯
????include $(CLEAR_VARS)
?????// 編譯出來的文件名
????LOCAL_MODULE := DEMO2
????LOCAL_SRC_FILES := test/DEMO.so
????// 設(shè)置可被依賴
????include $(PREBUILT_SHARED_LIBRARY)
?
5. 打包.so庫
各種文件準備好后,cd到\app目錄下,執(zhí)行命令 ndk-build即可,我沒有用terminal,不知啥原因用不了。
執(zhí)行命令后會在libs文件夾中生成.so文件,默認支持4種命令庫。
?
?
同樣另一種方式是使用External tools工具,使用其ndk-build指令,該指令需要我們自行配置,后面會單獨講解。
?
在這里插入圖片描述
?
6. 測試.so庫
測試.so庫生成完畢,正??捎?。
?
在這里插入圖片描述
?
4. external tools配置
在上面制作.h文件和.so文件中要在cmd或者terminal中輸入javah、ndk-build命令比較麻煩,我們可以在external tools中進行配置,一次配置,終生幸福!
?
在這里插入圖片描述
?
1. javah配置
?
在這里插入圖片描述
?
//javah.exe的地址
Program:$JDKPath$\bin\javah
//生成.h文件的路徑指定在jni文件中,$FileClass$為源.java文件
Arguments:-encoding UTF-8 -d ../jni -jni $FileClass$
//進行編譯成.h文件的源.java文件目錄
Working directory:$ProjectFileDir$\app\src\main\java
?
2. ndk-build配置
?
在這里插入圖片描述
?
//ndk-build.cmd路徑
Program:$ModuleSdkPath$/ndk/21.3.6528147/ndk-build.cmd
//生成的.so文件存放位置
Arguments:NDK_LIBS_OUT=$ProjectFileDir$\app\libs
//編譯源文件的目錄
Working directory:$ProjectFileDir$\app\src\main
?
總結(jié)
本篇博文主要講解了兩種.so庫打包生成的方式,對于NDK開發(fā)的其他知識點學習請繼續(xù)閱讀我的系列文章。
?
在我們使用.so文件時,一定要記得做好配置,否則會出現(xiàn)無法找到.so庫的異常
?
android {
????compileSdkVersion 30
????buildToolsVersion "30.0.2"
????...
????sourceSets {
????????main {
????????????jniLibs.srcDirs = ['libs']
????????}
????}
}
?