Trước tiên chúng ta cần có các công cụ sau đây:
- JDK 2 trở lên, phiên bản mới nhất tại thời điểm viết bài là JDK 8.
- Trình biên dịch MinGW.
Bạn lên mạng tải và cài đặt 2 trình biên dịch trên, JDK có thể tải trực tiếp từ website của Oracle. MinGW có thể tải về từ mingw.org, lưu ý là các IDE phổ biến như DevC++ hoặc CodeBlocks cũng thường có sẵn MinGW khi tải về rồi, nếu bạn đã từng tải và cài 2 IDE trên mà có sử dụng bản đính kèm MinGW thì bạn không cần phải tải MinGW về nữa.
Lưu ý là mặc dù bạn có cài bằng IDE thì ở đây chúng ta sẽ biên dịch trên dòng lệnh hết, tức là sẽ không có bấm nút màu xanh lá cây hay bấm Ctrl + F9, F6… gì cả.
Sau khi đã tải và cài xong thì bạn thêm đường dẫn đến thư mục bin
trong thư mục cài đặt của 2 trình biên dịch này vào biến môi trường PATH
để tiện sử dụng. Ví dụ trên Windows 10 thì bạn vào My Computer → Systems properties → Advanced system settings → Environment Variables… Sau đó trong phần System variables bạn tìm biến PATH, bấm Edit… và thêm đường dẫn đến 2 thư mục trên vào.
Xong xuôi rồi thì bạn có thể kiểm tra bằng cách mở Command Prompt (cmd) lên và gõ lệnh java -version
để xem phiên bản Java và g++ --version
để xem phiên bản C++.
Kết quả ra tương tự như hình dưới đây là được:
Liên kết Java và C++ bằng JNI
Để có thể gọi code C++ từ code Java thì chúng ta làm như sau:
- Bước 1: Viết phương thức
native
trong Java
- Bước 2: Dùng lệnh
javac
để dịch code đó ra file .class
- Bước 3: Dùng lệnh
javah -jni
để tạo file .h
(Header trong C++)
- Bước 4: Viết hàm/lớp/phương thức C++ mà sẽ được gọi từ phương thức
native
trong Java đã khai báo ở trên
- Bước 5: Dịch hàm/lớp/phương thức C++ trên thành file thư viện liên kết động
.dll
(Windows) hoặc .so
(Linux)
- Bước 6: Load file thư viện đó trong main ở phía Java và sử dụng
Ví dụ
Chúng ta sẽ viết hàm in dòng chữ “Hello World” bằng C++, sau đó biên dịch hàm đó ra file .dll
rồi load file đó vào trong Java, từ Java chúng ta sẽ gọi hàm in dòng chữ “Hello World” đó.
Đầu tiên chúng ta tạo một thư mục chứa project, ở đây mình tạo thư mục HelloWorld
trong ổ đĩa C.
Bước 1: Viết code Java
Chúng ta tạo một file có tên HelloWorld.java
trong thư mục HelloWorld
có nội dung như sau:
public class HelloWorld {
static {
System.loadLibrary("HelloWorld");
}
private native void print();
public static void main(String[] args) {
new HelloWorld().print();
}
}
Đoạn code trên rất đơn giản, đầu tiên chúng ta gọi phương thức System.loadLibrary("HelloWorld")
để chương trình nạp file HelloWorld.dll
vào, file HelloWorld.dll
sẽ được tạo sau.
Sau đó ở dưới chúng ta định nghĩa phương thức có tên print(),
phương thức này là phương thức native,
chúng ta dùng từ khóa native
để khai báo một phương thức là native.
Phương thức native
là phương thức được gọi từ file thư viện .dll
.
Cuối cùng là hàm main(),
trong đó chúng ta tạo đối tượng HelloWorld
rồi gọi phương thức print()
.
Bước 2: biên dịch code Java
Chúng ta dùng lệnh javac HelloWorld.java
để dịch ra file HelloWorld.class
Bước 3: Tạo file header .h
Chúng ta dùng lệnh javah -jni -classpath . HelloWorld
để tạo ra file HelloWorld.h
.
Trong lệnh trên thì tham số -classpath
có giá trị là đường dẫn đến file .class
đã được tạo ra ở bước 2, ở đây chúng ta truyền vô giá trị là dấu chấm, tức là đường dẫn đến thư mục hiện tại trong Command Prompt.
File HelloWorld.h
sẽ được tạo ra tự động có nội dung như sau:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: print
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_print
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Chúng ta sẽ không đụng chạm gì tới các file .h này, có một lưu ý là ở đầu file này có include file thư viện jni.h
, file này nằm trong thư mục include
trong thư mục cài đặt JDK, bạn có thể tìm và mở ra xem, trong đó lại có dòng include đến file jni_md.h
nữa, và file này nằm trong thư mục include/win32
trong thư mục cài đặt JDK, chúng ta sẽ cần lưu ý đến 2 thư mục này.
Một lưu ý nữa là trong đoạn code trên có phần khai báo prototype cho hàm Java_HelloWorld_print(JNIEnv *, jobject),
không có phần thân hàm {}
. Nhiệm vụ của chúng ta là phải code hàm này ở đâu đó.
Bước 4: viết hàm native có trong file header
Do đó bây giờ chúng ta tạo một file có tên HelloWorld.cpp
trong thư mục đó với nội dung như sau:
#include <iostream>
#include "HelloWorld.h"
using namespace std;
JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *e, jobject obj) {
cout << "Hello World printed from C++" << endl;
}
Ở đây chúng ta viết giống như viết code C++ bình thường, chỉ khác là chúng ta dùng những từ khóa của JNI như JNIEXPORT, JNICALl, JNIEnv, jobject
…v.v chúng ta sẽ tìm hiểu chúng sau, một điểm khác nữa là không có hàm main(),
đơn giản là vì chúng ta đang viết thư viện chứ không viết một chương trình.
Bước 5: dịch hàm native C++ ra file thư viện
Nếu bạn đã từng dịch C++ bằng dòng lệnh thì bạn sẽ biết là chúng ta cần dịch ra file object trước, sau đó mới link các file object thành file exe, ở đây cũng vậy, chỉ khác là chúng ta link thành file .dll
(hoặc .so
).
Đầu tiên chúng ta chạy lệnh g++ để tạo file object, ở đây chúng ta chạy lệnh này với cú pháp như sau:
g++ -I"<đường_dẫn_thư_mục_header" -c <tên_file_cpp> -o <tên_file_object>
Trong đó -I
là tham số nhận vào đường dẫn đến các thư mục chứa file .h được dùng thêm, tại vì ở đây chúng ta có dùng đến file jni.h
và jni_md.h
trong thư mục include của JDK nên chúng ta phải truyền vào tham số -I
. Tham số -c
nhận vào tên file .cpp
mà chúng ta đã viết ra ở bước 4, tham số -o
nhận vào tên file .o
sẽ được tạo ra sau khi chạy lệnh này.
Giả sử thư mục cài đặt JDK của mình nằm ở C:\Programs Files\Java\jdk1.8.0_65
thì mình sẽ gõ lệnh trên như hình sau:
Tiếp theo chúng ta tạo file thư viện .dll
theo cú pháp như sau:
g++ -Wl,--add-stdcall-alias -shared -o <tên_file_dll> <tên_file_object>
Bạn cần đưa vào tham số -Wl,--add-stdcall-alias
nếu bạn dùng hệ điều hành Windows. Tham số -shared
cho biết chúng ta sẽ tạo file thư viện, tham số -o
có giá trị là tên file .dll
sẽ được tạo ra, cuối cùng là tên file .o
đã được tạo ra ở trên.
Lệnh mình gõ sẽ tương tự như sau:
Bước 6: Run
Vậy là xong, chúng ta có thể chạy code Java được rồi, chúng ta dùng lệnh sau:
java -classpath . -Djava.library.path=<đường_dẫn_đến_file_dll> <tên_lớp_main>
Chúng ta dùng lệnh Java để chạy một file đã được dịch thành file .class,
tuy nhiên ở đây chúng ta có dùng thêm thư viện nữa, mặc dù file thư viện .dll
nằm chung thư mục với file .class
(nếu bạn làm giống như những gì đã hướng dẫn ở trên) nhưng Java vẫn sẽ không tự nhận diện file đó, do đó chúng ta phải thêm đường dẫn đến thư mục chứa file .dll
đó trong tham số –Djava.library.path
.
Kết quả giống như hình dưới: