一:JNI介紹
1.簡(jiǎn)介:Java Native Interface哼拔,即java本地接口,本地接口就是指用C和C++開(kāi)發(fā)的接口。
2.由來(lái):實(shí)際使用中,java需要與本地代碼進(jìn)行交互级乍,因?yàn)閖ava具備跨平臺(tái)的特點(diǎn),所以java與本地代碼交互能力非常弱帚湘,所以采用JNI特性增強(qiáng)java與本地代碼交互的能力玫荣。
3.作用:使得java與本地其他類型語(yǔ)言(如c、c++)交互大诸,即在java代碼里調(diào)用c捅厂、c++等語(yǔ)言的代碼或c、c++代碼調(diào)用java代碼底挫。
4.JNI開(kāi)發(fā)主要流程:
(1) 編寫(xiě)聲明了native方法的Java類
(2) 將Java源代碼編譯成class字節(jié)碼文件
(3)用javah -jni命令生成.h頭文件(javah是jdk自帶的一個(gè)命令恒傻,-jni參數(shù)表示將class中用native聲明的函數(shù)生成JNI規(guī)則的函數(shù))
(4) 用本地代碼實(shí)現(xiàn).h文件中的函數(shù)
(5) 將本地代碼編譯成動(dòng)態(tài)庫(kù)(Windows:\*.dll,linux/unix:\*.so,mac os x:\*.jnilib)
(6) 拷貝動(dòng)態(tài)庫(kù)至java.library.path本地庫(kù)搜索目錄下,并運(yùn)行Java程序建邓。
5.注意點(diǎn):(1) JNI是java調(diào)用Native語(yǔ)言的一種特性, (2) JNI是屬于java的盈厘,與Android沒(méi)有直接關(guān)聯(lián)。
二:NDK介紹
1.簡(jiǎn)介:Native Development Kit官边,是Android的一個(gè)開(kāi)發(fā)工具包沸手,NDK是屬于Android,與java沒(méi)有直接關(guān)聯(lián)
2.作用:快速開(kāi)發(fā)c注簿、c++的動(dòng)態(tài)庫(kù)契吉,并自動(dòng)將so和應(yīng)用一起打包成APK即可通過(guò)NDK在android中,使用JNI與本地代碼(如c诡渴、c++)交互捐晶。
3.應(yīng)用場(chǎng)景:在Android的場(chǎng)景下使用JNI即Android開(kāi)發(fā)的功能需要本地代碼(c\c++)實(shí)現(xiàn)。
4.特點(diǎn):用下面的圖來(lái)直觀的了解它的特點(diǎn)
5.使用流程:
(1) 配置Android NDK環(huán)境
(2) 創(chuàng)建Android項(xiàng)目妄辩,并于NDK關(guān)聯(lián)
(3) 在Android項(xiàng)目中生命所需要調(diào)用的Native方法
(4) 使用Android需要交互的本地代碼惑灵,實(shí)現(xiàn)在Android中聲明的Native方法,比如Android需要與C++交互,那么就用C++實(shí)現(xiàn)Java的Native方法
(5) 通過(guò)ndk-build命令編譯產(chǎn)生.so庫(kù)文件
(6) 編譯Android Studio工程眼耀,從而實(shí)現(xiàn)Android 調(diào)用本地代碼
6.額外注意點(diǎn):
三:NDK與JNI關(guān)系:
1.NDK與JNI關(guān)系:NDK可以為我們生成了C/C++的動(dòng)態(tài)鏈接庫(kù)英支,JNI是java和C/C++溝通過(guò)的接口,兩者與android沒(méi)有半毛錢(qián)關(guān)系哮伟,只因?yàn)榘沧渴莏ava程序語(yǔ)言開(kāi)發(fā)干花,然后通過(guò)JNI又能與C/C++溝通,所以我們可以使用NDK+JNI來(lái)實(shí)現(xiàn)Java+C的開(kāi)發(fā)方式楞黄。
2.使用NDK開(kāi)發(fā)的好處:
(1) 項(xiàng)目需要調(diào)用底層的一些C/C++的一些東西(java無(wú)法直接訪問(wèn)到操作系統(tǒng)底層(系統(tǒng)硬件等))池凄,或者已經(jīng)在C/C++環(huán)境下實(shí)現(xiàn)了功能代碼(大部分現(xiàn)存的開(kāi)源庫(kù)都是用C/C++代碼編寫(xiě)的。)直接使用即可
(2) 為了效率更加高效些谅辣。將要求高性能的應(yīng)用邏輯使用C/C++開(kāi)發(fā)修赞,從而提高應(yīng)用程序的執(zhí)行效率。但是C/C++代碼雖然是高效的,在java與C/C++相互調(diào)用時(shí)卻增大了開(kāi)銷
(3) 基于安全性考慮柏副。防止代碼被反編譯勾邦,為了安全起見(jiàn),使用C/C++語(yǔ)言來(lái)編寫(xiě)重要的部分以增大系統(tǒng)的安全性割择,最后生成so庫(kù)(用過(guò)第三方庫(kù)的應(yīng)該都不陌生)便于給人提供方便
(4) 便于移植眷篇。用C/C++寫(xiě)的庫(kù)可以方便在其他的嵌入式平臺(tái)上再次使用
四:配置及具體使用
1.下載安裝NDK:NDK的下載方式有兩種方式,可以直接去 https://developer.android.com/ndk/downloads?hl=zh-cn荔泳;另一種就是直接在我們的Android Studio中下載蕉饼,打開(kāi)我們Studio項(xiàng)目下的Project Structure界面,如下:
在SDK Location目錄下玛歌,有SDK和NDK的路徑昧港,我這里已經(jīng)安裝NDK。如果還沒(méi)有安裝配置過(guò)NDK支子,Android Studio會(huì)提示下載Android NDK如下圖:
點(diǎn)擊Download Android NDK來(lái)進(jìn)行下載创肥。這里Android Studio會(huì)下載最新版本的NDK進(jìn)行安裝,默認(rèn)會(huì)下載保存在SDK的路徑下值朋。我們?cè)谏蠄D中還能看到有一段介紹文字叹侄,說(shuō)SDK以及NDK的路徑配置會(huì)保存在local.properties文件內(nèi),安裝完成后我們刷新Project昨登,進(jìn)local.properties文件查看也能看到SDK與NDK的路徑趾代。
注意:以前有些版本需要在gradle.properties文件中加上一行? android.useDeprecatedNdk=true ,3.0版本不再支持了丰辣。
2.添加插件:為了省去控制臺(tái)輸入命令麻煩撒强。我們借助強(qiáng)大的Android Studio的插件功能,在External Tools下配置兩個(gè)非常有用的插件笙什。進(jìn)入Setting->Tools->ExternalTools,點(diǎn)擊+號(hào)增加尿褪,如下圖:
3.javah -jni命令:根據(jù)java文件生成.h頭文件,會(huì)自動(dòng)根據(jù)java文件中的類名(包含包名)與方法名生成對(duì)應(yīng)的C/C++里面的方法名得湘。
參數(shù)及其含義:
(1) Program:$JDKPath$\bin\javah\java.exe這里配置的是JDK目錄下的javah.exe的路徑
(2) Parametes:-classpath .-jni -d $ModuleFileDir$/src/main/jni $FileClass$ 這里$FileClass$指的是要執(zhí)行操作的類名(類的全名(包名+類名))(即我們操作的文件),$ModuleFileDir$/src/main/jni表示生成的文件保存在這個(gè)module目錄的src/main/jni目錄下
(3) Working :$ModuleFileDir$\src\main\java表示module目錄下的src\main\java目錄顿仇。使用方式:選中java文件->右鍵->External Tools->javah -jni,將生成jni文件夾以及文件夾下的包名.類名的.h頭文件(名字有點(diǎn)長(zhǎng)淘正,可以自己重命名)
4.ndk-build命令:
ndk -build命令,是根據(jù)C/C++文件生成so文件的,具體參數(shù)配置及其含義:
(1) Program:F:\apk\sdk\ndk-bundle\ndk-build.cmd這里配置的是ndk下的ndk-build.cmd的路徑(根據(jù)實(shí)際情況填寫(xiě))
(2) Working:$ModuleFileDir$\src\main\
5.使用方法:選中C/C++文件->右鍵->External Tools->ndk-build,將在main文件夾下生成libs文件夾以及多個(gè)so文件臼闻,我們可以移動(dòng)至jniLibs目錄下去鸿吆,接下來(lái)通過(guò)一個(gè)簡(jiǎn)單的實(shí)例來(lái)了解它的使用。
(1) 創(chuàng)建Java類:隨意創(chuàng)建一個(gè)訪問(wèn)本地C/C++方法的java類:
? ? ? ? public class JniTest {
? ? ? ? /** * 將用C++代碼實(shí)現(xiàn)述呐,在android代碼中調(diào)用的方法:獲取一段文字 */ public static native String? ? ? ? getStrFromC();
? ? ? ? /** * 配置加載的so庫(kù)的文件名字===>如 :libmyLib.so */
? ? ? ? static {?
? ? ? ? System.loadLibrary("myLib");
? ? ? ? ? ? }
? ? ? ? }
(2) 該類的getStrFromC會(huì)報(bào)錯(cuò)提示惩淳,不用管。
生成.h頭文件:對(duì)該文件執(zhí)行javah -jni操作,生成對(duì)應(yīng)的.h頭文件思犁。
如圖代虾,已經(jīng)根據(jù)我們的java類在jni文件夾下生成了對(duì)應(yīng)的.h文件,文件名為包名類名.h激蹲,我們可以手動(dòng)改.h文件名棉磨,里面只有一個(gè)方法,返回值為String(jstring)学辱,方法名為java類的包名類名方法名(包名中的分級(jí)不是用.而是_)乘瓤,前面兩個(gè)參數(shù)是C++里面必須有的(JNIEnv代表指向JVM的指針,jclass是調(diào)用該方法的java對(duì)象)策泣,第三個(gè)jobject就是我們java類的方法里面的參數(shù)Object衙傀。注意,這是java函數(shù)與C++函數(shù)對(duì)應(yīng)的靜態(tài)注冊(cè)方法萨咕,即通過(guò)特定的規(guī)則來(lái)寫(xiě)统抬。此處方法名可以隨意起名字,然后還可以用動(dòng)態(tài)注冊(cè)的方式關(guān)聯(lián)兩個(gè)方法任洞。
(3) 創(chuàng)建C或C++文件:在jni文件夾下蓄喇,新建一個(gè).c(c語(yǔ)言)或者.cpp(C++)的文件,來(lái)實(shí)現(xiàn).h文件里聲明的方法:把.h文件里面聲明的方法拷貝到新建的c文件里面交掏,然后在文件里面引入.h文件妆偏,如下圖:
至此,.h文件和c++文件已完成盅弛,想生成so文件還需要在這個(gè)jni目錄下增加兩個(gè)文件钱骂,Android.mk和Application.mk。
注意LOCAL_MODULE的值與之前的java類中設(shè)置要生成so庫(kù)的名字相對(duì)應(yīng)挪鹏,LOCAL_SRC_FILES的值寫(xiě)C++文件的名字见秽,這兩個(gè)值成對(duì)設(shè)置,可設(shè)置多組讨盒。(:=是賦值的意思解取,$是引用某變量的值)
LOCAL_PATH := $(call my-dir)? ? // 設(shè)置當(dāng)前的編譯目錄(Android.mk所在的目錄)
include $(CLEAR_VARS)? ? ? ? ? ? // 清除LOCAL_XX變量(LOCAL_PATH除外)
LOCAL_MODULE := myLib // 指定當(dāng)前編譯模塊的名稱?
LOCAL_SRC_FILES := jnittest.c? ? // 編譯模塊需要的源文件
include $(BUILD_SHARED_LIBRARY)
// 指定編譯出的庫(kù)類型,BUILD_SHARED_LIBRARY:動(dòng)態(tài)庫(kù)返顺;BUILD_STATIC_LIBRARY:靜態(tài)庫(kù)禀苦, BUILD_EXECUTEABLE指:可執(zhí)行文件
在一個(gè)Android.mk文件中配置多個(gè)Module的方式如下(include $(CLEAR_VARS)、include $(BUILD_SHARED_LIBRARY)兩個(gè)語(yǔ)句也需要加上)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := myLib
LOCAL_SRC_FILES := jnittest.c
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := myLib2
LOCAL_SRC_FILES := jnittest2.c
include $(BUILD_SHARED_LIBRARY)
Application.mk遂鹊,APP_ABI有四種類型(默認(rèn)armeabi)振乏,armeabi、armeabi-v7a秉扑、x86慧邮、mips,設(shè)置時(shí)以空格隔開(kāi),all表示所有误澳。該文件中有個(gè)可選配置的APP_MODULES,類似于上面Android.mk文件中的LOCAL_MODUEL,以空格隔開(kāi)耻矮,且會(huì)覆蓋掉Android.mk文件中的LOCAL_MODULE設(shè)置(比如Android.mk文件中的寫(xiě)了兩個(gè)jni庫(kù)的配置,LOCAL_MODULE:=JNI1脓匿、LOCAL_MODULE:=JNI2,而Application.mk中設(shè)置的APP_MODULES:=JNI1,則只能生成JNI1的so文件淘钟,要生成JNI2的so文件的時(shí)候會(huì)報(bào)錯(cuò),除非寫(xiě)成APP_MODULE:=JNI1 JNI2,這里我們直接省略默認(rèn)使用Android.mk中的)
APP_ABI := all,生成so文件對(duì)C++文件執(zhí)行ndk-build操作陪毡,生成相應(yīng)的so文件
如圖米母,在main/libs目錄下生成了多個(gè)so文件,名字為lib+我們指定的庫(kù)名(同時(shí)還生成了obj文件夾毡琉,中間文件)铁瞒。這時(shí)候我們可以生成的main/libs文件夾內(nèi)的東西復(fù)制到app/libs下,并刪除main下新生成的jni桅滋、libs慧耍、obj三個(gè)文件夾。
配置app/gradle:
調(diào)用在Activity中測(cè)試調(diào)用丐谋,在TextView上顯示我們通過(guò)C++代碼實(shí)現(xiàn)的方法getStrFromC獲取字符串芍碧。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 如有侵權(quán),請(qǐng)及時(shí)于我聯(lián)系