一、前言:
以前做过一个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 等返回类型;