編輯:關於Android編程
Android NDK系列(4) — SO中調用Java方法,我在博客上發表一些我的NDK學習心得,希望對大家能有幫助。
這一篇我們講述如何在so中調用java層的函數
首先,之前寫的文章中通過一個簡單的例子來使用了一下NDK,編寫了調用so中方法,返回一個字符串的功能,該方法是從Java層調用Native方法。
下面,我們要介紹的是如何從Native中調用Java方法。
廢話不多說,直接開始。
首先,Java方法簡單可以分為兩種,靜態方法和非靜態方法。
先給一個Java類,其中包含上述兩種方法
public class MyJni { ... private static void getvalue1(int value) { Log.d("123", "" + value); } private void getvalue2(int value) { Log.d("123", "" + value); } }
需要實現從 靜態&非靜態 Native方法中調用上述兩個方法。
在jni.h中定義了Call***Method()函數,通過這些函數,我們可以從Native中調用Java函數。
// 靜態函數 jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*); jobject (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...); jobject (*CallStaticObjectMethodV)(JNIEnv*, jclass, jmethodID, va_list); jobject (*CallStaticObjectMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jboolean (*CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...); jboolean (*CallStaticBooleanMethodV)(JNIEnv*, jclass, // 普通函數 jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__; jfloat (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list) __NDK_FPABI__; jfloat (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) __NDK_FPABI__; jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__; jdouble (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list) __NDK_FPABI__; jdouble (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) __NDK_FPABI__; void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list); void (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
在jni.h中定義了很多,大家可以去看一下。我這邊放上的是截取其中一部分。
看到上面的函數,主要區別在與返回值和參數不同,所以定義了很多不同的函數。在我們的例子中,使用的是如下兩個:
void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
在使用上述函數前,我們要對了解其中的參數:
JNIEnv *是指向vm的一個指針,通過該指針,我們可以調用到vm提供的很多功能。
jobjec 是表示調用函數的對象實例,即上面的 new MyJni()實例
jclass 是表示函數所在類的類信息,即上面的MyJni類
jmethodID 是表示需要調用的Java方法ID
其他就還有方法參數信息
為了獲取上述調用的所有的參數,首先調用初始化函數
myJni = new MyJni(); // MyJni.java: public MyJni() { Log.d("123", "Load MyJni nativeSetup"); nativeSetup(); // 生成對象的時候調用初始化函數 } public native void nativeSetup();
// 定義全局變量 jclass m_class; jobject m_object; jmethodID m_getValue, m_getV; // 初始化函數 JNIEXPORT void JNICALL Java_com_example_qiuyu_testhellojni_MyJni_nativeSetup (JNIEnv *env, jobject thiz) { // 初始化中存儲相應變量 jclass clazz = (*env)->GetObjectClass(env, thiz); //獲取當前對象的類信息 m_class = (jclass)(*env)->NewGlobalRef(env,clazz); //將類型信息存儲到m_class中 m_object = (jobject)(*env)->NewGlobalRef(env,thiz); // 將對象信息存儲到m_object中 m_getV = (*env)->GetMethodID(env,m_class,"getvalue2","(I)V"); // 根據類信息、方法名、參數返回值找到方法ID m_getValue = (*env)->GetStaticMethodID(env,m_class,"getvalue1", "(I)V"); // 根據類信息、方法名、參數返回值找到方法ID return; }
使用兩種方法調用Java方法,一種靜態native:nativeStaticExec, 另一種是非靜態native:nativeExec
JNIEXPORT void JNICALL Java_com_example_qiuyu_testhellojni_MyJni_nativeExc (JNIEnv *env, jobject thiz, jint value) { (*env)->CallStaticVoidMethod(env,m_class,m_getValue,value); (*env)->CallVoidMethod(env,m_object,m_getV,value); return; } /* * Class: com_example_qiuyu_testhellojni_MyJni * Method: nativeStaitcExec * Signature: (I)V */ JNIEXPORT void JNICALL Java_com_example_qiuyu_testhellojni_MyJni_nativeStaitcExec (JNIEnv *env, jclass clasz, jint value) { (*env)->CallStaticVoidMethod(env,m_class,m_getValue,value); (*env)->CallVoidMethod(env,m_object,m_getV,value); return; }
在Java層調用上面兩個native方法
public void onClick(View v) { switch (v.getId()) { case R.id.button: Log.d("123", "nativeStaitcExec"); MyJni.nativeStaitcExec(11); // Static break; case R.id.button1: Log.d("123", "nativeExec"); myJni.nativeExec(10); // normal break; } }
Github : https://github.com/QyMars/AndroidNativeCode
這樣,實現了簡單C調用Java方法,這種還是很好用的,比如在C中調用獲取應用簽名函數,並在Native層中創建線程來進行簽名校驗,關於這個可以在下一篇中講述。 由於手機不在身邊,也沒有實際的截圖,代碼我會放到我的Github上,給大家共享一下。
最近感觸很多,還是得把心沉下來,好好學習技術。爭取學的深一些,能多發一些干貨給大家。
ListView是每個app中都要使用的,所以今天我來總結下ListView的使用和一些簡單的優化。先看下運行效果:一、創建數據庫為了模擬數據,這裡將數據保存數據庫中,順
本文主要介紹使用Google自帶的FaceDetectionListener進行人臉檢測,並將檢測到的人臉用矩形框繪制出來。本文代碼基於PlayCameraV1.0.0,
0.使用多線程下載會提升文件下載的速度,那麼多線程下載文件的過程是: (1)首先獲得下載文件的長度,然後設置本地文件的長度 HttpURLConnection.get
我在此基礎上優化了部分代碼, 添加了滑動回調, 可自定義性更強. 並且添加了點擊按鈕左右滑動的功能.據說無圖都不敢發文章了.看圖:1:這種功能, 首先需要自己管理布局繼承