layout: post title: "淺析JNI" date: 2013-06-26 22:31 comments: truejava
本文從如下4個部分進行:android
我相信,若是你弄懂了以上的問題,可使用jni技術進行基本的開發。本文經過實現一個簡單的demo,對以上的問題的進行解答。git
先看MediaScanner.java的代碼程序員
{% codeblock MediaScanner lang:java %}github
public class jniActivity extends Activity {web
private TextView tv; /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); TextView tv = new TextView(this); tv.setText( stringFromJNI() ); setContentView(tv); } // 這個方法採用靜態註冊,參考ndk自帶的例子hello-jni的實現 public native String stringFromJNI(); // 修改爲動態註冊 public native String DynamicStringFromJNI(); // 提供方法,讓native層調用 public void testMethodForNativeCallJava(){ Toast.makeText(getApplicationContext(), "Call from Native", Toast.LENGTH_SHORT).show(); } // 加載jni庫 static{ System.loadLibrary("learnJni"); }
}數據結構
{% endcodeblock %}app
能夠大體猜到,static塊load了jni的庫文件,有好幾個帶native聲明的方法,這些方法都沒有具體的實現,其實現應該在native層也就是c層。該方法對java來說使用起來沒什麼不一樣。Java層須要作的事情就結束了,祕密應該就在這兩個咱們不熟悉的部分。總結下Java層的工做:eclipse
看來Jni對Java程序員仍是很友好的,使用起來很方便。ide
首先,咱們看下最簡單的native方法實現,修改自ndk自帶的sample:hello-jni的hellojni.c
{% codeblock learnjni lang:java %}
/ This is a trivial JNI example where we use a native method * to return a new VM String. See the corresponding Java source * file located at: * *
/ jstring Java_com_example_learnjni_LearnJni_stringFromJNI( JNIEnv env, jobject thiz ) { return (env)->NewStringUTF(env, "Hello from JNI !"); } {% endcodeblock %}
明顯,這裏的函數名好奇怪,一大串。其實若是你有jni的基礎,你會發現這個函數名和你知道的靜態註冊和動態註冊都不同,這裏並無使用包含方法簽名的頭文件,而是使用虛擬機默認的函數調用方式。
註冊這個概念不難理解,打個比方,java和c是兩個世界的東西,若要將他們鏈接在一塊兒,必需要有一個統一的溝通標準,註冊就是將雙方的方法函數用一個標準描述,通知對方。JNI裏面有兩種註冊方式,分別是靜態註冊和動態註冊。下面咱們來詳細介紹下這兩種註冊方式:
靜態註冊比較簡單,其步驟有二:
javah -o output package.classname
,這樣會生成一個output.h的jni頭文件,package.classname是java編譯好的class文件。IdeaProject/*/out
cd IdeaProject/*/out
javah -o output -classpath ~/IdeaProjects/Learn/learn-jni/src/ com.example.learn_jni.jniActivity
/ Header for class com_example_learn_jni_jniActivity /
extern "C" {
/ * Class: com_example_learn_jni_jniActivity * Method: stringFromJNI * Signature: ()Ljava/lang/String; / JNIEXPORT jstring JNICALL Java_com_example_learn_1jni_jniActivity_stringFromJNI (JNIEnv *, jobject);
/ * Class: com_example_learn_jni_jniActivity * Method: DynamicStringFromJNI * Signature: ()Ljava/lang/String; / JNIEXPORT jstring JNICALL Java_com_example_learn_1jni_jniActivity_DynamicStringFromJNI (JNIEnv *, jobject);
}
{% endcodeblock %}
須要解釋下靜態方法中native方法是如何和jni函數對應上。當Java層調用native方法,好比stringFromJNI時,它會從對應的JNI庫中尋找 Java_com_example_learn_1jni_jniActivity_stringFromJNI
,若是找不到就報錯。若是找到,則爲兩者創建一個映射關係,實際上是保存了jni層函數的函數指針。固然,這項工做由虛擬機完成。
能夠明顯的看出,靜態註冊方法有很多弊端。