一、简介
关于NDK有两种方法注册:静态注册
和动态注册
。
- 静态注册: 就是直接在Java文件里写个native方法 然后再c/c++文件中实现这个方法就行了;
- 动态注册: 就是为了不要写很长的方法名,用
JNI_OnLoad
方法实现预注册,即当执行System.LoadLibrary()
方法时候就把需要调用的方法给注册了,效率要高 Android就是采用此方法;
下面就以示例来说明静态注册
、动态注册
以及C层调用Java层方法
。
二、代码实现
Github:NDKDemo
Java层Native方法定义
package com.cloudwise.ndk;
import android.util.Log;public class NDKUtil {// 静态注册Native方法public native static String stringFromJNI();// 静态注册Native方法public native static void setJNILogEnable(int type);// 动态注册Native方法public native static int getVersionCode();// 动态注册Native方法public native static String getVersion(int code);// 动态注册Native方法,并且C回调Java方法public native static void callJavaString();// 动态注册Native方法,并且C回调Java方法public native static void callJavaVoid();// C调用Java方法(带返回值)public static String getStringToC(String name){Log.e("CLOUDWISE", "name : " + name);return "C From Java";}// C调用Java方法(不带返回值)public static void getVoidToC(int id, String name){Log.e("CLOUDWISE", "id : " + id + " ---- name : " + name);}
}
C层代码
common.h
#ifndef NDKDEMO_COMMON_H
#define NDKDEMO_COMMON_H#ifdef __cplusplus
extern "C" {
#endif#define LOGOPEN 1 //日志开关,1为开,其它为关
#define LOG_TAG "[CLOUDWISE-NDK]"
#if(LOGOPEN==1)#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)#define LOGD(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#else
#define LOGI(...) NULL#define LOGE(...) NULL#define LOGD(...) NULL
#endif#ifdef __cplusplus
}
#endif#endif //NDKDEMO_COMMON_H
native-lib.c
#include <jni.h>
#include <string.h>
#include <android/log.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include "common.h"static JavaVM *j_vm = NULL;
static jclass j_class = NULL;static jint debug = 1;/*** 静态注册的Native方法* @param env* @param arg* @return*/
JNIEXPORT jstring JNICALL Java_com_cloudwise_ndk_NDKUtil_stringFromJNI(JNIEnv *env,jclass arg) {jstring hello = "Hello ---- from ---- C";if(debug == 1){LOGE("stringFromJNI ---- Hello ---- from ---- C");}return (*env)->NewStringUTF(env, hello);
}/*** 静态注册的Native方法* @param env* @param arg* @param type*/
JNIEXPORT void JNICALL Java_com_cloudwise_ndk_NDKUtil_setJNILogEnable(JNIEnv *env,jclass arg, jint type){if(type == 1){debug = 1;} else {debug = 0;}
}/*** 动态注册的Native方法* @param env* @param arg* @return*/
JNIEXPORT jint JNICALL getVersionCode(JNIEnv *env, jclass arg){if(debug == 1){LOGE("getVersionCode ----------- 10");}return 10;
}/*** 动态注册的Native方法* @param env* @param arg* @param code* @return*/
JNIEXPORT jstring JNICALL getVersion(JNIEnv *env, jclass arg, jint code){if(debug == 1){LOGE("getVersion ----------- 1.2.6");}jstring ver = "1.2.6";return (*env)->NewStringUTF(env, ver);
}/*** C调用Java方法(带参数不带返回值)*/
void callVoidFromJava(){JNIEnv *env;(*j_vm)->AttachCurrentThread(j_vm, &env, NULL);jmethodID methodid = (*env)->GetStaticMethodID(env, j_class, "getVoidToC", "(ILjava/lang/String;)V");(*env)->CallStaticVoidMethod(env, j_class, methodid, 10, (*env)->NewStringUTF(env, "C-Name"));if(debug == 1) {LOGE("callVoidFromJava Java To C");}
}/*** C调用Java方法(带参数带返回值)*/
void callStringFromJava(){JNIEnv *env;(*j_vm)->AttachCurrentThread(j_vm, &env, NULL);jmethodID methodid = (*env)->GetStaticMethodID(env, j_class, "getStringToC", "(Ljava/lang/String;)Ljava/lang/String;");jstring str = (jstring)(*env)->CallStaticObjectMethod(env, j_class, methodid,(*env)->NewStringUTF(env, "C-Name"));char* java = (char*)(*env)->GetStringUTFChars(env, str, NULL);if(debug == 1) {LOGE("callStringFromJava Java To C : %s", java);}
}/*** 动态注册的Native方法,然后调用Java方法* @param env* @param arg*/
JNIEXPORT void JNICALL callJavaString(JNIEnv *env, jclass arg) {callStringFromJava();
}/*** 动态注册的Native方法,然后调用Java方法* @param env* @param arg*/
JNIEXPORT void JNICALL callJavaVoid(JNIEnv *env, jclass arg) {callVoidFromJava();
}static JNINativeMethod mMethods[] = {{"getVersionCode", "()I", (void*)getVersionCode},{"getVersion", "(I)Ljava/lang/String;", (void*)getVersion},{"callJavaString", "()V", (void*)callJavaString},{"callJavaVoid", "()V", (void*)callJavaVoid}
};static int registerNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods){jclass clazz;clazz = (*env)->FindClass(env, className);if(clazz == NULL){return -1;}j_class = (jclass)(*env) -> NewGlobalRef(env, (jobject)clazz);if((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0){return -1;}return 0;
}static int registerNative(JNIEnv* env){return registerNativeMethods(env, "com/cloudwise/ndk/NDKUtil", mMethods, sizeof(mMethods)/ sizeof(mMethods[0]));
}JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){LOGE("JNI_OnLoad ------------------------------ ");JNIEnv* env = NULL;jint result = -1;/** JavaVM::GetEnv 原型为 jint (*GetEnv)(JavaVM*, void**, jint);* GetEnv()函数返回的 Jni 环境对每个线程来说是不同的,* 由于Dalvik虚拟机通常是Multi-threading的。每一个线程调用JNI_OnLoad()时,* 所用的JNI Env是不同的,因此我们必须在每次进入函数时都要通过vm->GetEnv重新获取*/if((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK){return -1;}if(registerNative(env) != JNI_OK){return -1;}j_vm = vm;//cloudwise_init(1);result = JNI_VERSION_1_4;return result;
}
Java层调用代码
package com.cloudwise.ndk;import android.app.ProgressDialog;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;import java.io.InputStream;public class CrashActivity extends AppCompatActivity implements View.OnClickListener {TextView txt;Button btn, btn_debug, btn_close, btn_ver_code, btn_ver, btn_logcat;// Used to load the 'native-lib' library on application startup.static {System.loadLibrary("native-lib");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_crash);// Example of a call to a native methodtxt = (TextView) findViewById(R.id.sample_text);btn = (Button)findViewById(R.id.btn);btn.setOnClickListener(this);btn_debug = (Button)findViewById(R.id.btn_debug);btn_debug.setOnClickListener(this);btn_close = (Button)findViewById(R.id.btn_close);btn_close.setOnClickListener(this);btn_ver = (Button)findViewById(R.id.btn_ver);btn_ver.setOnClickListener(this);btn_ver_code = (Button)findViewById(R.id.btn_ver_code);btn_ver_code.setOnClickListener(this);btn_logcat = (Button)findViewById(R.id.btn_string);btn_logcat.setOnClickListener(this);btn = (Button)findViewById(R.id.btn_void);btn.setOnClickListener(this);}private void btnClick(){txt.setText(NDKUtil.stringFromJNI());//Log.e("CLOUDWISE", "stringFromJNI-------");}private void btnDebug(){NDKUtil.setJNILogEnable(1);}private void btnClose(){NDKUtil.setJNILogEnable(0);}private void btnVer(){txt.setText(NDKUtil.getVersion(1));}private void btnVerCode(){txt.setText(NDKUtil.getVersionCode()+"");}private void btnString(){NDKUtil.callJavaString();}private void btnVoid(){NDKUtil.callJavaVoid();}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.btn:btnClick();break;case R.id.btn_debug:btnDebug();break;case R.id.btn_close:btnClose();break;case R.id.btn_ver:btnVer();break;case R.id.btn_ver_code:btnVerCode();break;case R.id.btn_string:btnString();break;case R.id.btn_void:btnVoid();break;}}
}
Java层布局代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><ScrollViewandroid:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/sample_text"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="10dp"android:text="" /><Buttonandroid:layout_width="match_parent"android:layout_height="50dp"android:text="JNI调用"android:id="@+id/btn"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_marginTop="10dp"/><Buttonandroid:layout_width="match_parent"android:layout_height="50dp"android:text="VersionCode"android:id="@+id/btn_ver_code"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_marginTop="10dp"/><Buttonandroid:layout_width="match_parent"android:layout_height="50dp"android:text="Version"android:id="@+id/btn_ver"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_marginTop="10dp"/><Buttonandroid:layout_width="match_parent"android:layout_height="50dp"android:text="开启JNI日志"android:id="@+id/btn_debug"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_marginTop="10dp"/><Buttonandroid:layout_width="match_parent"android:layout_height="50dp"android:text="关闭JNI日志"android:id="@+id/btn_close"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_marginTop="10dp"/><Buttonandroid:layout_width="match_parent"android:layout_height="50dp"android:text="C调Java(带返回值)"android:id="@+id/btn_string"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_marginTop="10dp"/><Buttonandroid:layout_width="match_parent"android:layout_height="50dp"android:text="C调Java(不带返回值)"android:id="@+id/btn_void"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_marginTop="10dp"/></LinearLayout></ScrollView></LinearLayout>
布局界面
运行结果
[CLOUDWISE-NDK]: stringFromJNI ---- Hello ---- from ---- C
[CLOUDWISE-NDK]: getVersionCode ----------- 10
[CLOUDWISE-NDK]: getVersion ----------- 1.2.6
CLOUDWISE: name : C-Name
[CLOUDWISE-NDK]: callStringFromJava Java To C : C From Java
CLOUDWISE: id : 10 ---- name : C-Name
[CLOUDWISE-NDK]: callVoidFromJava Java To C