Skip to content

Latest commit

 

History

History
185 lines (151 loc) · 4.25 KB

File metadata and controls

185 lines (151 loc) · 4.25 KB

INTEROP (JNI)

我们来看Java和C的互操作,这也是一个困然我很久的问题。

C这部分采用之前的calc 复习一下libcalc.a是静态库, libcalc.so是动态库 (so means share objects)

JNI 开发流程 - JNI/NDK 开发指南 - 极客学院Wiki

JNI 开发流程主要分为以下 4 步:

  • 编写Java native申明
  • 生成jni c header
  • 实现jni c
  • 在jni c中link linux so

Step 1

package cn.nonocast;

public class App {
  public static void main(String[] args) {
    System.out.println("hello world");
    System.out.println(calc(1, 2));
  }

  public static native int calc(int a, int b);

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

gradle run


> Task :run FAILED
Exception in thread "main" java.lang.UnsatisfiedLinkError: no calc in java.library.path
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
        at java.lang.Runtime.loadLibrary0(Runtime.java:870)
        at java.lang.System.loadLibrary(System.java:1122)
        at cn.nonocast.App.<clinit>(App.java:12)


FAILURE: Build failed with an exception.

Step 2

javah -jni  -classpath ./build/classes/java/main -d ./jni cn.nonocast.App

很神奇的出现jni/cn_nonocast_App.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class cn_nonocast_App */

#ifndef _Included_cn_nonocast_App
#define _Included_cn_nonocast_App
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     cn_nonocast_App
 * Method:    calc
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_cn_nonocast_App_calc
  (JNIEnv *, jclass, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

Step 3

编写对应实现cn_nonocast_App.c

#include <stdio.h>
#include "cn_nonocast_App.h"

// libcalcJNI.jnilib
JNIEXPORT jint JNICALL Java_cn_nonocast_App_calc(JNIEnv* env, jclass, jint a, jint b) {
  printf("Hello World!\n");
  return 0;
}

先来个mock,然后Makefile

CC=gcc
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk/Contents/Home

libcalc.jnilib : cn_nonocast_App.c
	$(CC) -I ${JAVA_HOME}/include -I ${JAVA_HOME}/include/linux -fPIC -shared $^ -o $@

$@: libcalcJNI.jnilib $^: cn_nonocast_App.c

MacOS下的JAVA_HOME设置, 首先通过java_home找到java安装目录,

$ /usr/libexec/java_home 
/Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk/Contents/Home

也可以直接在.zshrc中加入export JAVA_HOME=$(/usr/libexec/java_home)

 echo $JAVA_HOME
/Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk/Contents/Home

提示找不到jni_md.h, ln一下, sudo ln -s darwin/jni_md.h jni_md.h

ok, 这时就应该生成libcalcJNI.jnilib

> Task :run FAILED
Exception in thread "main" java.lang.UnsatisfiedLinkError: no calc in java.library.path
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
        at java.lang.Runtime.loadLibrary0(Runtime.java:870)
        at java.lang.System.loadLibrary(System.java:1122)
        at cn.nonocast.App.<clinit>(App.java:12)

通过程序来看一下java.library.path /Users/nonocast/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.

我们优先考虑'.',将jnilib link到build/libs ln -s ../../jni/libcalc.jnilib . 然后运行,

$ java -jar app.jar 
hello world
hello world from jni
0

Step 4

#include <stdio.h>
#include "cn_nonocast_App.h"

int add(int, int);

// libcalcJNI.jnilib
JNIEXPORT jint JNICALL Java_cn_nonocast_App_calc(JNIEnv* env, jclass c, jint a, jint b) {
  return add(a, b);
}

其实这步和nodejs非常类似,过渡一下,可以参考之前的app.cc

#include <iostream>
extern "C" {
  #include "calc.h"
}

int main() {
  std::cout << "1+2=" << add(2,3) << std::endl;
  return 0;
}

这里这需要解决一个link问题, 我们把libcalc.so放在~/lib下,

$(CC) -shared -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -L~/lib -lcalc $^ -o $@

运行

$ java -jar app.jar 
hello world
3

这样就完成了java -> jni -> so 的过程。

漏了gradle, 只需要指定run的java.library.path就ok了

run {
    systemProperty "java.library.path", "$projectDir/jni"
}