拓勝技術(shù)專家教你如何深入理解AndroidNative_第1頁(yè)
拓勝技術(shù)專家教你如何深入理解AndroidNative_第2頁(yè)
拓勝技術(shù)專家教你如何深入理解AndroidNative_第3頁(yè)
拓勝技術(shù)專家教你如何深入理解AndroidNative_第4頁(yè)
拓勝技術(shù)專家教你如何深入理解AndroidNative_第5頁(yè)
已閱讀5頁(yè),還剩11頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、java native interface (jni)標(biāo)準(zhǔn)是java平臺(tái)的一部分,它允許java代碼和其他語(yǔ)言寫(xiě)的代碼進(jìn)行交互。jni 是本地編程接口,它使得在 java 虛擬機(jī) (vm) 內(nèi)部運(yùn)行的 java 代碼能夠與用其它編程語(yǔ)言(如 c、c+ 和匯編語(yǔ)言)編寫(xiě)的應(yīng)用程序和庫(kù)進(jìn)行交互操作。1.從如何載入.so檔案談起由于android的應(yīng)用層的類都是以java寫(xiě)的,這些java類編譯為dex型式的bytecode之后,必須靠dalvik虛擬機(jī)(vm: virtual machine)來(lái)執(zhí)行。vm在android平臺(tái)里,扮演很重要的角色。此外,在執(zhí)行java類的過(guò)程中,如果java類需要與

2、c組件溝通時(shí),vm就會(huì)去載入c組件,然后讓java的函數(shù)順利地調(diào)用到c組件的函數(shù)。此時(shí),vm扮演著橋梁的角色,讓java與c組件能通過(guò)標(biāo)準(zhǔn)的jni介面而相互溝通。應(yīng)用層的java類是在虛擬機(jī)(vm: vitual machine)上執(zhí)行的,而c件不是在vm上執(zhí)行,那么java程式又如何要求vm去載入(load)所指定的c組件呢? 可使用下述指令:system.loadlibrary(*.so的檔案名);例如,android框架里所提供的mediaplayer.java類,含指令:java代碼:1. public class mediaplayer2. static 3. system.load

3、library(media_jni);4. 5.6. 7.8.復(fù)制代碼這要求vm去載入android的/system/lib/libmedia_jni.so檔案。載入*.so之后,java類與*.so檔案就匯合起來(lái),一起執(zhí)行了。2.如何撰寫(xiě)*.so的入口函數(shù)jni_onload()與jni_onunload()函數(shù)的用途當(dāng)android的vm(virtual machine)執(zhí)行到system.loadlibrary()函數(shù)時(shí),首先會(huì)去執(zhí)行c組件里的jni_onload()函數(shù)。它的用途有二:(1)告訴vm此c組件使用那一個(gè)jni版本。如果你的*.so檔沒(méi)有提供jni_onload()函數(shù),v

4、m會(huì)默認(rèn)該*.so檔是使用最老的 jni 1.1版本。由于新版的jni做了許多擴(kuò)充,如果需要使用jni的新版功能,例如jni 1.4的java.nio.bytebuffer,就必須藉由jni_onload()函數(shù)來(lái)告知vm。(2)由于vm執(zhí)行到system.loadlibrary()函數(shù)時(shí),就會(huì)立即先呼叫jni_onload(),所以c組件的開(kāi)發(fā)者可以藉由jni_onload()來(lái)進(jìn)行c組件內(nèi)的初期值之設(shè)定(initialization) 。例如,在android的/system/lib/libmedia_jni.so檔案里,就提供了jni_onload()函數(shù),其程式碼片段為:java代碼:

5、1.2. /#define log_ndebug 03. #define log_tag mediaplayer-jni4.5. jint jni_onload(javavm* vm, void* reserved)6. jnienv* env = null;7. jint result = -1;8.9. if (vm-getenv(void*) &env, jni_version_1_4) != jni_ok) 10. loge(error: getenv failed );11. goto bail;12. 13.14. assert(env != null);15.16.17. if

6、(register_android_media_mediaplayer(env) 0) 18. loge(error: mediaplayer native registration failed );19. goto bail;20. 21.22. if (register_android_media_mediarecorder(env) 0) 23. loge(error: mediarecorder native registration failed );24. goto bail;25. 26.27. if (register_android_media_mediascanner(e

7、nv) 0) 28. loge(error: mediascanner native registration failed );29. goto bail;30. 31.32. if (register_android_media_mediametadataretriever(env) 0) 33. loge(error: mediametadataretriever native registration failed );34. goto bail;35. 36. result = jni_version_1_4;37. bail:38. return result;39.40. 此函數(shù)

8、回傳jni_version_1_4值給vm,于是vm知道了其所使用的jni版本了。此外,它也做了一些初期的動(dòng)作(可呼叫任何本地函數(shù)),例如指令:java代碼:1. if (register_android_media_mediaplayer(env) getenv(void*) &env, jni_version_1_4) != jni_ok) 7. loge(error: getenv failed );8. goto bail;9. 10. 11.12.復(fù)制代碼由于vm通常是多執(zhí)行緒(multi-threading)的執(zhí)行環(huán)境。每一個(gè)執(zhí)行緒在呼叫jni_onload()時(shí),所傳遞進(jìn)來(lái)的jn

9、ienv 指標(biāo)值都是不同的。為了配合這種多執(zhí)行緒的環(huán)境,c組件開(kāi)發(fā)者在撰寫(xiě)本地函數(shù)時(shí),可藉由jnienv指標(biāo)值之不同而避免執(zhí)行緒的資料沖突問(wèn)題,才能確保所寫(xiě)的本地函數(shù)能安全地在android的多執(zhí)行緒vm里安全地執(zhí)行?;谶@個(gè)理由,當(dāng)在呼叫c組件的函數(shù)時(shí),都會(huì)將jnienv指標(biāo)值傳遞給它,如下:java代碼:1. jint jni_onload(javavm* vm, void* reserved)2.3. jnienv* env = null;4. if (register_android_media_mediaplayer(env) monitorenter(env, obj) != jn

10、i_ok) 查看是否已經(jīng)有其他執(zhí)行緒進(jìn)入此物件,如果沒(méi)有,此執(zhí)行緒就進(jìn)入該物件里執(zhí)行了。還有,也可撰寫(xiě)下述指令:if (*env)-monitorexit(env, obj) != jni_ok) 查看是否此執(zhí)行緒正在此物件內(nèi)執(zhí)行,如果是,此執(zhí)行緒就會(huì)立即離開(kāi)。3.registernativemethods()函數(shù)的用途應(yīng)用層級(jí)的java類別透過(guò)vm而呼叫到本地函數(shù)。一般是仰賴vm去尋找*.so里的本地函數(shù)。如果需要連續(xù)呼叫很多次,每次都需要尋找一遍,會(huì)多花許多時(shí)間。此時(shí),組件開(kāi)發(fā)者可以自行將本地函數(shù)向vm進(jìn)行登記。例如,在android的/system/lib/libmedia_jni.so

11、檔案里的代碼段如下:java代碼:1. /#define log_ndebug 02.3. #define log_tag mediaplayer-jni4. static jninativemethod gmethods = 5. setdatasource, (ljava/lang/string;)v,(void *)android_media_mediaplayer_setdatasource,setdatasource, (ljava/io/filedescriptor;jj)v,(void *)android_media_mediaplayer_setdatasourcefd,6.

12、prepare, ()v, (void *)android_media_mediaplayer_prepare,7. prepareasync, ()v, (void *)android_media_mediaplayer_prepareasync,8. _start, ()v, (void *)android_media_mediaplayer_start,9. _stop, ()v, (void *)android_media_mediaplayer_stop,10. getvideowidth, ()i, (void *)android_media_mediaplayer_getvide

13、owidth,11. getvideoheight, ()i, (void *)android_media_mediaplayer_getvideoheight,12. seekto, (i)v, (void *)android_media_mediaplayer_seekto,13. _pause, ()v, (void *)android_media_mediaplayer_pause,14. isplaying, ()z, (void *)android_media_mediaplayer_isplaying,15. getcurrentposition, ()i, (void *)an

14、droid_media_mediaplayer_getcurrentposition,16. getduration, ()i, (void *)android_media_mediaplayer_getduration,17. _release, ()v, (void *)android_media_mediaplayer_release,18. _reset, ()v, (void *)android_media_mediaplayer_reset,19. setaudiostreamtype,(i)v, (void *)android_media_mediaplayer_setaudio

15、streamtype,20. setlooping, (z)v, (void *)android_media_mediaplayer_setlooping,21. setvolume, (ff)v, (void *)android_media_mediaplayer_setvolume,22. getframeat, (i)landroid/graphics/bitmap;,(void *)android_media_mediaplayer_getframeat,23. native_setup, (ljava/lang/object;)v,(void *)android_media_medi

16、aplayer_native_setup,24. native_finalize, ()v, (void *)android_media_mediaplayer_native_finalize,25. ;26.27. static int register_android_media_mediaplayer(jnienv *env)28. return androidruntime:registernativemethods(env,android/media/mediaplayer, gmethods, nelem(gmethods);29. 30.復(fù)制代碼java代碼:1. jint jn

17、i_onload(javavm* vm, void* reserved)2. if (register_android_media_mediaplayer(env) 0) 3.4. loge(error: mediaplayer native registration failed );5. goto bail;6. 7. 8.9.復(fù)制代碼當(dāng)vm載入libmedia_jni.so檔案時(shí),就呼叫jni_onload()函數(shù)。接著,jni_onload()呼叫 register_android_media_mediaplayer()函數(shù)。此時(shí),就呼叫到 androidruntime:registe

18、rnativemethods()函數(shù),向vm(即androidruntime)登記 gmethods表格所含的本地函數(shù)了。簡(jiǎn)而言之,registernativemethods()函數(shù)的用途有二:(1)更有效率去找到函數(shù)。(2)可在執(zhí)行期間進(jìn)行抽換。由于gmethods是一個(gè)對(duì)照表,在程序執(zhí)行時(shí),可多次呼叫registernativemethods()函數(shù)來(lái)更換本地函數(shù)之指針,而達(dá)到彈性抽換本地函數(shù)之目的。4.andoird 中使用了一種不同傳統(tǒng)java jni的方式來(lái)定義其native的函數(shù)。其中很重要的區(qū)別是andorid使用了一種java 和 c 函數(shù)的映射表數(shù)組,并在其中描述了函數(shù)的參數(shù)

19、和返回值。這個(gè)數(shù)組的類型是jninativemethod,定義如下:java代碼:1.2. typedef struct 3. const char* name;4. const char* signature;5. void* fnptr;6. 7. jninativemethod;8.9.復(fù)制代碼其中比較難以理解的是第二個(gè)參數(shù),例如java代碼:1. ()v2. (ii)v3. (ljava/lang/string;ljava/lang/string;)v4.5.6.復(fù)制代碼實(shí)際上這些字符是與函數(shù)的參數(shù)類型一一對(duì)應(yīng)的。() 中的字符表示參數(shù),后面的則代表返回值。例如()v 就表示void

20、func();(ii)v 表示 void func(int, int);具體的每一個(gè)字符的對(duì)應(yīng)關(guān)系如下字符 java類型 c類型java代碼:1. v void void2. z jboolean boolean3. i jint int4. j jlong long5. d jdouble double6. f jfloat float7. b jbyte byte8. c jchar char9. s jshort short10.復(fù)制代碼數(shù)組則以開(kāi)始,用兩個(gè)字符表示java代碼:1. i jintarray int2. f jfloatarray float3. b jbytearray

21、 byte4. c jchararray char5. s jshortarray short6. d jdoublearray double7. j jlongarray long8. z jbooleanarray boolean9.上面的都是基本類型。如果java函數(shù)的參數(shù)是class,則以l開(kāi)頭,以;結(jié)尾,中間是用/ 隔開(kāi)的包及類名。而其對(duì)應(yīng)的c函數(shù)名的參數(shù)則為jobject. 一個(gè)例外是string類,其對(duì)應(yīng)的類為jstringljava/lang/string; string jstringljava/net/socket; socket jobject如果java函數(shù)位于一個(gè)嵌入

22、類,則用$作為類名間的分隔符。例如 (ljava/lang/string;landroid/os/fileutils$filestatus;)zandroid jni編程實(shí)踐一、直接使用java本身jni接口(windows/ubuntu)1.在eclipsh中新建一個(gè)android應(yīng)用程序。兩個(gè)類:一個(gè)繼承于activity,ui顯示用。另一個(gè)包含native方法。編譯生成所有類。java代碼:1. package eoe.jnitest;2.3. import android.app.activity;4. import android.os.bundle;5.6. public clas

23、s jnitest extends activity 7.8. override9. public void oncreate(bundle savedinstancestate) 10. super.oncreate(savedinstancestate);11. setcontentview(r.layout.main);12. nadd cal = new nadd();13. settitle(the native add result is + string.valueof(cal.nadd(10, 19);14. 15. 16.17. /nadd.java文件:18.19. pac

24、kage eoe.jnitest;20.21. public class nadd 22. static 23. system.loadlibrary (nadd);24. 25. public native int nadd(int a, int b);26. 27.28.復(fù)制代碼使用javah命令生成c/c+的.h文件。注意類要包含包名,路徑文件夾下要包含所有包中的類,否則會(huì)報(bào)找不到類的錯(cuò)誤。classpath參數(shù)指定到包名前一級(jí)文件夾,文件夾層次結(jié)構(gòu)要符合java類的組織層次結(jié)構(gòu)。java代碼:1. #include 2. #ifndef _included_com_hello_jnit

25、est_nadd3. #define _included_com_hello_jnitest_nadd4. #ifdef _cplusplus5.6. extern c 7. #endif8. jniexport jint jnicall java_com_hello_jnitest_nadd_nadd9. (jnienv *, jobject, jint, jint);10. #ifdef _cplusplus11. 12.13.復(fù)制代碼3.編輯.c文件實(shí)現(xiàn)native方法。com_hello_jnitest_nadd.c文件:java代碼:1. #include 2. #include c

26、om_hello_jnitest_nadd.h3. jniexport jint jnicall java_com_hello_jnitest_nadd_nadd(jnienv * env, jobject c, jint a, jint b)4. 5. return (a+b);6. 7.8.復(fù)制代碼4.編譯.c文件生存動(dòng)態(tài)庫(kù)。arm-none-linux-gnueabi-gcc -i/home/a/work/android/jdk1.6.0_17/include -i/home/a/work/android/jdk1.6.0_17/include/linux -fpic -c com_he

27、llo_jnitest_nadd.c arm-none-linux-gnueabi-ld -t/home/a/codesourcery/sourcery_g+_lite/arm-none-linux-gnueabi/lib/ldscripts/armelf_linux_eabi.xsc -share -o libnadd.so com_hello_jnitest_nadd.o得到libnadd.so文件。以上在ubuntu中完成。5.將相應(yīng)的動(dòng)態(tài)庫(kù)文件push到avd的system/lib中:adb push libnadd.so /system/lib。若提示read-only file s

28、ystem錯(cuò)誤,運(yùn)行adb remount命令,即可。adb push libnadd.so /system/lib6.在eclipsh中運(yùn)行原應(yīng)用程序即可。 對(duì)于一中生成的so文件也可采用二中的方法編譯進(jìn)apk包中。只需在工程文件夾中建libsarmeabi文件夾(其他文件夾名無(wú)效,只建立libs文件夾也無(wú)效),然后將so文件拷入,編譯工程即可。二.使用ndk生成本地方法(ubuntu and windows)1.安裝ndk:解壓,然后進(jìn)入ndk解壓后的目錄,運(yùn)行build/host-setup.sh(需要make 3.81和awk)。若有錯(cuò),修改host-setup.sh文件:將#!/bi

29、n/sh修改為#!/bin/bash,再次運(yùn)行即可。2.在apps文件夾下建立自己的工程文件夾,然后在該文件夾下建一文件application.mk和項(xiàng)project文件夾。java代碼:1. application.mk文件:2. app_project_path := $(call my-dir)/project3. app_modules := myjni4.5.復(fù)制代碼3.在project文件夾下建一jni文件夾,然后新建android.mk和myjni.c。這里不需要用javah生成相應(yīng)的.h文件,但函數(shù)名要包含相應(yīng)的完整的包、類名。4.編輯相應(yīng)文件內(nèi)容。 android.mk文件:

30、java代碼:1. # copyright (c) 2009 the android open source project2. #3. # licensed under the apache license, version 2.0 (the license);4. # you may not use this file except in compliance with the license.5. # you may obtain a copy of the license at6. # unless required by applicable law or agreed to in

31、writing, software7. # distributed under the license is distributed on an as is basis,8. # without warranties or conditions of any kind, either express or implied.9. # see the license for the specific language governing permissions and10. # limitations under the license.11. #12.13. local_path := $(ca

32、ll my-dir)14. include $(clear_vars)15. local_module := myjni16. local_src_files := myjni.c17. include $(build_shared_library)18. /myjni.c文件:19.20.21. #include 22. jstring23. java_com_hello_ndktest_ndktest_stringfromjni( jnienv* env,jobject thiz )24. 25. return (*env)-newstringutf(env, hello from my-

33、jni !);26. 27.28. /myjni文件組織:29. aubuntu:/work/android/ndk-1.6_r1/apps$ tree myjni30. myjni31. |- application.mk32. - project33. |- jni34. | |- android.mk35. | - myjni.c36. - libs37. - armeabi38. - libmyjni.so在這個(gè)例子中,我們要實(shí)現(xiàn)一個(gè)native方法 string getline(string prompt);讀入一個(gè)string參數(shù),返回一個(gè)string值。通過(guò)執(zhí)行javah -jn

34、i得到的頭文件是這樣的java代碼:1. #include 2.3. #ifndef _included_prompt4. #define _included_prompt5. #ifdef _cplusplus6.7. extern c 8. #endif9.10. jniexport jstring jnicall java_prompt_getline(jnienv *env, jobject this, jstring prompt);11.12. #ifdef _cplusplus13. 14.15. #endif16. #endif復(fù)制代碼jstring是jni中對(duì)應(yīng)于string

35、的類型,但是和基本類型不同的是,jstring不能直接當(dāng)作c+的string用。如果你用cout prompt getstringutfchars(prompt, false);8.9. if(str = null) 10. return null;11. 12.13. std:cout str releasestringutfchars(prompt, str);15. char* tmpstr = return string succeeded;16. jstring rtstr = env-newstringutf(tmpstr);17. return rtstr;18. 19.20.復(fù)

36、制代碼在上面的例子中,作為參數(shù)的prompt不能直接被c+程序使用,先做了如下轉(zhuǎn)換str = env-getstringutfchars(prompt, false);將jstring類型變成一個(gè)char*類型。返回的時(shí)候,要生成一個(gè)jstring類型的對(duì)象,也必須通過(guò)如下命令,jstring rtstr = env-newstringutf(tmpstr);這里用到的getstringutfchars和newstringutf都是jni提供的處理string類型的函數(shù),還有其他的函數(shù)這里就不一一列舉了。3. 數(shù)組類型的傳遞和string一樣,jni為java基本類型的數(shù)組提供了j*array

37、類型,比如int對(duì)應(yīng)的就是jintarray。來(lái)看一個(gè)傳遞int數(shù)組的例子,java代碼:1.2. jniexport jint jnicall java_intarray_sumarray(jnienv *env, jobject obj, jintarray arr)3.4. jint *carr;5. carr = env-getintarrayelements(arr, false);6.7. if(carr = null) 8. return 0;9. 10.11. jint sum = 0;12. for(int i=0; ireleaseintarrayelements(arr,

38、 carr, 0);17. return sum;18. 19.復(fù)制代碼這個(gè)例子中的getintarrayelements和releaseintarrayelements函數(shù)就是jni提供用于處理int數(shù)組的函數(shù)。如果試圖用arr的方式去訪問(wèn)jintarray類型,毫無(wú)疑問(wèn)會(huì)出錯(cuò)。jni還提供了另一對(duì)函數(shù)getintarrayregion和 releaseintarrayregion訪問(wèn)int數(shù)組,就不介紹了,對(duì)于其他基本類型的數(shù)組,方法類似。4. 二維數(shù)組和string數(shù)組在jni中,二維數(shù)組和string數(shù)組都被視為object數(shù)組,因?yàn)閿?shù)組和string被視為object。仍然用一個(gè)例子來(lái)說(shuō)明,這次是一個(gè)二維int數(shù)組,作為返回值。java代碼:1.2. jniexport jobjectarray jnicall java_objectarraytest_initint2darray(jnienv *env, jclass cls, int size)3.4. jobjectarray result;5.6. jclass intarrcls = env-findclass(i);7. result

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論