1. 程式人生 > >一步一步學習JNI

一步一步學習JNI

動態鏈接 下載 默認 .class 學習 什麽 jdk 產品 用戶

本文來自網易雲社區

作者:孫有軍


前言

本篇的主要目的就是JNI開發入門,使大家對JNI開發流程有一個大致的了解,後續再進行深入學習。

JNI不是Android特有的,JNI是Java Native Interface單詞首字母的縮寫,就是指用C或者C++開發的接口。JNI是JVM規範中的一部份,因此JNI程序在任何實現了JNI規範的Java虛擬機中都可以運行。

作為一個Android開發,這裏不大書特書學習JNI的必要性和重要性,很多博客,文章都有講述,這裏主要給出入門步驟,讓大家可以根據一步一步操作進行學習。

俗話說學習動最快的方式,就是找一個會的人,照著學,跟著做。照貓畫虎,也能描出一個大概!

準備工作

我的開發環境為Mac os x 10.10.5,Eclipse 4.4.1(也可以不需要,有這個只是方便寫代碼,記事本,Sublime都可以)。由於環境為Mac,因此下面大部分的配置都是針對Mac的。

1,Java環境變量

由於需要Java環境,因此必須要設置Java環境變量,網上有很多方式教大家怎麽設置。這裏給出mac的設置方式。進入terminal。

1,輸入cd ~進入當前用戶

2,ls -al 可以看到一個.bash_profile文件,這就是設置環境變量的地方

3,如果沒有輸入:touch .bash_profile (用來修改文件時間戳,或者新建一個不存在的文件。)

4,輸入 vim .bash_profile 輸入E(表示進入編輯模式),輸入I表示進入編輯輸入

5,輸入 export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home (這是我的目錄,你可以去Library下查看自己的JavaVirtualMachines)

6,按ESC鍵退出輸入模式,:wq 退出編輯模式

7,source .bash_profile (使配置生效)

8,echo $JAVA_HOME 如果有輸入表示設置生效

實例操作

1,編寫一個Java類

這裏類名為Hello,包名為com.sunny(可以隨便設置),代碼如下:


package com.sunny;public class Hello {   
 static{       
 //System.loadLibrary("Hello");
        System.load("/Users/doc/Jni/jniHello.jnilib");
    }    public static native int getSum(int a, int b);    
    public static void main(String[] args) {        // 虛擬機掃描加載的lib路徑
        System.out.println(System.getProperty("java.library.path"));     
           int sum = getSum(2, 5);
        System.out.println(sum);
    }

}

從上面的代碼中我們設置了一個native函數,進行兩數字的求和,並且在靜態代碼塊中加載了動態鏈接庫代碼。這裏有兩種加載方式:


System.loadLibrary("Hello")

該方式不需要加入lib前綴,也不需要jnilib(不同的操作系統會有不同的後綴),虛擬機會自動從System.getProperty("java.library.path")路徑進行掃描加載。後面會講述怎麽將自己的動態鏈接庫加入到掃描路徑。如果不設置會拋出UnsatisfiedLinkError異常。

System.load("/Users/doc/Jni/jniHello.jnilib");

該方式是設置的全路徑,他會從設置的路徑進加載。

2,生成.class文件

這裏我們對上述的Java類編譯生成 .class文件,我上述的代碼在/Users/doc/Documents/Eclipse/Jni路徑中,這裏如果你直接采用Eclipse->Run As -> Java Application是不能運行的,因為這裏還沒有動態鏈接庫,會拋出以下的異常。

Exception in thread "main" java.lang.UnsatisfiedLinkError: 
Can‘t load library: 
/Users/doc/Jni/jniHello.jnilib   
 at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1827)  
   at java.lang.Runtime.load0(Runtime.java:809)   
    at java.lang.System.load(System.java:1086)  
      at com.sunny.Hello.<clinit>(Hello.java:7)

輸入命令:

javac src/com/sunny/Hello.java -d bin

-d 表示將生成的.class文件放到bin目錄下,你可以去本地看看是否已經有該文件。

3,生成.h文件

輸入命令:

javah -jni -classpath bin -d jni com.sunny.Hello

上述生成的.h文件為comsunny_Hello.h,生成規則為包名加類名,.轉為,尾綴為.h。 -jni為可選參數 -classpath 類查找路徑,當前查找路徑為bin目錄 -d 與上述相同,表示將生成的.h放置到jni目錄。

也可以通過-o參數生成指定的Hellox.h,該參數與-d互斥。命令如下:

javah -jni -classpath bin -o Hellox.h com.sunny.Hello

生成的.h文件如下:

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_sunny_Hello */#ifndef _Included_com_sunny_Hello#define _Included_com_sunny_Hello#ifdef __cplusplusextern "C" {#endif/*
 * Class:     com_sunny_Hello
 * Method:    getSum
 * Signature: (II)I
 */JNIEXPORT jint JNICALL Java_com_sunny_Hello_getSum
  (JNIEnv *, jclass, jint, jint);#ifdef __cplusplus}#endif#endif

4,編寫.c文件實現.h

這裏可以采用VS來進行編寫,也可以用一個文本文檔來編寫。代碼如下:

#include "com_sunny_Hello.h"#ifdef __cplusplusextern "C" {#endif/*
 * Class:     com_sunny_Hello
 * Method:    getSum
 * Signature: (II)I
 */JNIEXPORT jint JNICALL Java_com_sunny_Hello_getSum
  (JNIEnv * env, jclass cls, jint a, jint b){      return a+b;
  }#ifdef __cplusplus}#endif

5,生成動態鏈接庫

這裏先補充前面的不同操作系統生成不同的名稱。

Mac OS X : libHello.jnilib(又有前綴又有後綴) Windows :Hello.dll(沒有lib前綴) Linux/Unix:libHello.so(是不是發現了android中常用的.so了)

Mac生成命令如下:

gcc -dynamiclib -o /Users/doc/Jni/libHello.jnilib Hello.c -framework JavaVM -I/$JAVA_HOME/include -I/$JAVA_HOME/include/darwin

-dynamiclib表示生成動態鏈接庫 -o表示生成動態鏈接庫放置的位置於名字 -framework JavaVM -I:編譯JNI需要用到JVM的頭文件(jni.h),第一個目錄是平臺無關的,第二個目錄是與操作系統平臺相關的頭文件,這裏就是我們前面為什麽要設置JAVA環境變量的原因。

Windows生成方式如下:

手頭沒有下載vs,因此從網上搜索了一下vs生成dll方式。開始菜單-->所有程序-->Microsoft Visual Studio 2012-->打開VS2012 X64本機工具命令提示,用cl命令編譯成dll動態庫:

cl -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -LD Hello.c -FeHello.dll

-I :和mac一樣,包含編譯JNI必要的頭文件 -LD:標識將指定的文件編譯成動態鏈接庫 -Fe:指定編譯後生成的動態鏈接庫的路徑及文件名

Linux生成命令如下:

Linux生成,記住該生成方式,因為後續Android底層是linux系統,因此最終生成使用的都是.so動態鏈接,生成的命令如下:

gcc -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -fPIC -shared Hello.c -o libHello.so

由於我本地下載的是mac版的jdk,編譯會出現的錯誤,因為找不到對應linux平臺的相關信息。

In file included from Hello.c:1:
In file included from ./com_sunny_Hello.h:2:
//Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home/include/jni.h:45:10: fatal error: ‘jni_md.h‘ file not found#include "jni_md.h"
         ^1 error generated.

這裏我們為了演示怎麽用命令行生成.so,因此把上述命令改成如下方式,後續會用AndroidStudio來生成.so動態鏈接庫:

gcc -I/$JAVA_HOME/include -I/$JAVA_HOME/include/darwin -fPIC -shared Hello.c -o libHello.so

-I 與-o與上述概念一致。 -fPIC: 編譯成與位置無關的獨立代碼 -shared:同Fe與dynamiclib,編譯成動態庫

6,運行結果

這裏可以采用Eclipse->Run As -> Java Application來運行了, 還要用命令行來運行。輸入如下命令:

java -classpath bin com.sunny.Hello

輸出結果如下:

/Users/doc/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:. 7

上述采用的是System.load("/Users/doc/Jni/jniHello.jnilib")方式,這裏填入的是全路徑,因此編譯運行時,會從輸入的路徑去查找動態鏈接庫,輸出結果為:第一行是java.library.path掃描的路徑,第二行7就是我們native返回的結果。

前面我們說了兩種加載方式。上面編譯的是全路徑,不需要將當前的動態鏈接庫放置到掃描路徑下面。如果采用System.loadLibrary("Hello")方式,則需要將動態鏈接庫放置到Java虛擬機能找到的地方。如果不進行設置則會拋出如下異常:

Exception in thread "main" java.lang.UnsatisfiedLinkError: no Hello 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 com.sunny.Hello.<clinit>(Hello.java:6)

怎麽鏈接到動態鏈接庫吶?可以采用如下三種方式進行設置:

方法1:則需要將剛才生成的動態鏈接庫放置到java.library.path掃描到的任何一個路徑下面。

方法2:項目郵件選擇properties技術分享圖片

之後選擇Java Build Path - Libraries - JRE System Library - Native library location - edit技術分享圖片

之後選擇本地生成的動態鏈接庫技術分享圖片

方法3:給jvm添加“-Djava.library.path=動態鏈接庫搜索目錄”參數,命令如下:

 java -Djava.library.path=/Users/doc/Jni/ -classpath bin
 com.sunny.Hello

設置了動態鏈接庫後,方法二,與方法三輸出結果如下:

/Users/doc/Jni/ 7

從結果看出,這裏輸出的java.library.path路徑與全路徑輸出已經不相同了,輸出從默認查找的路徑變成了設置的路徑。

總結

這裏主要做了一個初步的入門,後續再進行深入學習。如果有錯誤請指出。


網易雲免費體驗館,0成本體驗20+款雲產品!

更多網易研發、產品、運營經驗分享請訪問網易雲社區


相關文章:
【推薦】 分布式存儲系統Kudu與HBase的簡要分析與對比

一步一步學習JNI