深入理解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);
}

时间: 2024-07-30 03:09:09

深入理解Android(4)——理解Android中的JNI(下)的相关文章

Android Studio Gradle项目中添加JNI so文件

首先在Android Studio(版本1.2.2)工程的app目录下创建整个jni目录,jni目录里写Android.mk.Application.mk以及各类C/C++和汇编源文件.然后跟原来一样,用ndk_build工具去编,然后工具会自动生成libs目录,里面还有每个你在Application.mk里所指定处理器架构的so文件. 如果是引用第三方so,直接把相关的文件放在libs目录下 然后编辑app目录下的build.gradle文件,为其添加以下代码: sourceSets { ma

Android:LinearLayout布局中Layout_weight的深刻理解

首先看一下LinearLayout布局中Layout_weight属性的作用:它是用来分配属于空间的一个属性,你可以设置他的权重.很多人不知道剩余空间是个什么概念,下面我先来说说剩余空间. 看下面代码: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" an

深入理解Android(2)——理解Android中的JNI(中)

阳光小强参加了CSDN博客之星评选,如果你觉得阳光小强的博客对你有所帮助,为小强投上一票吧:http://vote.blog.csdn.net/blogstar2014/details?username=lxq_xsyu#content 在上一篇中我们了解了Android中有关JNI的使用,其实JNI是很早就有的,不是在Android创造的新技术,是SUN为我们提供的一种Java和本地代码之间相互调用的方法,这一篇我们来建立一个普通的Java工程来具体看一下Java中如何调用C/C++代码. 一

深入理解Android(5)——从MediaScanner分析Android中的JNI

前面几篇介绍了Android中的JNI和基本用法,这一篇我们通过分析Android源代码中的JNI实例,来对JNI部分做一个总结. 一.通向两个不同世界的桥梁 在前面我们说过,JNI就像一个桥梁,将Java和Native世界紧密的联系在了一起,在Android平台上如果没有Native层的支持我们的系统寸步难行,甚至Java中的虚拟机也是通过Native实现的. 二.MediaScanner类的简单介绍 MediaScannerr完成android中的多媒体文件的扫描工作.例如,mediasca

Android 深入理解Android中的自定义属性

转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/45022631: 本文出自:[张鸿洋的博客] 1.引言 对于自定义属性,大家肯定都不陌生,遵循以下几步,就可以实现: 自定义一个CustomView(extends View )类 编写values/attrs.xml,在其中编写styleable和item等标签元素 在布局文件中CustomView使用自定义的属性(注意namespace) 在CustomView的构造方法中

【Android个人理解(四)】自定义Application类的使用

1.为什么要重写Application类 如果想在整个应用中使用全局变量,在java中一般是使用静态变量,public类型:而在android中如果使用这样的全局变量就不符合Android的框架架构,但是可以使用一种更优雅的方式就是使用Application context. 那么为什么这样的全局变量就不符合Android的框架架构? 众说纷纭,我理解的是static访问是无法跨进程的.Android中的Activity,Service是可以在各自进程中运行的,用static传递参数到不同进程的

我对Android的理解

前言 写这篇文章是为了和大家描述下我对Android的理解,同时会讲述下我后面的技术规划,希望能够对大家有点参考价值.大家都想学好Android,那么怎么才能学好呢?这个真不好说,但是我可以和大家交流下我自己的心得,当然仅限应用层开发,因为我本身是侧重应用层开发的. 知识的分类 Android中有很多知识点,虽然说大部分知识点都不难,但是量很多,这就会给人一种很琐碎的感觉.在这种情况下就要对知识进行分类,我对Android知识的分类为: 1. 基本知识点 比如四大组件如何使用.如何创建Servi

[Android个人理解(六)]使用include标签的注意事项

我们在使用include标签时是为了提高部分代码的重用性,同时增加代码的层次性和条理性. 但是在我们实际使用的时候,往往就会由于include的属性和include的原理不够清楚,而产生bug. 1.首先,我们说明include标签所在布局文件a和include所包含的布局文件b的关系 必须明确a和b通过使用include连接,与其说是包含,更像是拼接.Include不是View类,自然与其内的布局对象,不是嵌套关系. 我们都清楚,我们可以将a中的include标签删去,直接复制粘贴b的代码,这

深入理解Android(3)__注册JNI函数(详细)

提示: 我们在深入理解Android(2)中简单介绍了JNI函数的注册方式,主要分为:静态注册和动态注册. 思来想去,还是觉得应该对两种注册方式的具体过程,给各位读者做个详细介绍,这样也可以自己做一遍Review,加深以下印象. 下面,我们步入正题,分析以下两种注册方式. -----------------------------------------------------------------------------------------------------------------

语音识别,语义理解一站式解决(android平台&olami sdk)

用olami sdk语音识别语义理解做在线听书 olamisdk实现了把录音或者文字转化为用户可以理解的json字符串,本文使用olami sdk做了一个在线听书的demo,用的是喜马拉雅的在线听书sdk.基于eclipse开发环境,libs目录下jar和so文件如下: olami-android-sdk.jar//olami sdk 的jar afinal_0.5.1_bin.jar litepal.jar gson-2.2.4.jar okhttp-2.4.0.jar okhttp-urlc