JNI技术概述
JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。它允许Java代码和其他语言写的代码进行交互。
其实就是java调用本地语言的技术,对于Linux和Windows而言本地语言自然是C和C++。换句话说就是java如何调用C或C++代码,而你要把C或C++代码给他人调用自然要编写成动态链接库了,在Windows是dll,在Linux是so。(这里我们讨论Android上的so库编写和调用)
这个技术当初在用友工作时就接触到了,由于当时技术的局限性,还被卡了一个星期。导致同事在外地待了一个星期。
开发环境
Eclipse+adt
android-ndk-r20(ndk的开发基础在上一章介绍:https://www.jianshu.com/p/c546783ad284)
静态注册
要对java中native关键字定义的方法进行注册, 注册方式有两种: 静态注册和动态注册, 这里我们先介绍静态注册
Android代码
我们在Android中先写一个简单的点击事件,并在static中加载so库,在按钮点击时调用so库中的函数
package com.example.jnitest;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends Activity {
static {
//加载libhello_jni.so(默认去项目目录下的libs下查找)
System.loadLibrary("hello_jni");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// 测试JNI
public void testJni(View view) {
String jniString = HelloNdk();
Toast.makeText(MainActivity.this, jniString, Toast.LENGTH_LONG).show();
}
//定义一个native方法 native关键字说明方法的实现在so里面
public native String HelloNdk();
}
界面如下:
image.png
SO库编写
静态注册 在c代码中我们的函数名要遵守一定的约定,不然jni无法识别函数,约定如下:
- (1)函数名:JNIEXPORT + 返回类型 + JNICALL Java_+包名 +类型 + 函数名(java 中声明的),以下划线连接
- (2)返回值类型,是 jni 中的数据类型,若没有返回类型,则使用void
- (3)默认传入两个参数 JNIEnv* env(jvm运行环境), jobject obj(调用这个函数的Java对象)
例如:
java中函数声明:public native String HelloNdk();
c中函数的实现:JNIEXPORT jstring JNICALL Java_com_example_jnitest_MainActivity_HelloNdk (JNIEnv * env, jobject obj)
这里你可以手动编写,但是java提供了javah来自动从java代码生成对应的头文件。
- 使用cmd进入项目的根目录,在项目的根目录创建jni目录
javah -classpath src -d jni com.example.jnitest.MainActivity
- classpath 表示类文件路径
- src表示java代码的文件夹
- d表示生成头文件所在路径
- jni 表示 jni 文件夹下
- com.example.jnitest.MainActivity,表示需要编译的类文件,也就是包名+类名(也就是包含 native 方法的类文件)
生成文件内容:com_example_jnitest_MainActivity.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_jnitest_MainActivity */
#ifndef _Included_com_example_jnitest_MainActivity
#define _Included_com_example_jnitest_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_jnitest_MainActivity
* Method: HelloNdk
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_jnitest_MainActivity_HelloNdk
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
这里我们只做c的编写所以去除c++的一些东西,在jni目录下创建hello_jni.c,并拷贝代码修改为以下:
#include <jni.h>
#include <string.h>
/* Header for class com_example_jnitest_MainActivity */
/*
* Class: com_example_jnitest_MainActivity
* Method: HelloNdk
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_jnitest_MainActivity_HelloNdk
(JNIEnv * env, jobject obj){
return (*env)->NewStringUTF(env, "Hello JNI!");
}
在jni.h中我们看到如下定义(在%java_home%\include\jni.h):
#include "jni_md.h"
typedef struct _jobject *jobject;
typedef jobject jclass;
typedef jobject jthrowable;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;
再进入jni_md.h中看看(在%java_home%\include\win32\jni_md.h):
#define JNIEXPORT __declspec(dllexport)
#define JNIIMPORT __declspec(dllimport)
#define JNICALL __stdcall
所以我们直接就不用解释JNIEXPORT 和 JNICALL 了
现在来讨论一下jstring 返回值。
java和c是如何互通的?
其实不能互通的原因主要是数据类型的问题,jni解决了这个问题,例如那个c文件中的jstring数据类型就是java传入的String对象,经过jni函数的转化就能成为c的char。
对应数据类型关系如下表:
image.png
image.png
这里我们使用(env)->NewStringUTF(env, "Hello JNI!");生成了一个jstring,并返回它。
使用ndk编译so库
- 在jni目录编写实现函数对应的Android.mk(该文件的描述在上一章中)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello_jni
LOCAL_SRC_FILES := hello_jni.c
include $(BUILD_SHARED_LIBRARY)
返回到项目根目录运行ndk
ndk-build
在项目根目录下会生成libs文件夹里面有所有平台的so库(这里你也可以指定平台)
image.png
由于我的虚拟机是X86的所有它使用的是x86文件夹下的so
运行
现在所有准备工作做完我们部署项目到手机上,点击按钮后如下:
image.png
尾言
这里我们只对JNI的类型对应做了一些基本的说明,由于此文的专注点是如何静态注册jni函数所以这里不做过多的描述,将来我将用一文详细说明jni的一些技术细节。下一章我们将对动态注册进行说明。












网友评论