NDK開發入門終極教程


 

0 前言

NDK技術的淵源始於3年前,使用so文件的時候了解到NDK技術,並且C語言一直是強項,就鼓搗起NDK開發。在AndroidStduio還沒推廣的年代,基於eclipse搭建NDK開發環境需要安全依賴開發工具,並且調試起來具備難度。隨后AndroidStudio也先后支持nkd-buildcmake使用NDK開發。

參見之前的博客:

eclipse下使用NDK開發so庫

AndroidStudio配置NDK開發環境

1 准備工作

1.1 下載 NDK

當前 NDK 穩定版已經 發布到 r15c 。附上各個平台的下載地址:

  1. android-ndk-r15c-windows-x86

  2. android-ndk-r15c-windows-x86_64

  3. android-ndk-r15c-darwin-x86_64

  4. android-ndk-r15c-linux-x86_64

1.2 添加NDK依賴

解壓下載好的文件在本地,在 AndroidStudio 工程配置(注意不是 AndroidStudio 工具配置)中指定 NDK 路徑。

或者在local.properties文件中指定NDK路徑。

1.3 添加cmake支持

在 AndroidStudio 工具配置中,選擇 Android SDK -> SDK Tools 中,勾選CMake並安裝。

2 新建支持NDk工程

現在的AndroidStduio更支持一種極簡方式集成NDK開發支持,即在下圖中勾選include C++ support。然后選擇C++標准。如C++ 11。建選默認的ToolChain Default

之后正常 run 即可將 C 語言部分生成出 so 文件並打包到 apk 文件中。

3 給工程添加NDK支持

上述方式適合在新的工程中添加 NDK 支持。如何要在現有的項目中添加 NDK 支持,現提供 cmake 和 ndk-build 兩種方式。

由於在同一個工程中,同時支持 cmake 和 ndk-build 兩種方式編譯 so 文件,因此將 C 源碼單獨放在 cpp-src 目錄。且將 cmakendk-build 區分不同的module進行編譯。

3.1 cmake

這是目前最受歡迎的集成方式,AndroidStduio 在創建新工程時默認使用該方式添加 NDK 支持。但在現有的工程中添加 NDK支持,需要手動配置。

創建 cmake module 添加個三個文件。

  1. CMakeLists.txt cmake編譯配置文件
cmake_minimum_required(VERSION 3.4.1)

add_library(
        hello-jni # so 庫的名稱 libhello-jni.so
        SHARED # 設置為分享庫
        # 指定C源文件的路徑,指向公共cpp-src目錄
        ../../../../cpp-src/hello-jni.c
)

find_library(
        log-lib # 設置路徑變量名稱
        log # 指定CMake需要加載的NDK庫
)
 # 鏈接hello-jni庫依賴的庫,注意下面變量名的配置 target_link_libraries(hello-jni ${log-lib} ) 復制代碼
  1. AndroidManifest.xml 每個module必須的配置文件,指定packageName。
<?xml version="1.0" encoding="UTF-8" ?> <manifest package="com.flueky.cmake"> </manifest> 復制代碼
  1. Build.gradle 每個module必須的配置文件,用於構建項目。
apply plugin: 'com.android.library' android { compileSdkVersion 28 defaultConfig{ externalNativeBuild { cmake { // 指定配置參數,更多參數設置見 https://developer.android.google.cn/ndk/guides/cmake arguments "-DCMAKE_BUILD_TYPE=DEBUG" // 添加CPP標准 // cppFlags "-std=c++11" } } } externalNativeBuild { cmake { // 指定CMake編譯配置文件路徑 path "src/main/cpp/CMakeLists.txt" } } } 復制代碼

關於 CMake 編譯參數的設置,更多內容請閱讀官方資料

眼尖的小伙伴已經發現兩處配置了 externalNativeBuild。其中第二處的externalNativeBuild配置是生成Gradle Task 可以不運行工程,直接在 ndk-cmake -> Tasks -> other 找到編譯 so 文件有關的四個任務。

 

雙擊 exeternalNativeBuildDebug 執行任務,如圖:

根據路徑即可找到生成的so文件。

3.2 ndk-build

這是最傳統的 ndk 編譯方式。在配置得當的情況下,可以在不打開 AndroidStudio 情況下完成so文件的編譯和輸出。

創建 ndk-build module ,添加4個文件。

  1. Android.mk
# 講真,這個參數我看不懂。從 官方demo 抄來的。用於指定源文件的時候使用 abspath_wa = $(join $(filter %:,$(subst :,: ,$1)),$(abspath $(filter-out %:,$(subst :,: ,$1))))  # 指定當前路徑 LOCAL_PATH := $(call my-dir)  # 指定源文件路徑 JNI_SRC_PATH := $(call abspath_wa, $(LOCAL_PATH)/../../../../cpp-src)  # 聲明 clear 變量 include $(CLEAR_VARS)  # 指定 so 庫的名稱 libhello-jni.so LOCAL_MODULE := hello-jni # 指定 c 源文件 LOCAL_SRC_FILES := $(JNI_SRC_PATH)/hello-jni.c # 添加需要依賴的NDK庫 LOCAL_LDLIBS := -llog -landroid # 指定為分享庫 include $(BUILD_SHARED_LIBRARY) 復制代碼

關於 Android.mk 編譯參數的設置,更多內容請閱讀官方資料

  1. Application.mk
# 指定編譯的的so版本 APP_ABI := all # 指定 APP 平台版本。比 android:minSdkVersion 值大時,會有警告 APP_PLATFORM := android-28 復制代碼

關於 Application.mk 編譯參數的設置,更多內容請閱讀官方資料

  1. AndroidManifext.xml
<?xml version="1.0" encoding="UTF-8" ?> <manifest package="com.flueky.ndk"> </manifest> 復制代碼
  1. build.gradle
apply plugin: 'com.android.library' android { compileSdkVersion 28 externalNativeBuild { ndkBuild { // 指定mk文件路徑 path 'src/main/jni/Android.mk' } } defaultConfig { } } 復制代碼

上面的externalNativeBuild作用同 CMake方式的一樣,用於編譯生成 so 文件。 但是 ndk-build 還支持使用命令ndk-build編譯 so 文件。 需要將 NDK 路徑添加至環境變量。

需要在jni目錄下執行該命令:

最后生成的so文件路徑如圖;

4 實踐

4.1 生成頭文件

在主 module 中的 MainActivity中添加 native 方法 。使用 javah 編譯出頭文件。 使用 -d 參數指定頭文件的輸出目錄。

public class MainActivity extends Activity { static { // 加載 JNI 庫 System.loadLibrary("hello-jni"); } ...... // 聲明 Native 方法 private native String hello(); } 復制代碼

在 app/src/main/java 目錄下執行命令 javah

4.2 編寫 C 源碼

hello-jni.c文件引用生成的頭文件,並編寫測試代碼。

#include <string.h> #include <jni.h> #include "com_flueky_demo_MainActivity.h" #include "util/log.h" /** * JNI 示例,演示native方法返回一個字符串,Java 源碼見 * * ndk-sample/app/src/main/java/com/flueky/demo/MainActivity.java */ JNIEXPORT jstring JNICALL Java_com_flueky_demo_MainActivity_hello( JNIEnv* env, jobject thiz ) { #if defined(__arm__) #if defined(__ARM_ARCH_7A__) #if defined(__ARM_NEON__) #if defined(__ARM_PCS_VFP) #define ABI "armeabi-v7a/NEON (hard-float)" #else #define ABI "armeabi-v7a/NEON" #endif #else #if defined(__ARM_PCS_VFP) #define ABI "armeabi-v7a (hard-float)" #else #define ABI "armeabi-v7a" #endif #endif #else #define ABI "armeabi" #endif #elif defined(__i386__) #define ABI "x86" #elif defined(__x86_64__) #define ABI "x86_64" #elif defined(__mips64) /* mips64el-* toolchain defines __mips__ too */ #define ABI "mips64" #elif defined(__mips__) #define ABI "mips" #elif defined(__aarch64__) #define ABI "arm64-v8a" #else #define ABI "unknown" #endif LOGD("日志輸出示例"); return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI " ABI "."); } 復制代碼

4.3 運行截圖

頁面截圖:

日志截圖:

5 源碼獲取

工程源碼已開放在GitHub,下載地址。 可以直接編寫 C 源碼並進行調試和生成 so 文件。 Google 官方資料需要翻牆才可以閱讀。想了解翻牆方法,請點SSR


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM