Android NDK初探

之前對NDK開發一直是個小白,最近花了幾天時間研究得到的一些理解在此做個記錄分享。結論不足之處拒絕反駁,所有觀點僅單方面宣布,后果自負。*.*,本文出處:http://www.lxweimin.com/p/201046751a7c

一、什么是NDK?

NDK全稱是Native Development Kit(原生開發工具包),NDK提供了一系列的工具,幫助開發者快速開發C(或C++)的動態庫,并能自動將so和java應用一起打包成apk。也就是說它是一個“開發工具包”,就像SDK一樣,區別就在于SDK是面向java開發者的工具集合,而NDK面向的則是C/C++開發者的工具集合(包括對c/c++源碼的打包編譯工具ndk,一些h頭文件等)。附上官方NDK工具包的下載路徑:官網ndk下載,需要翻墻。

二、為什么需要使用NDK?

1.代碼的保護。由于apk的java層代碼很容易被反編譯,而C/C++庫反匯難度較大。
2.可以方便地使用現存的開源庫。大部分現存的開源庫都是用C/C++代碼編寫的。
3.提高程序的執行效率。將要求高性能的應用邏輯使用C開發,從而提高應用程序的執行效率。
4.便于移植。用C/C++寫得庫可以方便在其他的嵌入式平臺上再次使用。

三、JNI、SO介紹

JNI 全稱Java Native Interface,這套技術的機制是用于java訪問c/c++代碼而產生的,說白了NDK開發的核心就是JNI開發,利用java代碼來調用遵循JNI規范的c/c++的方法實現某個功能。

so全稱Shared Object,本地原生庫,先暫時理解為java中的jar包,所有的c/c++的代碼在android(Linux)平臺中最終都會編譯成so庫,然后才能被調用。所以ndk開發所編寫出的c/c++代碼最終的目的都是為了獲得這個so庫,與java方法形成jni的映射關系從實現調用。

四、開始擼碼

本文使用Android Studio2.0進行演示HelloJni

? 大概步驟:

? ? ?1. java文件中聲明native方法,和java方法聲明一樣,在此基礎上加了natvie修飾。
? ? ?2. 利用javah命令生成與該類對應的頭文件(包含方法信息)
? ? ?3. 根據頭文件的信息編寫c源代碼文件
? ? ?4. 在app\build.gradle文件中配置ndk的編譯信息
? ? ?5. 配置NDK工具包路徑,編譯運行

創建項目:HelloJni

圖 1

1. 定義一個java類SayHello,并在里面聲明一個靜態無參native方法speak,并且創建jni文件夾

圖 2

2. 利用javah命令生成與該類對應的jni頭文件,生成的頭文件的目的主要是用來編寫c/c++源文件

圖 3

3.根據.h頭文件的信息編寫c源代碼文件 : 創建SayHello.c文件,把頭文件里的方法copy到該文件中,并修改成實體方法,下面則是返回一段字符串。如果熟悉了jni方法名稱命名規范,完全可自己手寫,生成頭文件的步驟也可跳過。親測發現如果包名帶有數字的命名規則不好把握,所以建議用javah生成。

圖 4

4.在build.gradle中配置ndk的編譯信息,配置完成保存同步之后可能出現錯誤,添加 android.useDeprecatedNdk=true 到gradle.properties 文件中即可解決。

圖 5

5.配置下載好的NDK工具包:File->Project Structure->SDK Location(文件路徑\android-ndk-r14b目錄配置到系統環境變量中,以備后面使用)

圖 6

然后回到在java文件中,加載buil.gradle中配置的moduleName的類庫名稱,這里配置為:SayHello

圖 7

最后在MainActivity中測試該方法。

圖 8

運行。

圖 9


至此,體驗了一把基本的ndk開發過程。不過洗腦還沒有結束:

在運行完成之后,我們并沒有發現工程目錄中有so庫文件,其實這個so庫文件是在運行之后直接打包到了apk文件中的lib目錄下了

圖 10

由于我們在build.gradle配置了abiFilters打包時只打包x86的文件夾中的so庫。所以我們在apk中只看到x86的文件夾,里面存放的就是so庫,如果不配置abiFilters,那么將會出現android支持的7種abi,可參見該文章理解ABI。

因為我們可以調用so這個庫,顯然這個so庫是根據我們在jni文件夾下編寫的源文件編譯生成的,如果我們沒有配置,gradle默認就會去編譯jni的文件夾下的c/c++的代碼生成so庫,這個路勁就是src/main/jni,如果這個文件夾沒有文件即使配置了ndk{...}信息也不會生成so庫,當然gradle還提供自定義配置,下面就看看如何配置:

sourceSets{
 ?main{
 ? ?jin.srcDirs=["src/mian/jni"] ?//默認路徑,jin.srcDirs指的是需要加入編譯的jni的路徑,可以自己修改路徑的
 ?}
}
圖 11

這個apk安裝到x86 abi手機上之后,so庫會安裝在data\app\包名-數值\ib目錄下(可通過Device Monitor工具查看),所以由此可判斷System.loadLibrary()加載的庫默認是這個路徑下的庫,也可以調用System.load(data\app\包名-數值\ib\abi\libxx.so)加載絕對路徑的so庫。

圖 12

所以java代碼能不能正確的執行so庫里的內容取決于so庫能否被正確的安裝。如果未能正確安裝,當虛擬機去System.loadLibrary時就會報錯java.lang.UnsatisfiedLinkError

上面的做法只有在打包時才能得到so庫,下面就介紹通過ndk開發工具包里的ndk-build單獨來編譯出so庫,這種方式就無需在build.gradle中配置ndk{...}了。

1. 在jni文件家中新建android.mk編譯配置文件,參見Android.mk詳細配置

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE ? ?:= SayHello
LOCAL_SRC_FILES := SayHello.c

include $(BUILD_SHARED_LIBRARY)

2.在jni文件家中新建application.mk配置需要生成支持的abi so庫,參見Application.mk詳細配置

APP_CFLAGS += -Wno-error=format-security
APP_ABI := all

這里配置支持所有的abi。

3.在terminal中調用ndk-build工具生成so庫

圖 13

我們可以看到在main文件夾下生成了libs目錄,并且生成了支持所有abi的so庫,到此生成so完畢;現在任務就是要讓這些so庫打包到apk文件中的libs目錄下,在build.gradle中配置sourceSets的另一個屬性jniLibs.srcDirs,配置的路徑下的so庫文件都會打包到apk文件中,其默認值為app/libs,所以也可以把這些so文件拷到app/libs中而不配置這個屬性用其默認值。

圖 14

上圖中不配置jni.srcDirs的路徑的作用是為了打包時不讓編譯系統再去編譯得到so庫(因為我們已經單獨生成),雖然上面ndk沒有被配置,但是只要的配置這個路徑下有c文件就會生成so庫,并且名字為libapp.so,這樣一來就造成了相同的包存在兩個增加app的體積。

最后build apk看看apk里有沒有so庫:


圖 15

運行,大功告成。

下一篇文章介紹:第三方so庫的調用

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,325評論 25 708
  • 一、NDK產生的背景 Android平臺從誕生起,就已經支持C、C++開發。眾所周知,Android的SDK基于J...
    Ten_Minutes閱讀 3,542評論 1 27
  • 本人為初學者,文章寫得不好,如有錯誤,請大力懟我 或者看這里 如何使用jni進行開發 本文主要針對Android環...
    AlbertHumbert閱讀 4,710評論 2 12
  • Android游戲開發實踐(1)之NDK與JNI開發02 承接上篇Android游戲開發實踐(1)之NDK與JNI...
    AlphaGL閱讀 3,780評論 0 24
  • 若你愿獻祭 我會在你的乳上 劃一道傷口 輕輕舔起 若你愿獻祭 我會在皮鞭和你之間 鋪散烏賊的氣息 圖畫現代性的隨機...
    主修寂靜閱讀 312評論 0 0