• 深入理解Android(4)——理解Android中的JNI(下)


    在前面文章中简单介绍了JNI,这一篇文章来简单看一下jni.h中定义的一些常用方法,来实现通过C++调用Android中的Java代码。

    转载请说明出处:http://blog.csdn.net/dawanganban

    一、两个参数的介绍

    在前面的代码中我们会遇到两个参数,下面对这两个参数做一解释

    1、JNIEnv是指向可用JNI函数表的接口指针,C代码中JNIEnv是指向JNINativeInterface结构的指针,在C语言中JNIEnv必须作为第一个参数传入每一个JNI函数的调用者,如:

    (*env)->NewStringUTF(env, "helloworld");
    在C++中,JNIEnv是C++类的实例,JNI函数以成员函数形式存在,所以在JNI环境中,无序传入该参数,如:

    env->NetStringUTF("helloworld");
    2、jobject是一个指向java对象的引用,如果本地方法是一个静态方法,则是指向类字节码文件的class对象。

    二、JNI数据类型

    1、基本类型映射

    从上表中可以看出,Java的数据类型和C++的基本数据类型有一个映射关系,我们在使用JNI的时候可以直接使用Natvie Type来操作Native层的数据,这样就不用记忆复杂的映射关系了,从变量的名字上我们可以看到在Java的基本数据类型前面加一个字母‘j'就是对应的C++的Native类型。

    2、引用类型映射


    与基本类型不同的是,引用类型对原生的方法是不透明的(不能直接使用和修改),JNI提供了与这些引用类型密切相关的一组API ,这些API通过JNIEnv接口指针提供给原生函数。

    我们在jni.h中可以看到上面类型的定义:

    class _jobject {};
    class _jclass : public _jobject {};
    class _jthrowable : public _jobject {};
    class _jstring : public _jobject {};
    class _jarray : public _jobject {};
    class _jbooleanArray : public _jarray {};
    class _jbyteArray : public _jarray {};
    class _jcharArray : public _jarray {};
    class _jshortArray : public _jarray {};
    class _jintArray : public _jarray {};
    class _jlongArray : public _jarray {};
    class _jfloatArray : public _jarray {};
    class _jdoubleArray : public _jarray {};
    class _jobjectArray : public _jarray {};
    
    typedef _jobject *jobject;
    typedef _jclass *jclass;
    typedef _jthrowable *jthrowable;
    typedef _jstring *jstring;
    typedef _jarray *jarray;
    typedef _jbooleanArray *jbooleanArray;
    typedef _jbyteArray *jbyteArray;
    typedef _jcharArray *jcharArray;
    typedef _jshortArray *jshortArray;
    typedef _jintArray *jintArray;
    typedef _jlongArray *jlongArray;
    typedef _jfloatArray *jfloatArray;
    typedef _jdoubleArray *jdoubleArray;
    typedef _jobjectArray *jobjectArray;
    这些类的设计和Java一样,都继承自一个名为_jobject的父类。

    三、对引用类型操作的例子

    package com.example.test;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Toast;
    
    import com.example.myfirstjniproj.R;
    
    /**
     * 阳光小强 http://blog.csdn.net/dawanganban
     * @author lixiaoqiang
     *
     */
    public class MainActivity extends Activity implements OnClickListener{
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		
    		findViewById(R.id.jni_jstring_button).setOnClickListener(this);
    		findViewById(R.id.jni_javaArray_button).setOnClickListener(this);
    	}
    
    	@Override
    	public void onClick(View view) {
    		switch (view.getId()) {
    		case R.id.jni_jstring_button:
    			showToast(jniStringTest("input string"));
    			break;
    		case R.id.jni_javaArray_button:
    			int[] array = jniArrayTest();
    			showToast("arr[1]=" + array[1] + " : arr[2]=" + array[2]);
    			break;
    		default:
    			break;
    		}
    	}
    	
    	private void showToast(String content){
    		Toast.makeText(MainActivity.this, content, Toast.LENGTH_SHORT).show();
    	}
    	
    	public native String jniStringTest(String str);
    	
    	public native int[] jniArrayTest();
    	
    	
    	static{
    		System.loadLibrary("jnitest");
    	}
    }
    
    通过javah生成的头文件

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_example_test_MainActivity */
    
    #ifndef _Included_com_example_test_MainActivity
    #define _Included_com_example_test_MainActivity
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_example_test_MainActivity
     * Method:    jniStringTest
     * Signature: (Ljava/lang/String;)Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_com_example_test_MainActivity_jniStringTest
      (JNIEnv *, jobject, jstring);
    
    /*
     * Class:     com_example_test_MainActivity
     * Method:    jniArrayTest
     * Signature: ()[I
     */
    JNIEXPORT jintArray JNICALL Java_com_example_test_MainActivity_jniArrayTest
      (JNIEnv *, jobject);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    c++实现

    #include "com_example_test_MainActivity.h"
    #include <stdlib.h>
    using namespace std;
    
    JNIEXPORT jstring JNICALL Java_com_example_test_MainActivity_jniStringTest
      (JNIEnv * env, jobject obj, jstring str){
    	//将java字符串转成c++字符串
    	jboolean isCopy;
    	const char* cstr = env->GetStringUTFChars(str, &isCopy);
    	//释放原生字符串
    	env->ReleaseStringUTFChars(str, cstr);
    	//创建字符串
    	jstring jstr = env->NewStringUTF("hello world");;
    
    	return jstr;
    }
    
    JNIEXPORT jintArray JNICALL Java_com_example_test_MainActivity_jniArrayTest
      (JNIEnv * env, jobject obj){
    	//创建一个10个元素的数组
    	jintArray intarray = env->NewIntArray(10);
    	if(0 != intarray){
    		jint nativeArray[10];
    		//获取原生的数组
    		env->GetIntArrayRegion(intarray, 0, 10, nativeArray);
    		nativeArray[1] = 10;
    		nativeArray[2] = 20;
    		//设置改变
    		env->SetIntArrayRegion(intarray, 0, 10, nativeArray);
    	}
    	return intarray;
    }
    
    四、访问域和获取ID

    Java有两个域:实例域和静态域,每个实例都有自己的实例域副本,而一个类的所有实例共享一个静态域。

    JNIEXPORT jstring JNICALL Java_com_example_test_MainActivity_getInstanceField
      (JNIEnv * env, jobject obj){
    	//通过对象获得类
    	jclass clazz;
    	clazz = env->GetObjectClass(obj);
    	//获得实例域Id
    	jfieldID instanceFieldId;
    	instanceFieldId = env->GetFieldID(clazz, "instanceField", "Ljava/lang/String");
    	//获得实例域
    	jobject instanceField;
    	instanceField = env->GetObjectField(obj, instanceFieldId);
    	jstring jstr = env->NewStringUTF("获取成功");
    	return jstr;
    }
    
    
    JNIEXPORT jstring JNICALL Java_com_example_test_MainActivity_getStaticField
      (JNIEnv * env, jobject obj){
    	jclass clazz;
    	clazz = env->GetObjectClass(obj);
    	jfieldID staticFieldId;
    	staticFieldId = env->GetStaticFieldID(clazz, "staticField", "Ljava/lang/String");
    	jobject staticField;
    	staticField = env->GetStaticObjectField(clazz, staticFieldId);
    	jstring jstr = env->NewStringUTF("获取成功");
    	return jstr;
    }
    在上面代码中我们看到了“Ljava/lang/String"字符串,这个是获取ID的类型签名,Java的类型签名映射表如下:


    五、调用Java中的方法

    与上面的域一样,Java中也有两类方法,实例方法和静态方法,JNI提供了两类方法的函数

    	public native void getInstanceMethod();
    	
    	public native void getStaticMethod();
    	
    	public String instanceMethod(){
    		return "instanceMethod";
    	}
    	
    	public static String staticMethod(){
    		return "staticMethod";
    	}
    JNIEXPORT void JNICALL Java_com_example_test_MainActivity_getInstanceMethod
      (JNIEnv * env, jobject obj){
    	jclass clazz;
    	clazz = env->GetObjectClass(obj);
    	jmethodID instanceMethodId;
    	instanceMethodId = env->GetMethodID(clazz, "instanceMethod", "()Ljava/lang/String");
    	jstring instanceMethodResult;
    	instanceMethodResult = env->CallStringMethod(obj, instanceMethodId);
    }
    
    JNIEXPORT void JNICALL Java_com_example_test_MainActivity_getStaticMethod
      (JNIEnv * env, jobject obj){
    	jclass clazz;
    	clazz = env->GetObjectClass(obj);
    	jmethodID staticMethodId;
    	staticMethodId = env->GetStaticMethodID(clazz, "staticMethod", "()Ljava/lang/String");
    	jstring staticMethodResult;
    	staticMethodResult = env->CallStaticStringMethod(clazz, staticMethodId);
    }

  • 相关阅读:
    jquery选择器
    js中的闭包技术
    idea创建servlet不能创建:
    JSP页面不解析EL表达式的原因
    大对象数据LoB的应用
    缓冲流、转换流、序列化流相关流知识点
    jdk5.0新特性(注解)
    EKT相关知识(Class类对象的方法补充)
    java中调用存储过程或函数
    Java 缓冲流
  • 原文地址:https://www.cnblogs.com/lanzhi/p/6468566.html
Copyright © 2020-2023  润新知