Android Studio NDK(C++)

對于部分android開發(fā)可能沒有使用過NDK,ndk只是工具,我們真正要實現(xiàn)的功能還是需要c/c++來編寫我們的實現(xiàn)代碼。對于沒有使用或?qū)W習過c/c++的同學,可能一聽到c/c++編碼的字眼就會有點頭暈,想放棄的想法了。但是其實我們平時在NDK中編寫C/C++會用到的語言特有特性很少,或者選擇不去使用。完全將其當成java來編寫,然后慢慢的會發(fā)現(xiàn)其實也就這樣嘛。。。

我們首先可以選擇使用C++來開始,因為C++相較于C來說更貼近Java,同樣能夠以面向?qū)ο蟮乃枷雭砭帉懳覀兊拇a。首先我們來創(chuàng)建一個NDK工程。

我們在AS中新建工程:


一直到下一步最后一頁


這里讓as是否自動為我們設置好前一篇(配置篇)中提的C++11支持。如果作為初學者我們這里可以暫時不去考慮,直接finish創(chuàng)建完成。最后創(chuàng)建出來的的工程目錄結(jié)構(gòu)是這樣的:


打開MainActivity:


// Used to load the 'native-lib' library on application startup.

static{

System.loadLibrary("native-lib");

}

@Override

protected voidonCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

// Example of a call to a native method

TextView tv = (TextView) findViewById(R.id.sample_text);

tv.setText(stringFromJNI());

}

/*** A native method that is implemented by the 'native-lib' native library,* which is packaged with this application.*/

public nativeStringstringFromJNI();

在這個Activity中像一個abstract方法一樣沒有方法體

native方法:stringFromJNI()。而在MainActivity中有一個static靜態(tài)代碼塊中使用System.loadLibrary加載了一個C/C++的動態(tài)庫。這樣當調(diào)用這個native方法的時候就會從動態(tài)庫中查找對應的C/C++方法。

這個方法在C++中聲明為

Java_com_dongnao_hello_1jni_MainActivity_stringFromJNI

我們的native-lib.cpp中全部內(nèi)容如下:

#include

#include

extern"C"

JNIEXPORTjstringJNICALL

Java_com_dongnao_hello_1jni_MainActivity_stringFromJNI(

JNIEnv*env,

jobject/* this */) {

std::stringhello ="Hello from C++";

returnenv->NewStringUTF(hello.c_str());

可以看到方法聲明由extern “C”開始, extern "C"的主要作用就是為了能夠正確實現(xiàn)C++代碼調(diào)用其他C語言代碼。加上extern "C"后,會指示編譯器這部分代碼按C語言的進行編譯,而不是C++的。這是由于C++會將函數(shù)的參數(shù)類型也加到編譯后的文件,而C不會。所以如果我們需要在C++中使用C編寫的代碼、開源庫就需要使用extern “C”聲明。

然后是JNIEXPORT jstring JNICALL聲明,jstring對應了java中的java.lang.String,而在jstring之前的JNIEXPORT則是由jni定義的一個宏(這一部分不感興趣可以跳過)。JNIEXPORT的定義如下:

#defineJNIIMPORT

#defineJNIEXPORT__attribute__((visibility ("default")))

#defineJNICALL

這個宏是設置屬性visibility(可見性),這個屬性的作用是防止重復符號的問題。比如說編譯的時候指定了-fvisibility=hidden那么沒有這個聲明的函數(shù)會被隱藏,無法讓外部調(diào)用。而JNICALL宏從上面可以看到是一個空。一般的我們的java native方法的實現(xiàn)會在返回值前后加上這兩個宏。當然對于自己app庫直接使用而不需要讓別的庫link(依賴)來說不加這兩個宏也可以。

在返回值后是函數(shù)名,函數(shù)名格式為:Java_包名_類名_方法名。包名的‘.’替換成‘_’,如果包名、類名或方法名本身存在‘_’則用1來區(qū)分

如我們的包名是com.dongnao.hello_jni則需要寫成:com_dongnao_hello_1jni


我們的這個java native方法是沒有任何參數(shù)的,但是在jni中我們可以在函數(shù)中接受一個JNIEnv與一個jobject。

JNIEnv在c與c++中有不同的定義(這里我們只看C++前面也說了沒接觸過C/C++的java開發(fā),C++入門更簡單)的。C++中定義為_JNIEnv結(jié)構(gòu)體(其實結(jié)構(gòu)體中存在一個JNINativeInterface指針,而C是直接將JNIEnv定義為JNINativeInterface指針)。在C++中需要通過這個結(jié)構(gòu)體去調(diào)用JNI函數(shù),如:調(diào)用java方法、創(chuàng)建java類對象等,也就是通過JNIEnv來達到與java交互的作用。

而jobject則是當前類的jni對象,如果native方法是static的則jobject對應了MainActivity的class對象,如果不是static則對應了MainActivity的實例對象。

在然后就是方法體中的實現(xiàn)。

嗯,基本的還有java與jni對象的對應關系,這個再我看來不用管,一個是用的多了自然記住了,如果記不住可以利用ide提供的indexer進去查看一眼就明白了,比如我們看jchar:

typedefuint16_tjchar;

typedef__uint16_t ???uint16_t;

typedef unsigned short__uint16_t;

所以java當中的char對應了C/C++中的unsigned short(無符號short, -1是有符號,1是無符號)。char在java是兩個字節(jié),short在C/C++也是兩個字節(jié)。而java中的byte占1個字節(jié),對應了jni中的jbyte->signed char。

其次還有關于C/C++調(diào)用Java方法時候關于java方法簽名的問題。這個也沒什么可說的,一張圖


如果實在不知道,javap命令查看class文件就行了。比如在as中我們cd到


然后執(zhí)行:


現(xiàn)在可以讓我們開始在android中利用ndk,在C++中編寫我們的“java”代碼。

9?|.??

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

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

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