自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

OPhone平臺(tái)Native開發(fā)與JNI機(jī)制詳解

移動(dòng)開發(fā)
眾所周知,OPhone平臺(tái)上的應(yīng)用開發(fā)主要基于Java語言,但平臺(tái)完全支持且提供了一定的Native開發(fā)能力(主要是C/C++),使得開發(fā)者可以借助JNI更深入的實(shí)現(xiàn)創(chuàng)意。本文主要介紹OPhone平臺(tái)的JNI機(jī)制和Native模塊開發(fā)與發(fā)布的方法。

JNI簡(jiǎn)介

Java Native Interface(JNI)是Java提供的一個(gè)很重要的特性。它使得用諸如C/C++等語言編寫的代碼可以與運(yùn)行于Java虛擬機(jī)(JVM)中的Java代碼集成。有些時(shí)候,Java并不能滿足你的全部開發(fā)需求,比如你希望提高某些關(guān)鍵模塊的效率,或者你必須使用某個(gè)以C/C++等Native語言編寫的程序庫;此時(shí),JNI就能滿足你在Java代碼中訪問這些Native模塊的需求。JNI的出現(xiàn)使得開發(fā)者既可以利用Java語言跨平臺(tái)、類庫豐富、開發(fā)便捷等特點(diǎn),又可以利用Native語言的高效。

實(shí)際上,JNI是JVM實(shí)現(xiàn)中的一部分,因此Native語言和Java代碼都運(yùn)行在JVM的宿主環(huán)境(Host Environment),正如圖1所示。此外,JNI是一個(gè)雙向的接口:開發(fā)者不僅可以通過JNI在Java代碼中訪問Native模塊,還可以在Native代碼中嵌入一個(gè)JVM,并通過JNI訪問運(yùn)行于其中的Java模塊??梢?,JNI擔(dān)任了一個(gè)橋梁的角色,它將JVM與Native模塊聯(lián)系起來,從而實(shí)現(xiàn)了Java代碼與Native代碼的互訪。在OPhone上使用Java虛擬機(jī)是為嵌入式設(shè)備特別優(yōu)化的Dalvik虛擬機(jī)。每啟動(dòng)一個(gè)應(yīng)用,系統(tǒng)會(huì)建立一個(gè)新的進(jìn)程運(yùn)行一個(gè)Dalvik虛擬機(jī),因此各應(yīng)用實(shí)際上是運(yùn)行在各自的VM中的。Dalvik VM對(duì)JNI的規(guī)范支持的較全面,對(duì)于從JDK 1.2到JDK 1.6補(bǔ)充的增強(qiáng)功能也基本都能支持。

開發(fā)者在使用JNI之前需要充分了解其優(yōu)缺點(diǎn),以便合理選擇技術(shù)方案實(shí)現(xiàn)目標(biāo)。JNI的優(yōu)點(diǎn)前面已經(jīng)講過,這里不再重復(fù),其缺點(diǎn)也是顯而易見的:由于Native模塊的使用,Java代碼會(huì)喪失其原有的跨平臺(tái)性和類型安全等特性。此外,在JNI應(yīng)用中,Java代碼與Native代碼運(yùn)行于同一個(gè)進(jìn)程空間內(nèi);對(duì)于跨進(jìn)程甚至跨宿主環(huán)境的Java與Native間通信的需求,可以考慮采用socket、Web Service等IPC通信機(jī)制來實(shí)現(xiàn)。

在OPhone開發(fā)中使用JNI

正如我們?cè)谏弦还?jié)所述,JNI是一個(gè)雙向的接口,所以交互的類型可以分為在Java代碼中調(diào)用Native模塊和在Native代碼中調(diào)用Java模塊兩種。下面,我們就使用一個(gè)Hello-JNI的示例來分別對(duì)這兩種交互方式的開發(fā)要點(diǎn)加以說明。

Java調(diào)用Native模塊

Hello-JNI這個(gè)示例的結(jié)構(gòu)很簡(jiǎn)單:首先我們使用Eclipse新建一個(gè)OPhone應(yīng)用的Java工程,并添加一個(gè)com.example.hellojni.HelloJni的類。這個(gè)類實(shí)際上是一個(gè)Activity,稍后我們會(huì)創(chuàng)建一個(gè)TextView,并顯示一些文字在上面。

要在Java代碼中使用Native模塊,必須先對(duì)Native函數(shù)進(jìn)行聲明。在我們的例子中,打開HelloJni.java文件,可以看到如下的聲明:#p#

  1. /* A native method that is implemented by the
  2. * 'hello-jni' native library, which is packaged
  3. * with this application.
  4. */
  5. public native String stringFromJNI();

從上述聲明中我們可以知道,這個(gè)stringFromJNI()函數(shù)就是要在Java代碼中調(diào)用的Native函數(shù)。接下來我們要?jiǎng)?chuàng)建一個(gè)hello-jni.c的C文件,內(nèi)容很簡(jiǎn)單,只有如下一個(gè)函數(shù):

  1. #include <string.h>
  2. #include <jni.h>
  3. jstring
  4. Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
  5. jobject thiz ) {
  6. return (*env)->NewStringUTF(env, "Hello from JNI !");
  7. }

從函數(shù)名可以看出,這個(gè)Native函數(shù)對(duì)應(yīng)的正是我們?cè)赾om.example.hellojni.HelloJni這個(gè)中聲明的Native函數(shù)String stringFromJNI()的具體實(shí)現(xiàn)。

從上面Native函數(shù)的命名上我們可以了解到JNI函數(shù)的命名規(guī)則: Java代碼中的函數(shù)聲明需要添加native關(guān)鍵字;Native的對(duì)應(yīng)函數(shù)名要以“Java_”開頭,后面依次跟上Java的“package名”、“class名”、“函數(shù)名”,中間以下劃線“_”分割,在package名中的“.”也要改為“_”。此外,關(guān)于函數(shù)的參數(shù)和返回值也有相應(yīng)的規(guī)則。對(duì)于Java中的基本類型如int、double、char等,在Native端都有相對(duì)應(yīng)的類型來表示,如jint、jdouble、jchar等;其他的對(duì)象類型則統(tǒng)統(tǒng)由jobject來表示(String是個(gè)例外,由于其使用廣泛,故在Native代碼中有jstring這個(gè)類型來表示,正如在上例中返回值String對(duì)應(yīng)到Native代碼中的返回值jstring)。而對(duì)于Java中的數(shù)組,在Native中由jarray對(duì)應(yīng),具體到基本類型和一般對(duì)象類型的數(shù)組則有jintArray等和jobjectArray分別對(duì)應(yīng)(String數(shù)組在這里沒有例外,同樣用jobjectArray表示)。還有一點(diǎn)需要注意的是,在JNI的Native函數(shù)中,其前兩個(gè)參數(shù)JNIEnv*和jobject是必需的——前者是一個(gè)JNIEnv結(jié)構(gòu)體的指針,這個(gè)結(jié)構(gòu)體中定義了很多JNI的接口函數(shù)指針,使開發(fā)者可以使用JNI所定義的接口功能;后者指代的是調(diào)用這個(gè)JNI函數(shù)的Java對(duì)象,有點(diǎn)類似于C++中的this指針。在上述兩個(gè)參數(shù)之后,還需要根據(jù)Java端的函數(shù)聲明依次對(duì)應(yīng)添加參數(shù)。在上例中,Java中聲明的JNI函數(shù)沒有參數(shù),則Native的對(duì)應(yīng)函數(shù)只有類型為JNIEnv*和jobject的兩個(gè)參數(shù)。

當(dāng)然,要使用JNI函數(shù),還需要先加載Native代碼編譯出來的動(dòng)態(tài)庫文件(在Windows上是.dll,在Linux上則為.so)。這個(gè)動(dòng)作是通過如下語句完成的:

  1. static {
  2. System.loadLibrary("hello-jni");
  3. }

注意這里調(diào)用的共享庫名遵循Linux對(duì)庫文件的命名慣例,因?yàn)镺Phone的核心實(shí)際上是Linux系統(tǒng)——上例中,實(shí)際加載的庫文件應(yīng)為“l(fā)ibhello-jni.so”,在引用時(shí)遵循命名慣例,不帶“l(fā)ib”前綴和“.so”的擴(kuò)展名。對(duì)于沒有按照上述慣例命名的Native庫,在加載時(shí)仍需要寫成完整的文件名。

JNI函數(shù)的使用方法和普通Java函數(shù)一樣。在本例中,調(diào)用代碼如下:

  1. TextView tv = new TextView(this);
  2. tv.setText( stringFromJNI() );
  3. setContentView(tv);

就可以在TextView中顯示出來自于Native函數(shù)的字符串。怎么樣,是不是很簡(jiǎn)單呢?

Native調(diào)用Java模塊

從OPhone的系統(tǒng)架構(gòu)來看,JVM和Native系統(tǒng)庫位于內(nèi)核之上,構(gòu)成OPhone Runtime;更多的系統(tǒng)功能則是通過在其上的Application Framework以Java API的形式提供的。因此,如果希望在Native庫中調(diào)用某些系統(tǒng)功能,就需要通過JNI來訪問Application Framework提供的API。#p#

JNI規(guī)范定義了一系列在Native代碼中訪問Java對(duì)象及其成員與方法的API。下面我們還是通過示例來具體講解。首先,新建一個(gè)SayHello的類,代碼如下:

  1. package com.example.hellojni;
  2. public class SayHello {
  3. public String sayHelloFromJava(String nativeMsg) {
  4. String str = nativeMsg + " But shown in Java!";
  5. return str;
  6. }
  7. }

接下來要實(shí)現(xiàn)的就是在Native代碼中調(diào)用這個(gè)SayHello類中的sayHelloFromJava方法。

一般來說,要在Native代碼中訪問Java對(duì)象,有如下幾個(gè)步驟:

1. 得到該Java對(duì)象的類定義。JNI定義了jclass這個(gè)類型來表示Java的類的定義,并提供了FindClass接口,根據(jù)類的完整的包路徑即可得到其jclass。

2. 根據(jù)jclass創(chuàng)建相應(yīng)的對(duì)象實(shí)體,即jobject。在Java中,創(chuàng)建一個(gè)新對(duì)象只需要使用new關(guān)鍵字即可,但在Native代碼中創(chuàng)建一個(gè)對(duì)象則需要兩步:首先通過JNI接口GetMethodID得到該類的構(gòu)造函數(shù),然后利用NewObject接口構(gòu)造出該類的一個(gè)實(shí)例對(duì)象。

3. 訪問jobject中的成員變量或方法。訪問對(duì)象的方法是先得到方法的Method ID,然后使用CallMethod接口調(diào)用,這里Type對(duì)應(yīng)相應(yīng)方法的返回值——返回值為基本類型的都有相對(duì)應(yīng)的接口,如CallIntMethod;其他的返回值(包括String)則為CallObjectMethod??梢钥闯?,創(chuàng)建對(duì)象實(shí)質(zhì)上是調(diào)用對(duì)象的一個(gè)特殊方法,即構(gòu)造函數(shù)。訪問成員變量的步驟一樣:首先GetFieldID得到成員變量的ID,然后Get/SetField讀/寫變量值。

上面概要介紹了從Native代碼中訪問Java對(duì)象的過程,下面我們結(jié)合示例來具體看一下。如下是調(diào)用sayHelloFromJava方法的Native代碼:

  1. jstring helloFromJava( JNIEnv* env ) {
  2. jstring str = NULL;
  3. jclass clz = (*env)->FindClass(env, "com/example/hellojni/SayHello");
  4. jmethodID ctor = (*env)->GetMethodID(env, clz, "<init>", "()V");
  5. jobject obj = (*env)->NewObject(env, clz, ctor);
  6. jmethodID mid = (*env)->GetMethodID(env, clz, "sayHelloFromJava", "(Ljava/lang/String;)Ljava/lang/String;");
  7. if (mid) {
  8. jstring jmsg = (*env)->NewStringUTF(env, "I'm born in native.");
  9. str = (*env)->CallObjectMethod(env, obj, mid, jmsg);
  10. }
  11. return str;
  12. }

可以看到,上述代碼和前面講到的步驟完全相符。這里提一下編程時(shí)要注意的要點(diǎn):1、FindClass要寫明Java類的完整包路徑,并將“.”以“/”替換;2、GetMethodID的第三個(gè)參數(shù)是方法名(對(duì)于構(gòu)造函數(shù)一律用“”表示),第四個(gè)參數(shù)是方法的“簽名”,需要用一個(gè)字符串序列表示方法的參數(shù)(依聲明順序)和返回值信息。由于篇幅所限,這里不再具體說明如何根據(jù)方法的聲明構(gòu)造相應(yīng)的“簽名”,請(qǐng)參考JNI的相關(guān)文檔。

關(guān)于上面談到的步驟再補(bǔ)充說明一下:在JNI規(guī)范中,如上這種使用NewObject創(chuàng)建的對(duì)象實(shí)例被稱為“Local Reference”,它僅在創(chuàng)建它的Native代碼作用域內(nèi)有效,因此應(yīng)避免在作用域外使用該實(shí)例及任何指向它的指針。如果希望創(chuàng)建的對(duì)象實(shí)例在作用域外也能使用,則需要使用NewGlobalRef接口將其提升為“Global Reference”——需要注意的是,當(dāng)Global Reference不再使用后,需要顯式的釋放,以便通知JVM進(jìn)行垃圾收集。

Native模塊的編譯與發(fā)布

通過前面的介紹,我們已經(jīng)大致了解了在OPhone的應(yīng)用開發(fā)中使用JNI的方法。那么,開發(fā)者如何編譯出能在OPhone上使用的Native模塊呢?編譯出的Native模塊又如何像APK文件那樣分發(fā)、安裝呢?

Google于2009年6月底發(fā)布了Android NDK的***個(gè)版本,為廣大開發(fā)者提供了編譯用于Android應(yīng)用的Native模塊的能力,以及將Native模塊隨Java應(yīng)用打包為APK文件,以便分發(fā)和安裝的整套解決方案。NDK的全稱是Native Development Toolkit,即原生應(yīng)用開發(fā)包。由于OPhone平臺(tái)也基于Android,因此使用Android NDK編譯的原生應(yīng)用或組件完全可以用于OPhone。需要注意的是,Google聲稱此次發(fā)布的NDK僅兼容于Android 1.5及以后的版本,由于OPhone 1.0平臺(tái)基于Android 1.5之前的版本,雖然不排除使用該NDK開發(fā)的原生應(yīng)用或組件在OPhone 1.0平臺(tái)上正常運(yùn)行的可能性,但建議開發(fā)者僅在OPhone 1.5及以上的平臺(tái)使用。#p#

***版本的NDK可以在http://developer.android.com/sdk/ndk/index.html下載。NDK提供了適用于Windows、Linux和MAC OS X的版本,開發(fā)者可以根據(jù)自己的操作系統(tǒng)下載相應(yīng)的版本。本文僅使用基于Linux的NDK版本做介紹和演示。

NDK的安裝很簡(jiǎn)單:解壓到某個(gè)路徑下即可,之后可以看到若干目錄。其中docs目錄中包含了比較詳細(xì)的文檔,可供開發(fā)者參考,在NDK根目錄下的README.TXT也對(duì)個(gè)別重要文檔進(jìn)行了介紹;build目錄則包含了用于Android設(shè)備的交叉編譯器和相關(guān)工具,以及一組系統(tǒng)頭文件和系統(tǒng)庫,其中包括libc、libm、libz、liblog(用于Android設(shè)備log輸出)、JNI接口及一個(gè)C++標(biāo)準(zhǔn)庫的子集(所謂“子集”是指Android對(duì)C++支持有限,如不支持Exception及STL等);apps目錄是用于應(yīng)用開發(fā)的目錄,out目錄則用于編譯中間結(jié)果的存儲(chǔ)。接下來,我們就用前面的例子簡(jiǎn)單講解一下NDK的使用。

進(jìn)入/apps目錄,我們可以看到一些示例應(yīng)用,以hello-jni為例:在hello-jni目錄中有一個(gè)Application.mk文件和一個(gè)project文件夾,project文件夾中則是一個(gè)OPhone Java應(yīng)用所有的工程文件,其中jni目錄就是Native代碼放置的位置。這里Application.mk主要用于告訴編譯器應(yīng)用所需要用到的Native模塊有什么,對(duì)于一般開發(fā)在示例提供的文件的基礎(chǔ)上進(jìn)行修改即可;如果需要了解更多,可參考/docs/APPLICATION-MK.txt。接下來,我們將示例文件與代碼如圖2放置到相應(yīng)的位置:

可以看到,和Java應(yīng)用一樣,Native模塊也需要使用Android.mk文件設(shè)置編譯選項(xiàng)和參數(shù),但內(nèi)容有較大不同。對(duì)于Native模塊而言,一般需要了解如下幾類標(biāo)簽:

1.LOCAL_MODULE:定義了在整個(gè)編譯環(huán)境中的各個(gè)模塊,其名字應(yīng)當(dāng)是唯一的。此外,這里設(shè)置的模塊名稱還將作為編譯出來的文件名:對(duì)于原生可執(zhí)行文件,文件名即為模塊名稱;對(duì)于靜態(tài)/動(dòng)態(tài)庫文件,文件名為lib+模塊名稱。例如hello-jni的模塊名稱為“hello-jni”,則編譯出來的動(dòng)態(tài)庫就是libhello-jni.so。

2.LOCAL_SRC_FILES:這里要列出所有需要編譯的C/C++源文件,以空格或制表符分隔;如需換行,可放置“\”符號(hào)在行尾,這和GNU Makefile的規(guī)則是一致的。

3.LOCAL_CFLAGS:定義gcc編譯時(shí)的CFLAGS參數(shù),與GNU Makefile的規(guī)則一致。比如,用-I參數(shù)可指定編譯所需引用的某個(gè)路徑下的頭文件。

4.LOCAL_C_INCLUDES:指定自定義的頭文件路徑。

5.LOCAL_SHARED_LIBRARIES:定義鏈接時(shí)所需要的共享庫文件。這里要鏈接的共享庫并不限于NDK編譯環(huán)境中定義的所有模塊。如果需要引用其他的庫文件,也可在此處指定。

6.LOCAL_STATIC_LIBRARIES:和上個(gè)標(biāo)簽類似,指定需要鏈接的靜態(tài)庫文件。需要注意的是這個(gè)選項(xiàng)只有在編譯動(dòng)態(tài)庫的時(shí)候才有意義。

7.LOCAL_LDLIBS:定義鏈接時(shí)需要引入的系統(tǒng)庫。使用時(shí)需要加-l前綴,例如-lz指的是在加載時(shí)鏈接libz這個(gè)系統(tǒng)庫。libc、libm和libstdc++是編譯系統(tǒng)默認(rèn)會(huì)鏈接的,無需在此標(biāo)簽中指定。

欲了解更多關(guān)于標(biāo)簽類型及各類標(biāo)簽的信息,可參考/docs/ANDROID-MK.txt文件,其中詳細(xì)描述了Android.mk中各個(gè)標(biāo)簽的含義與用法。如下給出的就是我們的示例所用的Android.mk:

  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_MODULE := hello-jni
  4. LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
  5. LOCAL_SRC_FILES := src/call_java.c \
  6. src/hello-jni.c
  7. include $(BUILD_SHARED_LIBRARY)

寫好了代碼和Makefile,接下來就是編譯了。使用NDK進(jìn)行編譯也很簡(jiǎn)單:首先從命令行進(jìn)入目錄,執(zhí)行./build/host-setup.sh,當(dāng)打印出“Host setup complete.”的文字時(shí),編譯環(huán)境的設(shè)置就完成了。這里開發(fā)者需要注意的是,如果使用的Linux發(fā)行版是Debian或者Ubuntu,需要通過在目錄下執(zhí)行bash build/host-setup.sh,因?yàn)樯鲜鰞蓚€(gè)發(fā)行版使用的dash shell與腳本有兼容問題。接下來,輸入make APP=hello-jni,稍等片刻即完成編譯,如圖3所示。從圖中可以看到,在編譯完成后,NDK會(huì)自動(dòng)將編譯出來的共享庫拷貝到Java工程的libs/armeabi目錄下。當(dāng)編譯Java工程的時(shí)候,相應(yīng)的共享庫會(huì)被一同打包到apk文件中。在應(yīng)用安裝時(shí),被打包在libs/armeabi目錄中的共享庫會(huì)被自動(dòng)拷貝到/data/data/com.example.HelloJni/lib/目錄;當(dāng)System.loadLibrary被調(diào)用時(shí),系統(tǒng)就可以在上述目錄尋找到所需的庫文件libhello-jni.so。如果實(shí)際的Java工程不在這里,也可以手動(dòng)在Java工程下創(chuàng)建libs/armeabi目錄,并將編譯出來的so庫文件拷貝過去。

***,將Java工程連帶庫文件一同編譯并在OPhone模擬器中運(yùn)行,結(jié)果如圖4所示。

通過上面的介紹,你應(yīng)該已經(jīng)對(duì)OPhone上的Native開發(fā)有了初步了解,或許也已經(jīng)躍躍欲試了。事實(shí)上,盡管Native開發(fā)在OPhone上不具有Java語言的類型安全、兼容性好、易于調(diào)試等特性,也無法直接享受平臺(tái)提供的豐富的API,但JNI還是為我們提供了更多的選擇,使我們可以利用原生應(yīng)用的優(yōu)勢(shì)來做對(duì)性能要求高的操作,也可以利用或移植C/C++領(lǐng)域現(xiàn)有的眾多功能強(qiáng)大的類庫或應(yīng)用,為開發(fā)者提供了充分的施展空間。這就是OPhone的魅力!

【編輯推薦】

  1. Android/OPhone開發(fā)完全講義
  2. 云計(jì)算應(yīng)用與Ophone是終端基礎(chǔ)設(shè)施
  3. 1.3.1 OPhone介紹
  4. Android系統(tǒng)平臺(tái)與OPhone之間的對(duì)攻戰(zhàn)
  5. 7.1.3 OPhone平臺(tái)支持的媒體格式
責(zé)任編輯:chenqingxiang 來源: ophonesdn
相關(guān)推薦

2010-07-23 15:30:18

2010-03-04 16:08:21

Android系統(tǒng)平臺(tái)

2021-09-23 14:41:58

鴻蒙HarmonyOS應(yīng)用

2010-07-26 15:47:02

Ophone系統(tǒng)

2010-09-17 10:18:59

ODTOPhone

2010-07-23 16:08:38

OPhone平臺(tái)

2009-03-26 08:26:22

AndroidGoogle移動(dòng)OS

2023-02-09 07:15:52

開發(fā)FlutterReact

2024-04-18 08:27:05

Android數(shù)據(jù)類型

2010-07-26 12:57:12

OPhone游戲開發(fā)

2009-04-10 08:42:38

OMS移動(dòng)OSophone

2010-07-26 14:25:06

Widget開發(fā)

2010-07-26 14:44:47

Widget開發(fā)

2010-07-26 13:55:10

OPhone游戲開發(fā)

2024-11-07 09:37:46

2009-05-07 08:32:59

中國移動(dòng)OphoneAndroid

2012-05-25 09:09:25

Windows Pho

2011-08-18 10:59:57

iPhone開發(fā)消息通信NSNotificat

2010-07-26 12:33:04

控件

2009-12-11 17:14:05

VS2008 開發(fā)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)