JNA có vẻ dễ sử dụng hơn một chút để gọi mã gốc so với JNI. Bạn sẽ sử dụng JNI thay vì JNA trong những trường hợp nào?
JNA có vẻ dễ sử dụng hơn một chút để gọi mã gốc so với JNI. Bạn sẽ sử dụng JNI thay vì JNA trong những trường hợp nào?
Câu trả lời:
Đây là những vấn đề tôi gặp phải. Có thể có nhiều hơn nữa. Nhưng nhìn chung hiệu suất không khác biệt giữa jna và jni nên bạn dùng JNA ở đâu thì dùng.
BIÊN TẬP
Câu trả lời này có vẻ khá phổ biến. Vì vậy, đây là một số bổ sung:
Vì vậy, tôi vẫn tin rằng bất cứ khi nào có thể, tốt hơn là sử dụng JNA hoặc BridJ và hoàn nguyên về jni nếu hiệu suất là quan trọng, bởi vì nếu bạn cần gọi các hàm gốc thường xuyên, thì hiệu suất sẽ đáng chú ý.
JNIEXPORT
hàm toàn cục . Tôi hiện đang khám phá JavaCpp như một tùy chọn, sử dụng JNI, nhưng tôi không nghĩ rằng JNI vani hỗ trợ điều này. Có cách nào để gọi các hàm thành viên C ++ bằng cách sử dụng vanilla JNI mà tôi đang bỏ qua không?
Thật khó để trả lời một câu hỏi chung chung như vậy. Tôi cho rằng sự khác biệt rõ ràng nhất là với JNI, việc chuyển đổi kiểu được thực hiện ở phía gốc của biên giới Java / native, trong khi với JNA, việc chuyển đổi kiểu được thực hiện trong Java. Nếu bạn đã cảm thấy khá thoải mái với việc lập trình bằng C và phải tự triển khai một số mã gốc, tôi sẽ cho rằng JNI có vẻ sẽ không quá phức tạp. Nếu bạn là một lập trình viên Java và chỉ cần gọi một thư viện gốc của bên thứ ba, sử dụng JNA có lẽ là cách dễ nhất để tránh những vấn đề có lẽ không quá rõ ràng với JNI.
Mặc dù tôi chưa bao giờ đánh giá bất kỳ sự khác biệt nào, nhưng tôi sẽ vì thiết kế, ít nhất giả sử rằng chuyển đổi kiểu với JNA trong một số tình huống sẽ hoạt động kém hơn với JNI. Ví dụ: khi truyền các mảng, JNA sẽ chuyển đổi các mảng này từ Java thành bản địa ở đầu mỗi lệnh gọi hàm và quay lại khi kết thúc lệnh gọi hàm. Với JNI, bạn có thể tự kiểm soát khi tạo "chế độ xem" gốc của mảng, có khả năng chỉ tạo chế độ xem của một phần của mảng, giữ chế độ xem trên một số lệnh gọi hàm và cuối cùng, hãy giải phóng chế độ xem và quyết định xem bạn có muốn để giữ các thay đổi (có thể yêu cầu sao chép lại dữ liệu) hoặc hủy các thay đổi (không cần sao chép). Tôi biết bạn có thể sử dụng một mảng gốc trên các lệnh gọi hàm với JNA bằng cách sử dụng lớp Bộ nhớ, nhưng điều này cũng sẽ yêu cầu sao chép bộ nhớ, có thể không cần thiết với JNI. Sự khác biệt có thể không liên quan, nhưng nếu mục tiêu ban đầu của bạn là tăng hiệu suất ứng dụng bằng cách triển khai các phần của nó trong mã gốc, thì việc sử dụng công nghệ cầu nối hoạt động kém hơn dường như không phải là lựa chọn rõ ràng nhất.
Đó chỉ là những gì tôi có thể nghĩ ra trên đỉnh đầu của mình, mặc dù tôi cũng không phải là người sử dụng nhiều. Cũng có vẻ như bạn có thể tránh JNA nếu bạn muốn có một giao diện tốt hơn giao diện mà họ cung cấp nhưng bạn có thể viết mã xung quanh đó trong java.
Nhân tiện, trong một dự án của chúng tôi, chúng tôi đã lưu giữ một bản in chân JNI rất nhỏ. Chúng tôi đã sử dụng bộ đệm giao thức để đại diện cho các đối tượng miền của chúng tôi và do đó chỉ có một hàm gốc để kết nối Java và C (tất nhiên sau đó hàm C đó sẽ gọi một loạt các hàm khác).
Đó không phải là câu trả lời trực tiếp và tôi không có kinh nghiệm với JNA nhưng khi tôi xem các Dự án sử dụng JNA và thấy những cái tên như SVNKit, IntelliJ IDEA, NetBeans IDE, v.v., tôi có xu hướng tin rằng đó là một thư viện khá tốt.
Trên thực tế, tôi chắc chắn nghĩ rằng tôi sẽ sử dụng JNA thay vì JNI khi tôi phải làm vì nó thực sự trông đơn giản hơn JNI (có một quá trình phát triển nhàm chán). Thật tệ, JNA đã không được phát hành vào thời điểm này.
Nếu bạn muốn hiệu suất JNI nhưng lại nản lòng vì độ phức tạp của nó, bạn có thể cân nhắc sử dụng các công cụ tạo liên kết JNI tự động. Ví dụ: JANET (từ chối trách nhiệm: Tôi đã viết nó) cho phép bạn trộn mã Java và C ++ trong một tệp nguồn duy nhất, và ví dụ: thực hiện các cuộc gọi từ C ++ sang Java bằng cú pháp Java tiêu chuẩn. Ví dụ: đây là cách bạn in một chuỗi C ra đầu ra chuẩn Java:
native "C++" void printHello() {
const char* helloWorld = "Hello, World!";
`System.out.println(#$(helloWorld));`
}
Sau đó, JANET dịch Java nhúng backtick thành các lệnh gọi JNI thích hợp.
Tôi thực sự đã thực hiện một số điểm chuẩn đơn giản với JNI và JNA.
Như những người khác đã chỉ ra, JNA là để thuận tiện. Bạn không cần phải biên dịch hoặc viết mã gốc khi sử dụng JNA. Trình tải thư viện gốc của JNA cũng là một trong những trình tải tốt nhất / dễ sử dụng nhất mà tôi từng thấy. Đáng buồn thay, có vẻ như bạn không thể sử dụng nó cho JNI. (Đó là lý do tại sao tôi đã viết một giải pháp thay thế cho System.loadLibrary () sử dụng quy ước đường dẫn của JNA và hỗ trợ tải liền mạch từ classpath (tức là các chum).)
Tuy nhiên, hiệu suất của JNA có thể kém hơn nhiều so với JNI. Tôi đã thực hiện một thử nghiệm rất đơn giản gọi là hàm tăng số nguyên bản địa đơn giản "return arg + 1;". Các điểm chuẩn được thực hiện với jmh cho thấy rằng các lệnh gọi JNI tới hàm đó nhanh hơn 15 lần so với JNA.
Một ví dụ "phức tạp" hơn trong đó hàm gốc tổng hợp một mảng số nguyên gồm 4 giá trị vẫn cho thấy rằng hiệu suất JNI nhanh hơn 3 lần so với JNA. Lợi thế bị giảm đi có lẽ là do cách bạn truy cập các mảng trong JNI: ví dụ của tôi đã tạo một số thứ và phát hành lại nó trong mỗi thao tác tính tổng.
Bạn có thể tìm thấy mã và kết quả kiểm tra tại github .
Tôi đã điều tra JNI và JNA để so sánh hiệu suất bởi vì chúng tôi cần quyết định một trong số họ gọi một dll trong dự án và chúng tôi gặp phải hạn chế về thời gian thực. Kết quả cho thấy JNI có hiệu suất lớn hơn JNA (khoảng 40 lần). Có thể có một mẹo để có hiệu suất tốt hơn trong JNA nhưng nó rất chậm đối với một ví dụ đơn giản.
Trừ khi tôi thiếu thứ gì đó, không phải sự khác biệt chính giữa JNA và JNI là với JNA bạn không thể gọi mã Java từ mã gốc (C)?
Trong ứng dụng cụ thể của tôi, JNI tỏ ra dễ sử dụng hơn nhiều. Tôi cần đọc và ghi các luồng liên tục đến và từ một cổng nối tiếp - và không có gì khác. Thay vì cố gắng tìm hiểu cơ sở hạ tầng liên quan trong JNA, tôi thấy việc tạo mẫu giao diện gốc trong Windows dễ dàng hơn nhiều với một DLL có mục đích đặc biệt chỉ xuất ra sáu chức năng: