Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android NDK系列(4) — SO中調用Java方法

Android NDK系列(4) — SO中調用Java方法

編輯:關於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上,給大家共享一下。
最近感觸很多,還是得把心沉下來,好好學習技術。爭取學的深一些,能多發一些干貨給大家。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved