android studio ndk-build 編譯C生成.so檔案(ndk基礎篇)
一、概要
最近專案需要,要把程式碼中加密的部分打包成so檔案,剛開始接觸的時候真是痛苦呀,網上好多資料,都不是很詳細,步驟也不清晰,所以我整理了一下,希望大家喜歡。
現在android studio打包so檔案有兩種方式,第一種是ndk-build編譯專案,還有一種用CMake指令碼構建專案,今天主要介紹ndk-build的編譯過程
二、ndk-build 構建過程
1,下載NDK和構建工具
正所謂:“工欲善其事必先利其器”,我們今天的主要工具是Android Studio2.3版本,至於 Android Studio環境搭建,sdk,jdk什麼的,你們自己去弄,這裡主要是講解ndk編譯c語言的配置。
為您的應用編譯和除錯原生程式碼,您需要以下元件:
- Android 原生開發工具包 (NDK):這套工具集允許您為 Android 使用 C 和 C++ 程式碼,並提供眾多平臺庫,讓您可以管理原生 Activity 和訪問物理裝置元件,例如感測器和觸控輸入。
- CMake:一款外部構建工具,可與 Gradle 搭配使用來構建原生庫。如果您只計劃使用 ndk-build,則不需要此元件。
- LLDB:一種除錯程式,Android Studio 使用它來除錯原生程式碼。
您可以使用 SDK 管理器安裝這些元件:
- 在開啟的專案中,從選單欄選擇 Tools > Android > SDK Manager(或者在頂部工具欄中直接點選SDK Manager, 下圖的標記就是從這裡點選的)
- 點選 SDK Tools 標籤。
- 選中 LLDB、CMake 和 NDK 旁的複選框,如圖 1 所
- 點選 Apply,然後在彈出式對話方塊中點選 OK。
- 安裝完成後,點選 Finish,然後點選 OK。
SDK環境配置:
提前新建一個測試用的專案 NdkDemo
- 切換到Project工程目錄,開啟Project Structure (方法一,直接在工具欄開啟,如下圖所示,方法二, 右鍵工程目錄 -> Open Module Settings)
- 選擇左邊欄 SDK Location
- 在Android NDK Location 位置,選擇 ndk安裝包的路徑,一般放在sdk目錄下(下圖是我mac系統放置ndk的路徑,windows系統也類似,大家自己選擇)
- 點選 OK
- 為最外層工程目錄下的gradle.properties的檔案末尾加上android.useDeprecatedNdk=true這段程式碼(如果沒有這個目錄,自己新建一個),如下圖所示:
Ok,上面我們把環境給配置好了,下面我們就可以寫Java和C程式碼了
2,Java程式碼和C程式碼的編寫過程
1,首先新建一個java類JNIUtils.java,程式碼如下
public class JNIUtils { // 載入native-jni static { System.loadLibrary("native-jni"); } //java調C中的方法都需要用native宣告且方法名必須和c的方法名一樣 public native String stringFromJNI(); }
2,重新Make Project一下工程如下圖2-1,完成後會在工程目錄 ... /NdkDemo/app/build/intermediates/classes/debug/com/niwoxuexi/ndkdemo 看到自己編譯後的classes檔案JNIUtils.class如下圖2-2所示:
圖:2-1
圖:2-2
3,用javah工具生成標頭檔案
1) 首先新建一個java類JNIUtils.java,
2) 程式碼在studio開啟Terminal命令列工具,開啟步驟是View->Tool Windows->Terminal (或者在下邊的工具欄中直接開啟或直接按Alt+F12)
在命令列中先進入到工程的main目錄下
3) 輸入命令:javah -d jni -classpath 自己編譯後的class檔案的絕對路徑
例如:
javah -d jni -classpath /Users/zhuxiaocheng/android/workspace/NdkDemo/app/build/intermediates/classes/debug com.niwoxuexi.ndkkemo.JNIUtils
注意: 1, debug後的空格
2, windows 系統路徑中的檔案的分割線是 '\' 而不是mac系統的 '/'
4)按回車之後就會在main目錄下生成jni資料夾,同時生成.h檔案 如下圖所示
5) 現在我們在jni目錄下新建一個 native-lib.c 的 c 檔案,內容如下
// // Created by 朱孝誠 on 2017/8/30. // #include "com_niwoxuexi_ndkdemo_JNIUtils.h" /** * 上邊的引用標籤一定是.h的檔名家字尾,方法名一定要和.h檔案中的方法名稱一樣 */ JNIEXPORT jstring JNICALL Java_com_niwoxuexi_ndkdemo_JNIUtils_stringFromJNI (JNIEnv *env, jobject ojb){ return (*env) -> NewStringUTF(env,"Hello, I'm from jni"); }
6)會後在app的build.gradle配置檔案中新增如下程式碼:
//ndk編譯生成.so檔案 ndk { moduleName "native-lib" //生成的so名字 abiFilters "armeabi", "armeabi-v7a", "x86" //輸出指定三種abi體系結構下的so庫。 }
如圖所示:
7) 最後在我們來測試一下,只需要在MainActivity中呼叫一下C就可以了,程式碼如下:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView textView = (TextView) findViewById(R.id.text); textView.setText(new JNIUtils().stringFromJNI()); } }
8) 直接執行專案,結果如下所示:
ok,沒問題,可以呼叫,其實也沒有想象中的那麼難。
三、呼叫編譯過的 .so 庫
上邊編譯完成了,有人會問:我要的是編譯後的.so庫,別人用的時候直接拿來用就可以了,那編譯後的.so庫在哪呢?不要著急,請看下圖:
根據這個路徑就可以找到指定輸出的三種體系結構下的.so庫檔案,然後把.so檔案複製出來,如下圖所示的放到相應libs的資料夾,
1, 把複製的so包,放到專案的libs目錄下
2, 在app module 下的buide.gradle 中新增下面程式碼:
//放在libs目錄中 sourceSets { main { jniLibs.srcDirs = ['libs'] } }
如圖所示:
ok, 這樣就可以了
四、總結
也沒什麼好總結的,直接按照上面步驟一步一步來,就可以了,jni呼叫過程有什麼問題,歡迎留言!