簡介
JNA(Java Native Access )提供了一組Java工具類用於在運行期間動態訪問系統本地庫(native library:如Window的dll)而不需要編寫任何Native/JNI代碼。開發人員只要在一個java接口中描述目標native library的函數與結構,JNA將自動實現Java接口到native function的映射。
優點:JNA可以讓你像調用一般java方法一樣直接調用本地方法。就和直接執行本地方法差不多,而且調用本地方法還不用額外的其他處理或者配置什麼的,也不需要多餘的引用或者編碼,使用很方便。
GitHub 地址
JNA 相關資源下載配置
要想使用JNA,我們需要對應的jna.jar和libdispatch.so。下面提供了兩種方式:
一、直接下載 jar 和 so
Eclipse 下載jar和so。下載解壓aar後,得到jar和so,添加到工程即可。具體配置如下圖:
aar解壓後中文件
jni文件夾中各個平臺的 so
Eclipse中的配置
二、用 gradle 產生依賴
Android Studio 的 gradle 添加依賴
compile 'net.java.dev.jna:jna:4.4.0@aar'
使用Proguard混淆代碼
-dontwarn java.awt.*
-keep class com.sun.jna.* { *; }
-keepclassmembers class * extends com.sun.jna.* { public *; }
JNA實戰
工程結構
用test.h test.cpp生成libtest.so,然後通過jna的api與test.h中的接口一一對應即可。
jni/test.h
#ifndef TEST_H
#define TEST_H
#ifdef __cplusplus
extern "C" {
#endif
void getStr(char *name);
int add(int a, int b);
#ifdef __cplusplus
};
#endif
#endif
jni/test.cpp
#include <jni.h>
#include <stdio.h>
#include <string.h>
#include "test.h"
void getStr(char *name) {
char str[10] = "abcdefg\n";
memcpy(name, str, strlen(str));
}
int add(int a, int b) {
return a+b;
}
上面是so部分即C++部分,下面java部分。
src/com/pachong/jnatest/MainActivity.java
public class MainActivity extends Activity implements OnClickListener {
private EditText mETNumber1, mETNumber2;
private TextView mTVResult, mTVGetStr;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mETNumber1 = (EditText) findViewById(R.id.id_et_num_1);
mETNumber2 = (EditText) findViewById(R.id.id_et_num_2);
mTVResult = (TextView) findViewById(R.id.id_tv_result);
mTVGetStr = (TextView) findViewById(R.id.id_tv_get);
findViewById(R.id.id_bt_equal).setOnClickListener(this);
findViewById(R.id.id_bt_get).setOnClickListener(this);
}
public interface TestLibrary extends Library {
public static final TestLibrary INSTANCE = Native.loadLibrary("test", TestLibrary.class);
public void getStr(ByteBuffer name);
public int add(int a, int b);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.id_bt_equal:
String numberStr1 = mETNumber1.getText().toString();
String numberStr2 = mETNumber2.getText().toString();
if (TextUtils.isEmpty(numberStr1)) {
numberStr1 = "0";
}
if (TextUtils.isEmpty(numberStr2)) {
numberStr2 = "0";
}
int number1 = Integer.parseInt(numberStr1);
int number2 = Integer.parseInt(numberStr2);
int result = TestLibrary.INSTANCE.add(number1, number2);
mTVResult.setText(Integer.toString(result));
break;
case R.id.id_bt_get:
byte[] buffer = new byte[20];
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
TestLibrary.INSTANCE.getStr(byteBuffer);
String str = new String(byteBuffer.array());
mTVGetStr.setText(str);
break;
default:
break;
}
}
}
res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<EditText
android:id="@+id/id_et_num_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:inputType="numberSigned" >
</EditText>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="+"
android:textAppearance="?android:attr/textAppearanceLarge" />
<EditText
android:id="@+id/id_et_num_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:inputType="number" />
<Button
android:id="@+id/id_bt_equal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="="
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/id_tv_result"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
<TextView
android:id="@+id/id_tv_get"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textAppearance="?android:attr/textAppearanceLarge" />
<Button
android:id="@+id/id_bt_get"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="getStr" />
</LinearLayout>
java部分最重要的就是下面這段代碼了。
public interface TestLibrary extends Library {
public static final TestLibrary INSTANCE = Native.loadLibrary("test", TestLibrary.class);
public void getStr(ByteBuffer name);
public int add(int a, int b);
}
通過繼承jna的Libraray類,然後調用Native.loadLibrary加載so庫,然後重寫對應C++接口函數即可。
上面是運行後的結果,成功從C++層獲取到了相關接口,完全不用再自己去寫jni的代碼了。
JNAerator 資源下載
上面我們已經知道了JNA的基本用法,但是對於jna的api和C++代碼之間的一些類型定義,我們還不是很熟練。怎麼辦?有沒有辦法一鍵生成相應的C++和java的對應關係,這個時候JNAerator就派上用場了。
第一步:下載jar
jnaerator.jar 下載
第二步:使用cmd命令打開JNAerator
java -jar jnaerator-0.12-shaded.jar
第三步:用JNAerator將C++代碼生成對應的java代碼
將C++代碼複製粘貼到左邊編輯框中,點擊菜單欄的 JNAerate 即可生成對應的java代碼。是不是很簡單!從此,再也不用爲寫JNI代碼而頭疼了。