一、前言:
以前做過一個NDK相關的項目,通常用NDK的應用,主要是目的在於數據保密(比如私有通信協議,或安全加密數據),音視頻編解碼相關等。
先上個流程圖,讓大家瞭解這個例子的調用關係:
二、代碼講解:
2.1 Java層代碼:
MainActivity.class代碼:
package com.ndk.chris.test;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private static final String libSoName = "ChrisTest";
private Button btnClick = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnClick = (Button) this.findViewById(R.id.btn_click);
btnClick.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
helloWorld();
}
});
}
public native void helloWorld() ;
/**
* 載入JNI生成的so庫文件
*/
static {
System.loadLibrary(libSoName);
}
}
MainActivity很簡單,聲明一個native的方法,並且在一開始就將本地庫加載起來,佈局裏定義了一個Button,點擊按鈕,調用NDK中的helloWorld方法。
CalledByNDK.class
package com.ndk.chris.test;
import android.util.Log;
public class CalledByNDK {
private final static String TAG = "ChrisTest";
public static String helloWorld1() {
Log.d(TAG, "NDK(C)調用Java靜態方法, 返回當前時間");
return String.valueOf(System.currentTimeMillis());
}
public void helloWorld2(String msg) {
Log.d(TAG, "NDK(C)調用Java普通方法:" + msg);
}
}
這個類中,實現了兩個方法,一個是靜態,一個是非靜態的,這兩個方法將會被NDK即C調用。
2.2 NDK層C代碼
CalledByJava.c
#include <string.h>
#include <android/log.h>
#include <jni.h>
#include "CallJava.h"
JNIEnv* jniEnv;
/**
* Java 中 聲明的native helloWorld 方法的實現
*
* 環境變量env是由JVM傳遞過來的;
* thiz是Java對象的引用
*/
void Java_com_ndk_chris_test_MainActivity_helloWorld(JNIEnv* env, jobject thiz)
{
if (jniEnv == NULL) {
jniEnv = env;
}
doHelloWorld();
}
JNI中接口的定義必需按照Java_包名_類名_方法名;的形式來定義,這樣JVM才能找到JAVA中調用Native的方法。
doHelloWorld在CallJava.h中聲明:
#include <string.h>
#include <jni.h>
void doHelloWorld();
CallJava.c
#include "CallJava.h"
#include <android/log.h>
extern JNIEnv* jniEnv;
jclass CalledByNDK;
jobject mCalledByNDK;
jmethodID helloWorld1;
jmethodID helloWorld2;
int GetCallJavaClassInstance(jclass obj_class) {
if(obj_class == NULL) {
return 0;
}
jmethodID construction_id = (*jniEnv)->GetMethodID(jniEnv, obj_class,
"<init>", "()V");
if (construction_id == 0) {
return -1;
}
mCalledByNDK = (*jniEnv)->NewObject(jniEnv, obj_class,
construction_id);
if (mCalledByNDK == NULL) {
return -2;
}
return 1;
}
/**
* 初始化 類、對象、方法
*/
int InitProvider() {
__android_log_print(ANDROID_LOG_INFO, "ChrisTest", "InitProvider Enter" );
if(jniEnv == NULL) {
__android_log_print(ANDROID_LOG_INFO, "ChrisTest",
"InitProvider jniEnv is Null!");
return 0;
}
if(CalledByNDK == NULL) {
CalledByNDK = (*jniEnv)->FindClass(jniEnv,"com/ndk/chris/test/CalledByNDK");
if(CalledByNDK == NULL){
__android_log_print(ANDROID_LOG_INFO, "ChrisTest",
"InitProvider CalledByNDK is Null!");
return -1;
}
}
if (mCalledByNDK == NULL) {
if (GetCallJavaClassInstance(CalledByNDK) != 1) {
__android_log_print(ANDROID_LOG_INFO, "ChrisTest",
"InitProvider getInstanceError");
(*jniEnv)->DeleteLocalRef(jniEnv, CalledByNDK);
return -1;
}
}
if (helloWorld1 == NULL) {
helloWorld1 = (*jniEnv)->GetStaticMethodID(jniEnv, CalledByNDK,
"helloWorld1","()Ljava/lang/String;");
if (helloWorld1 == NULL) {
__android_log_print(ANDROID_LOG_INFO, "ChrisTest",
"InitProvider get helloWorld1 static methord error!");
(*jniEnv)->DeleteLocalRef(jniEnv, CalledByNDK);
(*jniEnv)->DeleteLocalRef(jniEnv, mCalledByNDK);
return -2;
}
}
if (helloWorld2 == NULL) {
helloWorld2 = (*jniEnv)->GetMethodID(jniEnv, CalledByNDK,
"helloWorld2","(Ljava/lang/String;)V");
if (helloWorld2 == NULL) {
__android_log_print(ANDROID_LOG_INFO, "ChrisTest",
"InitProvider get helloWorld2 non-static methord error!");
(*jniEnv)->DeleteLocalRef(jniEnv, CalledByNDK);
(*jniEnv)->DeleteLocalRef(jniEnv, mCalledByNDK);
(*jniEnv)->DeleteLocalRef(jniEnv, helloWorld1);
return -3;
}
}
__android_log_print(ANDROID_LOG_INFO, "ChrisTest",
"InitProvider Leave");
return 1;
}
/**
* 調用 Java 方法
*/
void doHelloWorld() {
if(CalledByNDK == NULL || mCalledByNDK == NULL ||
helloWorld1 == NULL || helloWorld2 == NULL) {
int result = InitProvider() ;
if(result != 1) {
return;
}
}
jstring jstr = NULL;
char* cstr = NULL;
jstr = (*jniEnv)->CallStaticObjectMethod(jniEnv, CalledByNDK, helloWorld1);
cstr = (char*) (*jniEnv)->GetStringUTFChars(jniEnv,jstr, 0);
__android_log_print(ANDROID_LOG_INFO, "ChrisTest",
"[NDK]time from java = %s",cstr);
(*jniEnv)->ReleaseStringUTFChars(jniEnv, jstr, cstr);
(*jniEnv)->DeleteLocalRef(jniEnv, jstr);
jstring jstrMSG = NULL;
jstrMSG =(*jniEnv)->NewStringUTF(jniEnv, "Hi,I'm From C");
(*jniEnv)->CallVoidMethod(jniEnv, mCalledByNDK, helloWorld2, jstrMSG);
(*jniEnv)->DeleteLocalRef(jniEnv, jstrMSG);
}
這裏,我們需要注意幾點:
1. C調用JAVA中的方法,需要先初始化類,對象和方法,在FindClass中,一定要寫全類所在的包名,在初始化方法時,需要注意該方法在JAVA中是靜態還是非靜態,所用的接口也是不一樣的,靜態的是GetStaticMethodID,非靜態的是GetMethodID,別忘記寫明參數類型和方法返回類型;
2. 調用JAVA方法,根據靜態與非靜態,方法有:CallStaticObjectMethod, CallVoidMethod 等返回類型;