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

android jni的使用

2016-02-03 16:11 337 查看
android中的jni,可以方便java与本地native的代码互相访问,上一篇文章是java访问本地方法的介绍,上一篇的做法是老版本jdk1.4之前的格式,新版本jdk1.6之后的格式是使用映射,本篇就是使用映射来实现java与native互相访问的一个实例,就是自己的一个笔记。

抛开android环境,单纯java跟native的交互:http://blog.csdn.net/lin20044140410/article/details/50605663

一,首先,还是通过eclipse新建一个app,关键代码如下,

一个MainActivity.java :

package com.nativedemo.hellonativedemo;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.util.Log;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

NewHelloJni mNewHelloJni = new NewHelloJni();
mNewHelloJni.displayNativeStr();
mNewHelloJni.printJaveStrFromNative();

int sum = mNewHelloJni.getAddSumFromNative(10000,500);
String mStr = mNewHelloJni.getStrFromNative();
Log.d("Hellojni","jni native ,getAddSumFromNative= "+sum+",mStr="+mStr);
}

static {
Log.d("hellonative","Loading JNI jni Library");

//这里加载库时,不需要指定后缀,linux下的库是.so,

System.loadLibrary("newhellojni");
}

。。。。。。

}


一个 NewHelloJni.java 这里声明了本地方法,和native中想要访问的java中的方法

package com.nativedemo.hellonativedemo;
import android.util.Log;

public class NewHelloJni{
String fromNativeStr ="It is from jave!";
int mInt =20000;

public NewHelloJni(){
fromNativeStr= "init...";
}

void setJaveStrFromNative(String str){
fromNativeStr = str;
}
void setJaveintFromNative(int mI){
mInt = mI;
}

void printJaveStrFromNative(){
Log.d("hellojni","from native str:"+fromNativeStr+",mInt="+mInt+",but show in jave!");
}

native void displayNativeStr(); //需要本地实现的方法,前面都有个native关键字
native String getStrFromNative();
native int getAddSumFromNative(int num1,int num2);
}


二,在app的根目录,新建一个文件夹 newjni,

里面放的文件有:Android.mk,com_nativedemo_hellonativedemo_NewHelloJni.cpp(本地方法的实现类),内容如下:

Android.mk的内容:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_LDLIBS := -llog

LOCAL_SRC_FILES := \
com_nativedemo_hellona
4000
tivedemo_NewHelloJni.cpp

LOCAL_C_INCLUDES := \
$(JNI_H_INCLUDE)

LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
libnativehelper \
libutils \
libcutils \
liblog

#LOCAL_PRELINK_MODULE := false

LOCAL_MULTILIB := 32
LOCAL_MODULE:= libnewhellojni
LOCAL_MODULE_TAGS := optional

include $(BUILD_SHARED_LIBRARY)


com_nativedemo_hellonativedemo_NewHelloJni.cpp的内容:

#include <jni.h>
#include "JNIHelp.h"
#include "utils/Log.h"
#include <string.h>
#include <pthread.h>
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"

namespace android{
jobject objGlobal = NULL;
JNIEnv* envGlobal =NULL;
jclass jniClass =NULL;

jobject instanceObj =NULL;
jmethodID constuctorId =0;
jfieldID fidInt =0;
jfieldID fidStr =0;
jmethodID jMIdNoArgs =0;
jmethodID jMIdIArgs =0;

static void displayNativeStr(JNIEnv* env, jobject obj){
env = AndroidRuntime::getJNIEnv();
jstring jStrJave=NULL;
const char* cStrJave = NULL;

const char* str= "set native string in native";
jstring jStr = env->NewStringUTF(str);//实例化一个字符串

jniClass = env->FindClass("com/nativedemo/hellonativedemo/NewHelloJni");
jniClass = env->GetObjectClass(obj);
constuctorId = env->GetMethodID(jniClass,"<init>","()V"); //获取构造函数id
if(instanceObj == NULL){
instanceObj = env->NewObject(jniClass,constuctorId);
}
if(objGlobal == NULL){
//objGlobal = env->NewGlobalRef(instanceObj); //实例化一个全局的引用
objGlobal = env->NewGlobalRef(obj);
}

fidInt = env->GetFieldID(jniClass,"mInt","I");   //访问java中的属性
int javeInt = env->GetIntField(objGlobal,fidInt);
env->SetIntField(objGlobal,fidInt,99990);

fidStr = env->GetFieldID(jniClass,"fromNativeStr","Ljava/lang/String;");//访问java中的字符串属性
//jStrJave = (jstring)env->GetObjectField(objGlobal,fidStr);
//cStrJave = env->GetStringUTFChars(jStrJave,NULL);
//env->ReleaseStringUTFChars(jStrJave,cStrJave);
env->SetObjectField(objGlobal,fidStr,jStr);

jMIdNoArgs = env->GetMethodID(jniClass, "printJaveStrFromNative", "()V");//访问java中的方法
if(jMIdNoArgs !=0){
env->CallVoidMethod(objGlobal, jMIdNoArgs);
}

jMIdIArgs = env->GetMethodID(jniClass, "setJaveintFromNative", "(I)V");
ALOGD("native jni code,constuctorId=%d,fidInt=%d,javeInt=%s,,fidStr=%d,jStrJave=%s,jMIdJave=%d,jMIdIArgs=%d \n",
constuctorId,fidInt,javeInt,fidStr,jStrJave,jMIdNoArgs,jMIdIArgs);
if(jMIdIArgs!=0){
env->CallVoidMethod(objGlobal, jMIdIArgs,350000);
}
}

//本地代码的实现,无参数的函数
static jstring getStrFromNative(JNIEnv* env, jobject obj){
const char* str= "from jni native string";
jstring jStr = env->NewStringUTF(str);
return jStr;
}
//本地代码的实现,有参数的函数
static int getAddSumFromNative(JNIEnv* env, jobject obj,jint num1,jint num2){
return num1+num2;
}
//java中的方法,跟本地方法的映射,通过这个映射,native中的方法名就不用完全依照jni命名规范(Java_+包名+类名+接口名)写的那么长了,可以自主定义。
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"displayNativeStr","()V",(void *) displayNativeStr},
{"getStrFromNative","()Ljava/lang/String;",(void *) getStrFromNative},
{"getAddSumFromNative","(II)I",(void *) getAddSumFromNative}
};

int register_com_nativedemo_hellonativedemo_NewHelloJni(JNIEnv* env)
{

//这个注册是把jni调用接口,设置到包"com/nativedemo/hellonativedemo/NewHelloJni"中,这样java中的应用程序就可以调用。

return jniRegisterNativeMethods(env, "com/nativedemo/hellonativedemo/NewHelloJni",
sMethods, NELEM(sMethods));
}
}//end namespace android

/*
* JNI Initialization
*/
jint JNI_OnLoad(JavaVM *jvm, void *reserved)
{
JNIEnv *e;
int status;

ALOGV("Hello jni native : loading JNI\n");

// Check JNI version
if (jvm->GetEnv((void **)&e, JNI_VERSION_1_6)) {
ALOGE("JNI version mismatch error");
return JNI_ERR;
}

if ((status = android::register_com_nativedemo_hellonativedemo_NewHelloJni(e)) < 0) {
ALOGE("jni hello native registration failure, status: %d", status);
return JNI_ERR;
}

return JNI_VERSION_1_6;
}

从native访问java,提供了两种语法,分别是C语法和c++语法:

c语法: (*env)->CallVoidMethod(env,...);

c++语法:env->CallVoidMethod(jobject,..)

因为c语言不支持对象的概念,所以在c语法中把env作为第一个参数传入,类似于C++隐式参数this指针。

另外JNIEnv这个指针是线程私有的,每个线程都有一个副本。

这个函数中的JNI_OnLoad,在jvm加载动态库时会被调用,其中一个作用是通过返回值告诉虚拟机使用的jni版本,因为不同的版本提供的功能函数不一样,如果是默认返回了老的版本jni1.1,将无法使用新版本的功能。

然后这个方法里调用了注册本地方法的函数,jniRegisterNativeMethods(env, "com/nativedemo/hellonativedemo/NewHelloJni",sMethods, NELEM(sMethods));第一个参数是包含有需要本地实现方法的jave类,第二个参数是一个映射数组,,第三个参数是有多少个这样的映射,在JNINativeMethod sMethods[]这个数组里面,第一个元素是java中的方法,第二个是Signature签名,第三个是本地实现方法。

其中第二个参数描述了函数参数和返回值,如:

"(II)V" 括号中字符表示参数,括号外的字符表示返回值,这些字符表示方法:void fun(int ,int)

每个字符的对应关系:

字符Java类型C类型

V void void

Z jbooleanboolean

I jint     int

J jlong long

D jdouble double

F jfloat float

B jbyte byte

C jchar char

S
 jshort short

数组以"["开始,后面跟具体类型:

[I jintArrayint[]

上面是基本数据类型的表示,如果是类类型,以"L"开头,以";"结尾,中间部分是用"/"隔开的包和类名,其在C环境下对应的表示是jobject,例外的是String类,其在C中表示为jstring。

如:(Ljava/lang/String;J)Z,表示的方法是:boolean fun(String , long)

mainactivity.java中,有使用的代码,比较容易看懂,不做解释了。

最后说一下,如何把native层的异常抛到java层:

jclass exceptionClass = env->FindClass("java/lang/RuntimeException");
env->ThrowNew(exceptionClass, "error msg!");
异常的类型可以通过FindClass的参数指定,ThrowNew的第二个参数是异常的消息。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: