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