理解Android中的JNI

什么是JNI

JNI是Java Native Interface的缩写(Java本地调用),Java程序中的函数可以调用Native语言写的函数(一般指的是C/C++编写的函数),Native语言写的函数可以调用Java层的函数。

JNI结构示意图

为什么要有JNI

Java语言的跨平台是因为在不同平台上可以运行Java虚拟机,而虚拟机是跑在具体平台上的,而本质上Java是通过JNI技术实现的跨平台,很多基层的模块在Java语言诞生之前已经有了比较优秀的实现,为了避免重复造轮子所以我们要使用JNI技术来使用已有的模块。

Mac OS上的环境搭建

在这里说明一下Max OS上的所需环境搭建,Windows和Linux的请搜索相关资料。

  1. 安装JDK(此处省略)。
  2. 安装ADT(Android Develop Tools),包括SDK和ADT插件,下载地址:http://pan.baidu.com/s/1o6OBIHG
  3. 安装Xcode可以去苹果商店下载安装(免费)。
  4. 安装Apache ANT(下载地址:http://ant.apache.org/bindownload.cgi)详细安装过程可以参考:http://blog.csdn.net/song_hui_xiang/article/details/14315529
  5. 安装GNU Make(默认已经安装,所以不用安装)可以使用 make -version命令验证是否安装。
  6. 安装NDK(下载地址:http://pan.baidu.com/s/1i3l5L8T),解压后在用户根目录下新建文件.bash_profile然后添加如下两行(配置环境变量,可以暂时不配置)。
1
2
export ANDROID_NDK_HOME=/Users/lixiaoqiang/Documents/install_tools/ndk/android-ndk-r10c  
export PATH=${PATH}:${ANDROID_NDK_HOME}

注意:后面的地址是你解压后的目录

关于上面部分开发工具简要介绍:

  1. Apache Ant,是一个将软件编译、测试、部署等步骤联系在一起加以自动化的一个工具,大多用于Java环境中的软件开发。
  2. NDK是Android原生开发工具包,可以支持C/C++等原生编程语言开发Android应用,它提供头文件、库和交叉编译工具链。

第一个示例程序

转载请说明出处:https://lxqxsyu.gitlab.io

  1. 为eclipse指定NDK路径

为eclipse指定NDK路径

  1. 导入Android NDK中的示例代码(导入hello-jni工程),做过Android开发的朋友应该很熟悉,这里就不啰嗦了。

导入hello-jni工程

  1. 向项目中添加原生支持
    项目——>右击——>Android Tools——>Add Native Support
    该项目其实已经包含了一个原生项目,所以这一步可以跳过,我们直接Finish继续。
  2. 插上手机(模拟器太慢了,建议使用真机)运行项目。在C/C++界面视图我们可以看到如下信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
**** Build of configuration Default for project HelloJni ****  

/Users/lixiaoqiang/Documents/install_tools/ndk/android-ndk-r10c/ndk-build all
Android NDK: WARNING: APP_PLATFORM android-19 is larger than android:minSdkVersion 3 in ./AndroidManifest.xml
[arm64-v8a] Gdbserver : [aarch64-linux-android-4.9] libs/arm64-v8a/gdbserver
[arm64-v8a] Gdbsetup : libs/arm64-v8a/gdb.setup
[x86_64] Gdbserver : [x86_64-4.9] libs/x86_64/gdbserver
[x86_64] Gdbsetup : libs/x86_64/gdb.setup
[mips64] Gdbserver : [mips64el-linux-android-4.9] libs/mips64/gdbserver
[mips64] Gdbsetup : libs/mips64/gdb.setup
[armeabi-v7a] Gdbserver : [arm-linux-androideabi-4.6] libs/armeabi-v7a/gdbserver
[armeabi-v7a] Gdbsetup : libs/armeabi-v7a/gdb.setup
[armeabi] Gdbserver : [arm-linux-androideabi-4.6] libs/armeabi/gdbserver
[armeabi] Gdbsetup : libs/armeabi/gdb.setup
[x86] Gdbserver : [x86-4.6] libs/x86/gdbserver
[x86] Gdbsetup : libs/x86/gdb.setup
[mips] Gdbserver : [mipsel-linux-android-4.6] libs/mips/gdbserver
[mips] Gdbsetup : libs/mips/gdb.setup
[arm64-v8a] Compile : hello-jni <= hello-jni.c
[arm64-v8a] SharedLibrary : libhello-jni.so
[arm64-v8a] Install : libhello-jni.so => libs/arm64-v8a/libhello-jni.so
[x86_64] Compile : hello-jni <= hello-jni.c
[x86_64] SharedLibrary : libhello-jni.so
[x86_64] Install : libhello-jni.so => libs/x86_64/libhello-jni.so
[mips64] Compile : hello-jni <= hello-jni.c
[mips64] SharedLibrary : libhello-jni.so
[mips64] Install : libhello-jni.so => libs/mips64/libhello-jni.so
[armeabi-v7a] Compile thumb : hello-jni <= hello-jni.c
[armeabi-v7a] SharedLibrary : libhello-jni.so
[armeabi-v7a] Install : libhello-jni.so => libs/armeabi-v7a/libhello-jni.so
[armeabi] Compile thumb : hello-jni <= hello-jni.c
[armeabi] SharedLibrary : libhello-jni.so
[armeabi] Install : libhello-jni.so => libs/armeabi/libhello-jni.so
[x86] Compile : hello-jni <= hello-jni.c
[x86] SharedLibrary : libhello-jni.so
[x86] Install : libhello-jni.so => libs/x86/libhello-jni.so
[mips] Compile : hello-jni <= hello-jni.c
[mips] SharedLibrary : libhello-jni.so
[mips] Install : libhello-jni.so => libs/mips/libhello-jni.so

**** Build Finished ****

这个过程其实就是在构建原生组件并和Java应用程序打包的过程。此时在我们手机上就可以看到一行文字

模拟器显示结果

项目结构及主要目录介绍

项目结构

  1. jni目录:包含原生组件的源代码及描述原生组件构建方法的Make文件(Android.mk),该目录作为NDK构建项目时的构建目录。
  2. libs目录:包含指定目标平台的独立子目录,在打包时该目录被包含在apk文件中。
  3. obj目录:这是一个中间目录,编译源码后产生的目标文件都保存在该目录下,我们最好不用访问该目录。

实例工程解析

Android.mk的内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Copyright (C) 2009 The Android Open Source Project  
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

有关Makefile的知识请参考我的另一篇博文:http://blog.csdn.net/dawanganban/article/details/38750151

第一行:Android.mk文档必须以LOCAL_PATH变量的定义开头,my-dir是一个系统的宏定义,来定义源文件的目录位置。

第二行:Android构建系统将CLEAR_VARS变量设置为clear-vars.mk片段的位置,更多片段的makefile文件请看ndk\build\core目录,如下:

makefile文件

作用是清除除了LOCAL_PATH以外的LOCAL_变量,这样可以避免冲突。

第三行:每一个原生组件被称为一个模块,LOCAL_MODULE变量用来给这些模块设定一个唯一的名称。

第四行:LOCAL_SRC_FILES变量定义用来建立和组装这个模块的源文件列表,用空格隔开。

第五行:指明了build-shared-library.mk文件的保存位置,该片段包含了将源文件构建成共享库的必要过程。

下面我们来看看HelloJni.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/* 
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.hellojni;

import android.app.Activity;
import android.widget.TextView;
import android.os.Bundle;


public class HelloJni extends Activity
{
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

/* Create a TextView and set its content.
* the text is retrieved by calling a native
* function.
*/
TextView tv = new TextView(this);
tv.setText( stringFromJNI() );
setContentView(tv);
}

/* A native method that is implemented by the
* 'hello-jni' native library, which is packaged
* with this application.
*/
public native String stringFromJNI();

/* This is another native method declaration that is *not*
* implemented by 'hello-jni'. This is simply to show that
* you can declare as many native methods in your Java code
* as you want, their implementation is searched in the
* currently loaded native libraries only the first time
* you call them.
*
* Trying to call this function will result in a
* java.lang.UnsatisfiedLinkError exception !
*/
public native String unimplementedStringFromJNI();

/* this is used to load the 'hello-jni' library on application
* startup. The library has already been unpacked into
* /data/data/com.example.hellojni/lib/libhello-jni.so at
* installation time by the package manager.
*/
static {
System.loadLibrary("hello-jni");
}
}

从上面可以看到调用了原生的stringFromJNI()方法,使用关键字native来通知Java编译器,这个是用另一种语言实现的,再通过加装共享库(static语句块)hello-jni来告诉虚拟机原生方法的具体实现。java.lang.System类提供了两个静态方法,load和loadLibrary用来运行时加载共享库。下面我们来看看具体的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <string.h>  
#include <jni.h>

/* This is a trivial JNI example where we use a native method
* to return a new VM String. See the corresponding Java source
* file located at:
*
* apps/samples/hello-jni/project/src/com/example/hellojni/HelloJni.java
*/
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz )
{
#if defined(__arm__)
#if defined(__ARM_ARCH_7A__)
#if defined(__ARM_NEON__)
#if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a/NEON (hard-float)"
#else
#define ABI "armeabi-v7a/NEON"
#endif
#else
#if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a (hard-float)"
#else
#define ABI "armeabi-v7a"
#endif
#endif
#else
#define ABI "armeabi"
#endif
#elif defined(__i386__)
#define ABI "x86"
#elif defined(__x86_64__)
#define ABI "x86_64"
#elif defined(__mips64) /* mips64el-* toolchain defines __mips__ too */
#define ABI "mips64"
#elif defined(__mips__)
#define ABI "mips"
#elif defined(__aarch64__)
#define ABI "arm64-v8a"
#else
#define ABI "unknown"
#endif

return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI " ABI ".");
}

第一个参数JNIEnv是指向可用JNI函数表的接口指针,第二个参数jobject是HelloJni类实例的java对象。最后一句代码是用c字符串创建UTF-8的Java字符串。


上面我们了解了Android中有关JNI的使用,其实JNI是很早就有的,不是在Android创造的新技术,是SUN为我们提供的一种Java和本地代码之间相互调用的方法,这一篇我们来建立一个普通的Java工程来具体看一下Java中如何调用C/C++代码。

新建一个普通Java工程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.csdn.test;  

/**
* 水寒 http://blog.csdn.net/dawanganban
* @author lixiaoqiang
*
*/
public class TestJNI {

public native void saveHello();

public static void main(String[] args) {
TestJNI jni = new TestJNI();
jni.saveHello();
}
}

在TestJNI中我们声明了一个native方法。

生成C/C++头文件

SUN的JDK中已经为我们提供了一个生成对应C/C++头文件的工具(javah)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
lixiaoqiangdeMac-mini:bin lixiaoqiang$ whereis javah  
/usr/bin/javah
lixiaoqiangdeMac-mini:bin lixiaoqiang$
```

说明:我这里用的是max os系统,如果你是windows系统,在jdk目录下寻找。

找到我们工程文件所在的目录,执行javah命令如下:

```shell
lixiaoqiangdeMac-mini:bin lixiaoqiang$ pwd
/Users/lixiaoqiang/Documents/AndroidWorkspace/TestJNI/bin
lixiaoqiangdeMac-mini:bin lixiaoqiang$ javah com.csdn.test.TestJNI
lixiaoqiangdeMac-mini:bin lixiaoqiang$ ls
com com_csdn_test_TestJNI.h
lixiaoqiangdeMac-mini:bin lixiaoqiang$

可以看到为我们生成了一个com_csdn_test_TestJNI.h的头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* DO NOT EDIT THIS FILE - it is machine generated */  
#include <jni.h>
/* Header for class com_csdn_test_TestJNI */

#ifndef _Included_com_csdn_test_TestJNI
#define _Included_com_csdn_test_TestJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_csdn_test_TestJNI
* Method: saveHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_csdn_test_TestJNI_saveHello
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

生成动态库文件

下面我们打开C/C++编辑器(我这里使用的时Xcode,在Windows平台上的朋友可以使用VisualStudio)

Xcode

main.cpp中我们输出一行文字,如下:

1
2
3
4
5
6
7
#include "com_csdn_test_TestJNI.h"  
#include <iostream>
using namespace std;

JNIEXPORT void JNICALL Java_com_csdn_test_TestJNI_saveHello(JNIEnv * evn, jobject obj){
cout << "hello world" << endl;
}

在编译的过程中会发现找不到jni.h和jni_md.h文件,这两个文件可以在JDK的include目录下找到,拷贝到工程中,下面将上面的工程导出动态链接库。

在导出链接库(编译)的时候在MacOS下和在Linux, Windows有所不同, 不是编译成.so或者dll, 而是MacOS自己的jnilib. 并且jni.h的目录也比较特殊, 是/System/Library/Frameworks/JavaVM.framework/Headers/, 这个需要稍微注意一下, 具体的命令如下:

1
2
3
4
5
lixiaoqiangdeMac-mini:TestJNI lixiaoqiang$ g++ -dynamiclib -o libhelloworld.jnilib main.cpp -framework JavaVM -I/System/Library/Frameworks/JavaVM.framework/Headers  
lixiaoqiangdeMac-mini:TestJNI lixiaoqiang$ ls
com_csdn_test_TestJNI.h jni_md.h main.cpp
jni.h libhelloworld.jnilib
lixiaoqiangdeMac-mini:TestJNI lixiaoqiang$

可以看到在该目录生成了一个libhelloworld.jnilib文件,我们将该文件拷贝到我们Java工程的bin目录下(windows下是classes目录)
向Java工程中添加加装动态库的代码(下面的static语句块中的System.loadLibrary方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.csdn.test;  

/**
* 阳光小强 http://blog.csdn.net/dawanganban
* @author lixiaoqiang
*
*/
public class TestJNI {

public native void saveHello();

public static void main(String[] args) {
TestJNI jni = new TestJNI();
jni.saveHello();
}

static{
System.loadLibrary("helloworld");
}
}

最后将该库文件添加到Java程序可以访问的环境变量中就可以运行输出“helloworld”了。


Eclipse集成javah和NDK-Builder

上面我们使用了javah工具来生成了native java文件所对应的C++头文件,但是这样生成比较麻烦,我们这一篇来介绍如何在eclipse中集成javah和NDK-Builder。

eclipse集成javah

eclipse集成javah

选择External Tools Configurations…

选择External Tools Configurations

上面是我配置的,解释如下:

Location:这里配置的是javah所在的路径。
Working Directory:配置项目所在路径
Arguments:就是所需参数了。

注意:在linux或mac os上配置如下:

1
2
Working Directory——${project_loc}/bin/classes
Arguments: -d${project_loc}/jni ${java_type_name}

eclipse配置NDK交叉编译

eclipse配置NDK交叉编译

添加到定制工具栏

添加到定制工具栏

选择Organize Favorites…

选择Organize Favorites

选择javah和NDK_Builder然后点击OK,我们可以方便的使用这两个工具了

运行菜单


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

两个参数的介绍

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

JNIEnv

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

1
(*env)->NewStringUTF(env, "helloworld");

在C++中,JNIEnv是C++类的实例,JNI函数以成员函数形式存在,所以在JNI环境中,无序传入该参数,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
env->NetStringUTF("helloworld");
```

**jobject**

jobject是一个指向java对象的引用,如果本地方法是一个静态方法,则是指向类字节码文件的class对象。

## JNI数据类型

**基本类型映射**

<img src="androidjni/image14.png" width=500 />

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

**引用类型映射**

<img src="androidjni/image15.png" width=500 />

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

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

```c++
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的父类。

对引用类型操作的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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生成的头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* 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++实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#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有两个域:实例域和静态域,每个实例都有自己的实例域副本,而一个类的所有实例共享一个静态域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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中的方法

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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);
}

评论

Ajax Android AndroidStudio Animation Anroid Studio AppBarLayout Babel Banner Buffer Bulma ByteBuffer C++ C11 C89 C99 CDN CMYK COM1 COM2 CSS Camera Raw, 直方图 Chrome Class ContentProvider CoordinatorLayout C语言 DML DOM Dagger Dagger2 Darktable Demo Document DownloadManage ES2015 ESLint Element Error Exception Extensions File FileProvider Flow Fresco GCC Git GitHub GitLab Gradle Groovy HTML5 Handler HandlerThread Hexo Hybrid I/O IDEA IO ImageMagick IntelliJ Intellij Interpolator JCenter JNI JS Java JavaScript JsBridge Kotlin Lab Lambda Lifecycle Lint Linux Looper MQTT MVC MVP Maven MessageQueue Modbus Momentum MySQL NDK NIO NexT Next Nodejs ObjectAnimator Oracle VM Permission PhotoShop Physics Python RGB RS-232 RTU Remote-SSH Retrofit Runnable RxAndroid RxJava SE0 SSH Spring SpringBoot Statubar Style Task Theme Thread Tkinter UI UIKit UML VM virtualBox VS Code VUE ValueAnimator ViewPropertyAnimator Vue Vue.js Web Web前端 Workbench api apk bookmark by关键字 cli compileOnly computed css c语言 databases demo hexo hotfix html iOS icarus implementation init jQuery javascript launchModel logo merge methods mvp offset photos pug query rxjava2 scss servlet shell svg tkinter tomcat transition unicode utf-8 vector virtual box vscode watch webpack 七牛 下载 中介者模式 串口 临潼石榴 主题 书签 事件 享元模式 仓库 代理模式 位运算 依赖注入 修改,tables 光和色 内存 内核 内部分享 函数 函数式编程 分支 分析 创建 删除 动画 单例模式 压缩图片 发布 可空性 合并 同向性 后期 启动模式 命令 命令模式 响应式 响应式编程 图层 图床 图片压缩 图片处理 图片轮播 地球 域名 基础 增加 备忘录模式 外观模式 多线程 大爆炸 天气APP 太白山 头文件 奇点 字符串 字符集 存储引擎 宇宙 宏定义 实践 属性 属性动画 岐山擀面皮 岐山肉臊子 岐山香醋 工具 工厂模式 年终总结 开发技巧 异常 弱引用 恒星 打包 技巧 指令 指针 插件 插值 摄影 操作系统 攻略 故事 数据库 数据类型 数组 文件 新功能 旅行 旋转木马 时序图 时空 时间简史 曲线 杂谈 权限 枚举 架构 查询 标准库 标签选择器 样式 核心 框架 案例 桥接模式 检测工具 模块化 模板 模板引擎 模板方法模式 油泼辣子 泛型 洛川苹果 浅色状态栏 渲染 源码 源码分析 瀑布流 热修复 版本 版本控制 状态栏 状态模式 生活 留言板 相册 相对论 眉县猕猴桃 知识点 码云 磁盘 科学 笔记 策略模式 类图 系统,发行版, GNU 索引 组件 组合模式 绑定 结构 结构体 编码 网易云信 网格布局 网站广播 网站通知 网络 美化 联合 脚手架 膨胀的宇宙 自定义 自定义View 自定义插件 蒙版 虚拟 虚拟机 补码 补齐 表单 表达式 装饰模式 西安 观察者模式 规范 视图 视频 解耦器模式 设计 设计原则 设计模式 访问者模式 语法 责任链模式 贪吃蛇 转换 软件工程 软引用 运算符 迭代子模式 适配器模式 选择器 通信 通道 配置 链表 锐化 错误 键盘 闭包 降噪 陕西地方特产 面向对象 项目优化 项目构建 黑洞
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×