现在的位置: 首页 > 综合 > 正文

Android NDK用法

2018年03月31日 ⁄ 综合 ⁄ 共 4482字 ⁄ 字号 评论关闭

一、前言:

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

 

抱歉!评论已关闭.