您的位置:首页 > 移动开发 > Android开发

Android之jni调用java函数总结

2017-05-14 21:51 531 查看

1、先看之前jni的如何实现动态注册

先看我之间的例子 http://blog.csdn.net/u011068702/article/details/71375920 
 Android之JNI动态注册native方法和JNI数据简单使用
因为这里演示的jni调用java函数是基于这个例子改的,然后还有如何在jni里面加上日志可以看这篇博客
http://blog.csdn.net/u011068702/article/details/71852904 


 Android之jni日志如何输出




2、贴出相关代码

      1)、java文加如下、

package com.example.chenyu.test;

public class JniClient {
public	JniClient() {
}
public native String getStr();
public native int addInt(int a, int b);

/**
* C调用java空方法
*/
public void nullMethod() {
System.out.println("I am chenyu, from java");
}

/**
* C调用java中的带两个int参数的方法
*
* @param x
* @param y
* @return
*/
public int add(int x, int y) {
int result = x + y;
System.out.println("result in java " + result);
return result;
}

/**
* C调用java中参数为String的方法
*
* @param s
*/
public String printString(String s) {
String result = "I am chenyu, from java " + s;
System.out.println(result);
return result;
}

// 本地方法
public native void callMethod1();
public native void callMethod2();
public native void callMethod3();
}


这里写了3个jni方法,然后我们实现这3个jni方法调用上面的3个java方法

jni里面的代码如下

//
// Created by chenyu on 5/7/17.
//

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <jni.h>
#include <assert.h>
#include <log_help.h>

//定义的时候要记得加上双引号 不是#define TAG JniClient
#define TAG "JniClient"

#define JNIREG_CLASS "com/example/chenyu/test/JniClient"//指定要注册的类

jstring get_str(JNIEnv* env, jobject thiz) {
LOGD(TAG, "hello chenyu");
char * str = "this is first LOGD";
LOGD(TAG, "hello chenyu %s", str);
return (*env)->NewStringUTF(env, "I am chenyu, 动态注册JNI");
}

jint add_int(JNIEnv* env, jobject jobj, jint num1, jint num2) {

LOGD(TAG, "nums + num2 is %d", num1 + num2);
return num1 + num2;
}

void callMethod1(JNIEnv* env, jobject obj) {
LOGD(TAG, "this is jni call1");
//在C语言中调用Java的空方法
//1.找到java代码native方法所在的字节码文件
//jclass (*FindClass)(JNIEnv*, const char*);
jclass clazz = (*env)->FindClass(env, "com/example/chenyu/test/JniClient");
if(clazz == 0) {
LOGD(TAG, "find class error");
return;
}
LOGD(TAG, "find class success");
//2.找到class里面对应的方法
// jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jmethodID method1 = (*env)->GetMethodID(env, clazz, "nullMethod", "()V");
if(method1 == 0) {
LOGD(TAG, "find callMethod1 error");
return;
}
LOGD(TAG, "find callMethod1 success");
//3.调用方法
//void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
(*env)->CallVoidMethod(env, obj, method1);
LOGD(TAG, "callMethod1 called success");
}

void callMethod2(JNIEnv* env, jobject obj) {
LOGD(TAG, "this is jni call2");
jclass clazz = (*env)->FindClass(env, "com/example/chenyu/test/JniClient");
if(clazz == 0) {
LOGD(TAG, "find class error");
return;
}
LOGD(TAG, "find class success");
//2.找到class里面对应的方法
// jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jmethodID method2 = (*env)->GetMethodID(env, clazz, "add", "(II)I");
if(method2 == 0) {
LOGD(TAG, "find callMethod2 error");
return;
}
LOGD(TAG, "find callMethod2 success");
//3.调用方法
//void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
int result = (*env)->CallIntMethod(env, obj, method2, 3, 5);
LOGD(TAG, "callMethod2 called success");
LOGD(TAG, "in java 3 + 5 is %d", result);
}

void callMethod3(JNIEnv* env, jobject obj) {
LOGD(TAG, "this is jni call3");
jclass clazz = (*env)->FindClass(env, "com/example/chenyu/test/JniClient");
if (clazz == 0) {
LOGD(TAG, "find class error");
return;
}
LOGD(TAG, "find class success");
//2.找到class里面对应的方法
// jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jmethodID method3 = (*env)->GetMethodID(env, clazz, "printString", "(Ljava/lang/String;)Ljava/lang/String;");
if(method3 == 0) {
LOGD(TAG, "find callMethod3 error");
return;
}

LOGD(TAG, "find callMethod3 success");
//3.调用方法
//void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
jstring result = (*env)->CallObjectMethod(env, obj, method3, (*env)->NewStringUTF(env, "hello jni insert data"));
//(*env)->CallVoidMethod(env, obj, method3, "chenyu"); //如果用这个会出现 JNI ERROR (app bug): accessed stale global reference 0xb39533f2 (index 19708 in a table of size 248)
const char* result1 = (*env)->GetStringUTFChars(env, result, NULL);//将返回的java字符串转换为c字符串
LOGD(TAG, "callMethod3 called success result is %s", result1);
}

/**
* 方法对应表
*/
static JNINativeMethod gMethods[] = {
{"getStr", "()Ljava/lang/String;", (void*)get_str},
{"addInt", "(II)I", (void*)add_int},
{"callMethod1", "()V", (void*)callMethod1},
{"callMethod2", "()V", (void*)callMethod2},
{"callMethod3", "()V", (void*)callMethod3},
};

/*
* 为某一个类注册本地方法
*/
static int registerNativeMethods(JNIEnv* env
, const char* className
, JNINativeMethod* gMethods, int numMethods) {
jclass clazz;
clazz = (*env)->FindClass(env, className);
if (clazz == NULL) {
return JNI_FALSE;
}
if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
return JNI_FALSE;
}

return JNI_TRUE;
}

/*
* 为所有类注册本地方法
*/
static int registerNatives(JNIEnv* env) {
return registerNativeMethods(env, JNIREG_CLASS, gMethods,
sizeof(gMethods) / sizeof(gMethods[0]));
}

/*
* System.loadLibrary("lib")时调用
* 如果成功返回JNI版本, 失败返回-1
*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
LOGD(TAG, "this is jni start and will exec JNI_OnLoad methos");
JNIEnv* env = NULL;
jint result = -1;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
assert(env != NULL);
if (!registerNatives(env)) {//注册
return -1;
}
//成功
result = JNI_VERSION_1_4;
return result;
}


步骤为:

      1)、获取jclass

      2)、获取jmethodID

      3)、调用本地的Call*****Method(); 如果不清楚函数,可以查看这里 http://www.ceeger.com/Script/AndroidJNI/AndroidJNI.CallStaticIntMethod.html
      如果函数返回String类型的话我们要调用

jstring result = (*env)->CallObjectMethod(env, obj, method3, (*env)->NewStringUTF(env, "hello jni insert data"));
      如果是jstring类型转成char *类型,我们需要用这个函数

const char* result1 = (*env)->GetStringUTFChars(env, result, NULL)


还有就是char *转成jstring可以用看下面的函数

/**
* 转换c中的字符串为java.lang.String,这个方法是从网上找到的,感谢原作者<a href="http://home.cnblogs.com/u/liangwind/">天末凉风</a>

*/
jstring stoJstring(JNIEnv* env, const char* pat)
{
jclass strClass = (*env)->FindClass(env,"Ljava/lang/String;");
jmethodID ctorID = (*env)->GetMethodID(env,strClass, "<init>", "([BLjava/lang/String;)V");
jbyteArray bytes = (*env)->NewByteArray(env,strlen(pat));
(*env)->SetByteArrayRegion(env,bytes, 0, strlen(pat), (jbyte*)pat);
jstring encoding = (*env)->NewStringUTF(env,"utf-8");
return (jstring)(*env)->NewObject(env,strClass, ctorID, bytes, encoding);
}

在C++里面,把jstring转化成string函数如下

static std::string jstring2str(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("UTF-8");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr,mid,strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr,JNI_FALSE);
if(alen > 0)
{
rtn = (char*)malloc(alen+1);
memcpy(rtn,ba,alen);
rtn[alen]=0;
}
env->ReleaseByteArrayElements(barr,ba,0);
std::string stemp(rtn);
free(rtn);
return stemp;
}

 
至于参数的详解,返回值,数据类型,我前面几篇博客已经讲解,这里就不详细说了

   

3、运行结果



4、总结

jni调用java函数简单步骤,在jni函数里面实现以下几个步骤

1)、获取jclass

2)、获取jmethodID

3)、调用本地的Call*****Method(); 如果不清楚函数

如果函数返回String类型的话我们要调用

jstring result = (*env)->CallObjectMethod(env, obj, method3, (*env)->NewStringUTF(env, "hello jni insert data"));
 如果是jstring类型转成char *类型,我们需要用这个函数
const char* result1 = (*env)->GetStringUTFChars(env, result, NULL)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐