Trong phần này chúng ta sẽ tìm hiểu cách đọc và ghi thuộc tính của một đối tượng/lớp.
Đọc thuộc tính với kiểu dữ liệu cơ bản
Đầu tiên chúng ta có đoạn code main trong Java như sau:
class AccessField { static { System.loadLibrary("AccessField"); } public String s; private native void accessField(); public static void main(String args[]) { AccessField af = new AccessField(); af.s = "Lorem ipsum"; af.accessField(); } }
Lớp AccessField
có một thuộc tính là s
kiểu String
, phương thức native accessField()
sẽ làm công việc hiển thị giá trị của thuộc tính đó lên màn hình.
Bên native chúng ta có đoạn code sau:
#include <iostream> #include "AccessField.h" JNIEXPORT void JNICALL Java_AccessField_accessField(JNIEnv *e, jobject obj) { jclass cls = e->GetObjectClass(obj); jfieldID fid = e->GetFieldID(cls, "s", "Ljava/lang/String;"); jstring jstr = (jstring)e->GetObjectField(obj, fid); const char *str = e->GetStringUTFChars(jstr, NULL); std::cout << str; e->ReleaseStringUTFChars(jstr, str); }
Để đọc một thuộc tính thì chúng ta làm theo các bước sau:
- Lấy đối tượng
jclass
bằng phương thứcGetObjectClass()
- Lấy đối tượng
jfieldID
bằng phương thứcGetFieldID(),
phương thức này nhận vào đối tượngjclass,
tên thuộc tính cần lấy, và tên lớp của thuộc tính đó, có đủ đường dẫn package - Lấy đối tượng đó bằng phương thức
GetObjectField(),
phương thức này nhận vào đối tượngjobject
vàjfieldID
, phương thức này trả về đối tượng kiểujobject
, nếu thuộc tính cần lấy là kiểu dữ liệu cơ bản thì chúng ta có thể ép kiểu trực tiếp, đối với kiểu dữ liệu đối tượng thì chúng ta cũng làm tương tự (ví dụ ở dưới).
Lorem Ipsum
Tên đường dẫn lớp trong phương thức GetFieldID()
có dạng:
"L/<đường_dẫn>/<package>/<tên_lớp;"
Nếu là các kiểu dữ liệu cơ bản thì chúng ta chỉ cần ghi ngắn gọn là:
int: "I"
float: "F"
double: "D"
boolean: "Z"
Không cần ghi tên package và tên lớp.
Đọc thuộc tính với kiểu dữ liệu đối tượng
Ví dụ chúng ta có lớp Point
trong Java như sau:
class Point { private int x; private int y; public Point(int x, int y) { this.x = x; this.y = y; } public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public int getX() { return x; } public int getY() { return y; } }
Tiếp theo chúng ta có đoạn code main như sau:
class AccessField { static { System.loadLibrary("AccessField"); } private Point p; private native void accessFieldObject(); public static void main(String args[]) { AccessField af = new AccessField(); af.p = new Point(3, 5); af.accessFieldObject(); } }
Lớp AccessField
có một thuộc tính kiểu Point
là p
, phương thức accessFieldObject()
sẽ lấy thuộc tính p
này rồi hiển thị 2 thuộc tính con của p
là x
và y,
sau đó hiển thị lên màn hình.
Chúng ta làm việc đó như sau:
#include <iostream> #include "AccessField.h" using namespace std; JNIEXPORT void JNICALL Java_AccessField_accessFieldObject(JNIEnv *e, jobject obj) { jclass cls = e->GetObjectClass(obj); jfieldID fid = e->GetFieldID(cls, "p", "LPoint;"); jobject pointObj = e->GetObjectField(obj, fid); jclass pointCls = e->GetObjectClass(pointObj); jfieldID xFid = e->GetFieldID(pointCls, "x", "I"); jfieldID yFid = e->GetFieldID(pointCls, "y", "I"); jint x = e->GetIntField(pointObj, xFid); jint y = e->GetIntField(pointObj, yFid); cout << "Point: " << x << ", " << y; }
Chúng ta cũng lần lượt lấy jclass
của đối tượng obj,
tức là lấy được lớp AccessField
. Sau đó lấy đối tượng jfieldID
của thuộc tính p,
rồi lấy đối tượng jobject
của thuộc tính p
này, từ đó chúng ta bắt đầu lại lấy jclass
của thuộc tính p
này, cứ thế cho đến khi lấy 2 thuộc tính x
và y
.
Ở đây khi lấy thuộc tính x
và y
thì chúng ta không dùng phương thức GetObjectField()
rồi ép kiểu nữa, mà chúng ta dùng phương thức GetIntField()
đế lấy thẳng đối tượng jint
luôn. Trong JNI thì int
và jint
là giống nhau nên chúng ta có thể dùng cout
để in ra luôn.
Point: 3, 5
Tương tự, JNI có một số phương thức lấy dữ liệu khác là:
GetFloatField(): jfloat
GetBooleanField(): jboolean
GetByteField(): jbyte
GetCharField(): jchar
GetShortField(): jshort
GetLongField(): jlong
GetDoubleField(): jdouble
Đọc thuộc tính static
Đọc thuộc tính static cũng giống như thuộc tính thường, chỉ khác là một số phương thức sẽ có tên ‘Static’ trong đó.
Ví dụ:
Chúng ta có code main Java như sau:
class AccessField { static { System.loadLibrary("AccessField"); } private static float PI = 3.14159265359f; private native void accessStaticField(); public static void main(String args[]) { new AccessField().accessStaticField(); } }
Thuộc tính PI
có kiểu là float
, đây là thuộc tính static
.
Code native như sau:
#include <iostream> #include "AccessField.h" using namespace std; JNIEXPORT void JNICALL Java_AccessField_accessStaticField (JNIEnv *e, jobject obj) { jclass cls = e->GetObjectClass(obj); jfieldID fid = e->GetStaticFieldID(cls, "PI", "F"); jfloat PI = e->GetStaticFloatField(cls, fid); printf("PI: %.9f", PI); }
Phương thức lấy jfieldID
là GetStaticFieldID()
, lấy dữ liệu là GetStaticFloatField()
.
PI: 3.141592741
Tương tự, các phương thức lấy dữ liệu khác là
GetStaticObjectField()
GetStaticBooleanFielđ()
GetStaticByteField()
GetStaticCharField()
GetStaticShortField()
GetStaticIntField()
GetStaticLongField()
GetStaticDoubleField(
)
Ghi dữ liệu vào thuộc tính
Ví dụ chúng ta có đoạn code main như sau:
class AccessField { static { System.loadLibrary("AccessField"); } private static float PI = 3.14159265359f; private static float radius; private native void setRadius(); public static void main(String args[]) { AccessField af = new AccessField(); af.setRadius(); System.out.println("Circle area: " + (radius * radius * PI)); } }
Lớp AccessField()
có thuộc tính radius
kiểu float,
phương thức native setRadius()
sẽ thiết lập giá trị cho thuộc tính này.
Code native như sau:
#include <iostream> #include "AccessField.h" using namespace std; JNIEXPORT void JNICALL Java_AccessField_setRadius (JNIEnv *e, jobject obj) { jclass cls = e->GetObjectClass(obj); jfieldID fid = e->GetStaticFieldID(cls, "radius", "F"); jfloat radius = e->GetStaticFloatField(cls, fid); radius = 5.0f; e->SetStaticFloatField(cls, fid, radius); }
Mọi quá trình vẫn giống như cũ, chúng ta lấy jclass, jfieldID,
lấy biến, sau đó gán giá trị cho biến.
Cuối cùng để thiết lập giá trị này cho thuộc tính ở phía Java thì chúng ta gọi phương thức SetStaticFloatField(),
phương thức này nhận vào đối tượng jclass
, nếu thuộc tính không phải là static thì chúng ta dùng phương thức SetFloatField()
và truyền vào một đối tượng jobject
, sau đó là jfieldID
và tới giá trị.
Circle area: 78.53982
Các phương thức tương tự là:
STATIC | NON STATIC | KIỂU DỮ LIỆU |
SetStaticObjectField() |
SetObjectField() |
jobject |
SetStaticBooleanField() |
S |
jboolean |
SetStaticByteField() |
SetByteField() |
jbyte |
|
SetCharField() |
jchar |
|
SetShortField() |
jshort |
|
SetIntField() |
jint |
|
SetLongField() |
jlong |
|
SetFloatField() |
jfloat |
|
SetDoubleField() |
jdouble |