Android Jni使用經(jīng)驗(yàn)總結(jié)

這段時(shí)間因?yàn)殚_(kāi)始編寫(xiě)與硬件結(jié)合的一個(gè)軟件項(xiàng)目,使用到了大量的jni調(diào)用,這里總結(jié)一下使用要點(diǎn)

首先開(kāi)始我們的環(huán)境搭建,Android studio的環(huán)境搭建還是比較簡(jiǎn)單的,在Sdk Manager里找到NDK并安裝,安裝好的NDk一般位于你的sdk文件夾下的ndk-bundle,然后將該路徑配置到你系統(tǒng)變量的path里面去
cmd命令輸入

ndk build

如果未提示"ndk-build不是系統(tǒng)命令"就表示NDK環(huán)境配置完成了!

對(duì)于app來(lái)說(shuō),一些對(duì)圖片的處理、視頻的處理等等需要復(fù)雜算法支持的功能,很多都會(huì)選擇用C語(yǔ)言寫(xiě)成,然后APP端去調(diào)用C語(yǔ)言方法,得到經(jīng)過(guò)算法處理之后的結(jié)果,jni就是Java和C之間調(diào)用的橋梁
在前端時(shí)間的項(xiàng)目中,因?yàn)樾枰罅康氖褂妙?lèi)似活體檢測(cè)、人臉校準(zhǔn)之類(lèi)的算法,所以必不可少的接觸到了jni的使用

Android studio2.2.3之前的jni調(diào)用(此處舉例)

在Java的demo里編寫(xiě)如下代碼
public class MainActivity extends Activity {  
  
    static {  
        System.loadLibrary("JniTest");  
    }  
  
    public native String getStringFromNative();  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
         
        getStringFromNative(); 
    }  
}  

此處根據(jù)你寫(xiě)的native的方法名等下生成C語(yǔ)言的.h頭文件

在app文件夾下的build.gradle文件里面配置Ndk相關(guān)信息
ndk {  
    moduleName "JniTest"  
    ldLibs "log", "z", "m"  
    abiFilters "armeabi", "armeabi-v7a", "x86"  
}

基本看代碼可以看懂,指定加載so庫(kù)的名稱(chēng)為JniTest,這個(gè)只是為了舉例取的名稱(chēng),加載的abi平臺(tái)為armeabi、armeabi-v7a和x86

sourceSets{  
    main{  
        jniLibs.srcDirs = ['libs']  
    }  
}  

指定加載so庫(kù)的路徑為libs

生成需要的.h頭文件

在Android Studio下方的Terminal命令行里面執(zhí)行生成命令,如果沒(méi)有Terminal,到"View->Tool Windows->Terminal"里面打開(kāi)該面板
操作命令:

javah -d jni -classpath <SDK_android.jar>;<dir path> 加載So所在的class包名+class名  

后面的 “dir path” 加載So所在的class包名+class名 是相對(duì)于你當(dāng)前所在目錄的路徑;

參考命令:

C:\git_source\demo\JniTest>javah -d jni -classpath C:\studio\android-sdk-windows\platforms\android-23\android.jar;app\src\main\java com.example.jni.jnitest.MainActivity  

這里的后面一部分是相對(duì)于"C:\git_source\demo\JniTest"目錄的路徑

注意:很多人寫(xiě)命令行的時(shí)候容易寫(xiě)錯(cuò),app\src\main\java后面是空格 接 包名.類(lèi)名

命令執(zhí)行成功會(huì)自動(dòng)生成一個(gè)jni文件夾和對(duì)應(yīng)的"包名.h"文件,這里的文件名是:com_example_jni_jnitest_MainActivity.h

20160803150457429.png

.h文件中的代碼如下

JNIEXPORT jstring JNICALL Java_com_example_jni_jnitest_MainActivity_getStringFromNative(JNIEnv * env, jobject jObj){  
      return (*env)->NewStringUTF(env,"Hello From JNI!");    
  }  

這里的方法就是根據(jù)你的native方法生成的頭信息,我們來(lái)解析一下意思
JNIEXPORT和JNICALL:jni關(guān)鍵字就跟Java里的關(guān)鍵字一樣
jstring: 方法的返回值為string
Java_com_example_jni_jnitest_MainActivity_getStringFromNative : 格式為Java_+包名_+調(diào)用jni方法的類(lèi)名_+方法名 如果包名里包含下劃線,則用_1代替

接下來(lái)就是編寫(xiě)你的C語(yǔ)言代碼了,如果你不會(huì)寫(xiě)C語(yǔ)言代碼,那就是得有底層給你.cpp的文件或者.C的文件,我們只需要將我們剛剛生成的頭文件里的方法聲明,去替換C語(yǔ)言文件里的方法聲明,這樣C語(yǔ)言的方法就可以被Java調(diào)用了

編輯Android.mk和Application.mk

按照前面第一步的圖里面的jni包里面有一個(gè)Android.mk文件和Application.mk文件,這里我們需要?jiǎng)?chuàng)建這兩個(gè)文件,還有一個(gè)main.c文件就是我們需要生成so庫(kù)的文件,Android.mk文件和Application.mk文件是用來(lái)控制編譯So文件的, Android.mk文件控制So文件如何編譯, Application.mk文件控制支持的架構(gòu)平臺(tái).

Android.mk文件:

LOCAL_PATH := $(call my-dir)  
  
include $(CLEAR_VARS)  
#bzlib模塊  
bzlib_files := \  
    main.c  
  
LOCAL_MODULE := libbz  
LOCAL_SRC_FILES := $(bzlib_files)  
include $(BUILD_STATIC_LIBRARY)  
  
#bspath模塊  
include $(CLEAR_VARS)  
LOCAL_MODULE    := main  
LOCAL_SRC_FILES := main.c  
LOCAL_STATIC_LIBRARIES := libbz #引入libbz庫(kù)  
  
include $(BUILD_STATIC_LIBRARY)  
  
include $(CLEAR_VARS)  
  
LOCAL_MODULE    := JniTest  
LOCAL_SRC_FILES := main.c  
LOCAL_STATIC_LIBRARIES := main  
LOCAL_LDLIBS := -llog#加入log  
  
include $(BUILD_SHARED_LIBRARY)  

"bzlib_files :"里面放的是所有的c源文件;"LOCAL_MODULE := JniTest"表示生成的So庫(kù)名,因?yàn)槲壹虞dSo庫(kù)的名字是"JniTest"所以這里需要同名;

static {  
        System.loadLibrary("JniTest");  
    }  

Application.mk文件:

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

APP_ABI := armeabi 表示只支持armeabi架構(gòu)的Jni,如需添加其他架構(gòu)可在后面追加

最后一步:生成.so文件

在CMD命令行里面cd到項(xiàng)目的目錄下, 輸入"ndk-build"生成So包


20160803160351099.png

命令執(zhí)行成功后在工程的libs文件夾下面生成一個(gè)So文件,如下:


20160803163127532.png

如果不添加Application.mk文件,會(huì)生成所有架構(gòu)的So文件,如下:


20160803163555931.png

編譯出來(lái)的So默認(rèn)是放在工程里面的libs文件夾下的,需要將它移到app文件夾下的libs里面!

2.2.3之后的Jni調(diào)用

從AndroidStudio2.2開(kāi)始,studio可以利用NDK直接編譯C/C++代碼。AndroidStudio 支持Cmake和NDK-BUILD 工具編譯本地代碼,但是默認(rèn)方式為Cmake。
在用AndroidStudio開(kāi)發(fā)native應(yīng)用之前先要下載好NDK,Cmake,LLDB(本地代碼調(diào)試工具),可以直接通過(guò)studio的SDK Manager進(jìn)行安裝,安裝完成如下路所示:


20161222172524036.png

創(chuàng)建可以編譯C/C++的工程


20161222172604318.png

直接選擇next,其他跟普通工程無(wú)異,直到Customize C++ support 界面,如下圖所示:


20161222172650742.png
  1. C++ Standard:Toolchain Default 會(huì)使用默認(rèn)的 CMake 設(shè)置。
  2. Exceptions Support:對(duì) C++ 異常處理的支持,如果啟用此復(fù)選框,Android Studio 會(huì)將 -fexceptions 標(biāo)志添加到模塊級(jí) build.gradle 文件的 cppFlags 中,Gradle 會(huì)將其傳遞到 CMake。
  3. Runtime Type Information Support:如果希望支持 RTTI,選中此復(fù)選框,Android Studio 會(huì)將 -frtti 標(biāo)志添加到模塊級(jí) build.gradle 文件的 cppFlags 中,Gradle 會(huì)將其傳遞到 CMake。

選擇好后,最后點(diǎn)擊Finish
稍等片刻,簡(jiǎn)直不敢相信自己的眼睛,簡(jiǎn)直辣眼睛,自動(dòng)生成項(xiàng)目結(jié)構(gòu)如下如所示:


20161222172721226.png

所有的模板已經(jīng)生成好了,只需要往里面填代碼即可!無(wú)需生成.so庫(kù),直接可以調(diào)用jni方法,studio編譯會(huì)自動(dòng)生成.so庫(kù),大大簡(jiǎn)化了以前要自己生成.so庫(kù)的過(guò)程!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容